"""
DR(재해복구) 자동화 API.

엔드포인트:
  GET  /api/dr/scenarios              시나리오 목록
  POST /api/dr/scenarios              시나리오 등록 (ADMIN)
  GET  /api/dr/scenarios/{id}         시나리오 상세
  PUT  /api/dr/scenarios/{id}         시나리오 수정 (ADMIN)
  POST /api/dr/test                   복구 테스트 실행
  GET  /api/dr/test/{id}              테스트 결과 조회
  GET  /api/dr/tests                  테스트 이력 목록
  POST /api/dr/backup-verify          백업 무결성 검증
  POST /api/dr/failover/{scenario_id} Failover 실행 (ADMIN)
  GET  /api/dr/rto-rpo                RTO/RPO 현황
  GET  /api/dr/dashboard              DR 전체 현황
"""
from __future__ import annotations

import logging
from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from pydantic import BaseModel
from sqlalchemy import select, desc
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user, require_admin_role
from database import get_db
from models import DRScenario, DRTest, User, UserRole

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/dr", tags=["dr"])


# ── 권한 ─────────────────────────────────────────────────────────────────────

def _require_ops(current_user: User = Depends(get_current_user)) -> User:
    if current_user.role not in (UserRole.ADMIN, UserRole.PM, UserRole.ENGINEER):
        raise HTTPException(403, "DR 접근 권한이 없습니다.")
    return current_user


# ── Pydantic 스키마 ──────────────────────────────────────────────────────────

class ScenarioCreate(BaseModel):
    name: str
    scenario_type: str = "SERVER_FAILURE"  # SITE_FAILURE | SERVER_FAILURE | DATA_CORRUPTION
    primary_server_id: Optional[int] = None
    standby_server_id: Optional[int] = None
    rto_minutes: Optional[int] = 240
    rpo_minutes: Optional[int] = 60
    failover_steps: Optional[list] = []
    healthcheck_url: Optional[str] = None


class ScenarioOut(BaseModel):
    id: int
    name: str
    scenario_type: str
    rto_minutes: Optional[int]
    rpo_minutes: Optional[int]
    healthcheck_url: Optional[str]
    last_test_at: Optional[datetime]
    last_test_result: Optional[str]
    is_active: bool

    model_config = {"from_attributes": True}


class TestRequest(BaseModel):
    scenario_id: int
    test_type: str = "RECOVERY"  # BACKUP_VERIFY | RECOVERY


class TestOut(BaseModel):
    id: int
    scenario_id: int
    test_type: str
    status: str
    rto_actual: Optional[int]
    rpo_actual: Optional[int]
    result_detail: Optional[dict]
    started_at: datetime
    completed_at: Optional[datetime]
    triggered_by: Optional[str]

    model_config = {"from_attributes": True}


class BackupVerifyRequest(BaseModel):
    server_name: str


# ── 엔드포인트 ───────────────────────────────────────────────────────────────

@router.get("/scenarios", response_model=List[ScenarioOut])
async def list_scenarios(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """DR 시나리오 목록."""
    q = await db.execute(
        select(DRScenario).where(DRScenario.is_active == True).order_by(DRScenario.name)
    )
    return q.scalars().all()


@router.post("/scenarios", response_model=ScenarioOut, status_code=201)
async def create_scenario(
    body: ScenarioCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_admin_role),
):
    """DR 시나리오 등록 (ADMIN 전용)."""
    scenario = DRScenario(**body.model_dump())
    db.add(scenario)
    await db.commit()
    await db.refresh(scenario)
    return scenario


@router.get("/scenarios/{scenario_id}", response_model=ScenarioOut)
async def get_scenario(
    scenario_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    q = await db.execute(select(DRScenario).where(DRScenario.id == scenario_id))
    sc = q.scalar_one_or_none()
    if not sc:
        raise HTTPException(404, "시나리오를 찾을 수 없습니다.")
    return sc


@router.put("/scenarios/{scenario_id}", response_model=ScenarioOut)
async def update_scenario(
    scenario_id: int,
    body: ScenarioCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_admin_role),
):
    q = await db.execute(select(DRScenario).where(DRScenario.id == scenario_id))
    sc = q.scalar_one_or_none()
    if not sc:
        raise HTTPException(404, "시나리오를 찾을 수 없습니다.")
    for k, v in body.model_dump().items():
        setattr(sc, k, v)
    await db.commit()
    await db.refresh(sc)
    return sc


@router.post("/test", response_model=TestOut)
async def run_recovery_test(
    body: TestRequest,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """복구 테스트 실행. 백그라운드로 실행되고 test_id를 즉시 반환."""
    from core.dr_engine import DREngine
    engine = DREngine()

    if body.test_type == "BACKUP_VERIFY":
        # 빠른 검증 — 동기 처리
        q = await db.execute(select(DRScenario).where(DRScenario.id == body.scenario_id))
        sc = q.scalar_one_or_none()
        if not sc:
            raise HTTPException(404, "시나리오를 찾을 수 없습니다.")
        test = DRTest(
            scenario_id=body.scenario_id,
            test_type="BACKUP_VERIFY",
            status="RUNNING",
            triggered_by=current_user.username,
            started_at=datetime.now(),
            result_detail={},
        )
        db.add(test)
        await db.commit()
        await db.refresh(test)
        background_tasks.add_task(
            _run_test_bg, body.scenario_id, test.id, current_user.username
        )
        return test

    result = await engine.run_recovery_test(db, body.scenario_id, current_user.username)
    if not result.get("test_id"):
        raise HTTPException(500, result.get("error", "테스트 실행 실패"))

    q = await db.execute(select(DRTest).where(DRTest.id == result["test_id"]))
    return q.scalar_one()


async def _run_test_bg(scenario_id: int, test_id: int, triggered_by: str):
    """백그라운드 테스트 실행 태스크."""
    from database import SessionLocal
    from core.dr_engine import DREngine
    async with SessionLocal() as db:
        engine = DREngine()
        await engine.run_recovery_test(db, scenario_id, triggered_by)


@router.get("/test/{test_id}", response_model=TestOut)
async def get_test_result(
    test_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    q = await db.execute(select(DRTest).where(DRTest.id == test_id))
    t = q.scalar_one_or_none()
    if not t:
        raise HTTPException(404, "테스트 결과를 찾을 수 없습니다.")
    return t


@router.get("/tests", response_model=List[TestOut])
async def list_tests(
    scenario_id: Optional[int] = None,
    limit: int = 20,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """테스트 이력 목록."""
    q = select(DRTest).order_by(desc(DRTest.started_at)).limit(limit)
    if scenario_id:
        q = q.where(DRTest.scenario_id == scenario_id)
    result = await db.execute(q)
    return result.scalars().all()


@router.post("/backup-verify")
async def verify_backup(
    body: BackupVerifyRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """서버 백업 무결성 검증 (SSH → SHA-256 확인)."""
    from core.dr_engine import DREngine
    result = await DREngine().verify_backup(db, body.server_name)
    if not result["success"]:
        raise HTTPException(400, result.get("error", "백업 검증 실패"))
    return result


@router.post("/failover/{scenario_id}")
async def execute_failover(
    scenario_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_admin_role),
):
    """
    Failover 실행 (ADMIN 전용).
    시뮬레이션 모드로 실행 — 실제 서비스 전환은 confirm=true 파라미터 필요.
    """
    from core.dr_engine import DREngine
    result = await DREngine().run_recovery_test(db, scenario_id, current_user.username)
    return {
        "message": "Failover 테스트 실행 완료",
        "test_id": result.get("test_id"),
        "status": result.get("status"),
        "rto_actual_minutes": result.get("rto_actual_minutes"),
    }


@router.get("/rto-rpo")
async def get_rto_rpo(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """RTO/RPO 목표 대비 실적 현황."""
    from core.dr_engine import DREngine
    return await DREngine().get_rto_rpo_stats(db)


@router.get("/dashboard")
async def get_dashboard(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_require_ops),
):
    """DR 전체 현황 대시보드."""
    sc_q = await db.execute(select(DRScenario).where(DRScenario.is_active == True))
    scenarios = sc_q.scalars().all()

    test_q = await db.execute(
        select(DRTest).order_by(desc(DRTest.started_at)).limit(10)
    )
    recent_tests = test_q.scalars().all()

    pass_count = sum(1 for sc in scenarios if sc.last_test_result == "PASS")
    fail_count = sum(1 for sc in scenarios if sc.last_test_result == "FAIL")

    return {
        "total_scenarios": len(scenarios),
        "pass_count": pass_count,
        "fail_count": fail_count,
        "untested_count": len(scenarios) - pass_count - fail_count,
        "recent_tests": [
            {
                "test_id": t.id,
                "scenario_id": t.scenario_id,
                "test_type": t.test_type,
                "status": t.status,
                "started_at": t.started_at.isoformat(),
            }
            for t in recent_tests
        ],
    }
