"""控制台所需的最小纵向 API。"""
from __future__ import annotations

from typing import Dict, List

from fastapi import APIRouter, HTTPException, status
from sqlalchemy import func, select

from ..db import session_scope
from ..enums import EventKind, EventSource, RunState, Severity
from ..events import append_event
from ..models import EvidenceBundle, Run, RunEvent, ResourceLock, utcnow
from ..schemas import CreateRunBody, HumanActionBody, PreflightBody
from ..services.runs import (
    cancel_simulation,
    create_run,
    event_to_dict,
    prepare_agent_run,
    preflight_for_body,
    run_to_dict,
    start_simulation,
)

router = APIRouter(prefix="/api/v1")


@router.get("/health")
async def health() -> Dict[str, str]:
    return {"status": "ok", "service": "flashops-control-plane"}


@router.post("/preflight")
async def preflight(body: PreflightBody) -> Dict[str, object]:
    try:
        async with session_scope() as session:
            return await preflight_for_body(session, body)
    except LookupError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc


@router.post("/runs", status_code=status.HTTP_201_CREATED)
async def create_run_route(body: CreateRunBody) -> Dict[str, object]:
    try:
        async with session_scope() as session:
            run = await create_run(session, body)
            if body.execution_mode == "agent_dry_run":
                await prepare_agent_run(session, run)
            payload = run_to_dict(run)
        if body.execution_mode == "simulated":
            start_simulation(run.id)
        return payload
    except LookupError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/runs/demo", status_code=status.HTTP_201_CREATED)
async def create_demo_run() -> Dict[str, object]:
    return await create_run_route(CreateRunBody())


@router.get("/runs")
async def list_runs(limit: int = 30) -> List[Dict[str, object]]:
    async with session_scope() as session:
        runs = (
            await session.scalars(
                select(Run).order_by(Run.created_at.desc()).limit(min(max(limit, 1), 100))
            )
        ).all()
        return [run_to_dict(run) for run in runs]


@router.get("/runs/{run_id}")
async def get_run(run_id: str) -> Dict[str, object]:
    async with session_scope() as session:
        run = await session.get(Run, run_id)
        if run is None:
            raise HTTPException(status_code=404, detail="Run 不存在")
        payload = run_to_dict(run)
        bundle = await session.scalar(
            select(EvidenceBundle)
            .where(EvidenceBundle.run_id == run_id)
            .order_by(EvidenceBundle.created_at.desc())
        )
        payload["evidence"] = (
            {
                "id": bundle.id,
                "uri": bundle.uri,
                "completeness": bundle.completeness,
                "size_bytes": bundle.size_bytes,
            }
            if bundle
            else None
        )
        return payload


@router.get("/runs/{run_id}/events")
async def get_events(run_id: str, after_seq: int = 0) -> List[Dict[str, object]]:
    async with session_scope() as session:
        exists = await session.get(Run, run_id)
        if exists is None:
            raise HTTPException(status_code=404, detail="Run 不存在")
        events = (
            await session.scalars(
                select(RunEvent)
                .where(RunEvent.run_id == run_id, RunEvent.seq > after_seq)
                .order_by(RunEvent.seq)
            )
        ).all()
        return [event_to_dict(event) for event in events]


@router.post("/runs/{run_id}/pause")
async def pause_run(run_id: str, body: HumanActionBody) -> Dict[str, object]:
    async with session_scope() as session:
        run = await session.get(Run, run_id)
        if run is None:
            raise HTTPException(status_code=404, detail="Run 不存在")
        if run.state != RunState.RUNNING.value:
            raise HTTPException(status_code=409, detail="只有 RUNNING 任务可以暂停")
        await append_event(
            session,
            run,
            EventKind.HUMAN_TOUCH.value,
            "{0}: {1}".format(body.actor, body.reason),
            source=EventSource.HUMAN.value,
            projection={
                "human_touches": run.human_touches + 1,
                "unattended_completion": False,
            },
        )
        await append_event(
            session,
            run,
            EventKind.RUN_PAUSED.value,
            "任务已在安全检查点暂停",
            source=EventSource.HUMAN.value,
            target_state=RunState.PAUSED.value,
        )
        return run_to_dict(run)


@router.post("/runs/{run_id}/resume")
async def resume_run(run_id: str, body: HumanActionBody) -> Dict[str, object]:
    async with session_scope() as session:
        run = await session.get(Run, run_id)
        if run is None:
            raise HTTPException(status_code=404, detail="Run 不存在")
        if run.state != RunState.PAUSED.value:
            raise HTTPException(status_code=409, detail="只有 PAUSED 任务可以继续")
        await append_event(
            session,
            run,
            EventKind.HUMAN_TOUCH.value,
            "{0}: {1}".format(body.actor, body.reason),
            source=EventSource.HUMAN.value,
            projection={
                "human_touches": run.human_touches + 1,
                "unattended_completion": False,
            },
        )
        await append_event(
            session,
            run,
            EventKind.RUN_RESUMED.value,
            "人工确认后从检查点继续",
            source=EventSource.HUMAN.value,
            target_state=RunState.RUNNING.value,
        )
        return run_to_dict(run)


@router.post("/runs/{run_id}/emergency-stop")
async def emergency_stop(run_id: str, body: HumanActionBody) -> Dict[str, object]:
    async with session_scope() as session:
        run = await session.get(Run, run_id)
        if run is None:
            raise HTTPException(status_code=404, detail="Run 不存在")
        if run.state in {
            RunState.COMPLETED.value,
            RunState.ABORTED.value,
            RunState.FROZEN.value,
            RunState.REJECTED.value,
        }:
            raise HTTPException(status_code=409, detail="终态任务不能再次停止")
        await append_event(
            session,
            run,
            EventKind.HUMAN_TOUCH.value,
            "{0}: {1}".format(body.actor, body.reason),
            source=EventSource.HUMAN.value,
            severity=Severity.CRITICAL.value,
            projection={
                "human_touches": run.human_touches + 1,
                "unattended_completion": False,
            },
        )
        await append_event(
            session,
            run,
            EventKind.RUN_ABORTED.value,
            "紧急停止已执行，现场与检查点已保留",
            source=EventSource.HUMAN.value,
            severity=Severity.CRITICAL.value,
            target_state=RunState.ABORTED.value,
            projection={"ended_at": utcnow()},
        )
        await session.execute(
            ResourceLock.__table__.delete().where(ResourceLock.run_id == run.id)
        )
        payload = run_to_dict(run)
    cancel_simulation(run_id)
    return payload


@router.get("/dashboard/summary")
async def dashboard_summary() -> Dict[str, object]:
    async with session_scope() as session:
        total = await session.scalar(select(func.count()).select_from(Run)) or 0
        running = (
            await session.scalar(
                select(func.count()).select_from(Run).where(
                    Run.state.in_(
                        [
                            RunState.QUEUED.value,
                            RunState.PREFLIGHT.value,
                            RunState.RUNNING.value,
                            RunState.RECOVERING.value,
                        ]
                    )
                )
            )
            or 0
        )
        completed = (
            await session.scalar(
                select(func.count()).select_from(Run).where(
                    Run.state == RunState.COMPLETED.value
                )
            )
            or 0
        )
        unattended = (
            await session.scalar(
                select(func.count()).select_from(Run).where(
                    Run.state == RunState.COMPLETED.value,
                    Run.unattended_completion.is_(True),
                )
            )
            or 0
        )
        ucr = round(unattended / completed * 100, 1) if completed else 0.0
        return {
            "total_runs": total,
            "active_runs": running,
            "completed_runs": completed,
            "unattended_completion_rate": ucr,
        }
