from __future__ import annotations

from pathlib import Path
from typing import Any

from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import FileResponse
from pydantic import BaseModel
from sync_state_machine.common.persistence import PersistenceBackend

from .config_models import PipelineUiConfig, list_ui_presets, preset_ui_config, file_ui_config
from sync_state_machine.config import get_preset_file_candidates
from .runner import PipelineRunnerService


class LogsResponse(BaseModel):
    chunks: list[str]
    next_index: int


class SqlQueryRequest(BaseModel):
    sql: str
    limit: int = 200


class SqlQueryResponse(BaseModel):
    columns: list[str]
    rows: list[dict[str, Any]]
    error: str = ""


class LoadFileRequest(BaseModel):
    path: str


def create_app() -> FastAPI:
    app = FastAPI(title="Sync State Machine UI", version="0.1.0")
    service = PipelineRunnerService()
    web_root = Path(__file__).resolve().parent / "web"

    @app.get("/")
    async def index() -> FileResponse:
        return FileResponse(str(web_root / "index.html"))

    @app.get("/api/config/default", response_model=PipelineUiConfig)
    async def get_default_config() -> PipelineUiConfig:
        return service.get_default_config()

    @app.get("/api/config/presets")
    async def get_presets() -> dict:
        data = []
        for name in list_ui_presets():
            data.append(
                {
                    "name": name,
                    "candidates": [str(p) for p in get_preset_file_candidates(name)],
                }
            )
        return {"presets": data}

    @app.get("/api/config/preset/{preset_name}", response_model=PipelineUiConfig)
    async def get_preset_config(preset_name: str) -> PipelineUiConfig:
        try:
            return preset_ui_config(preset_name)
        except Exception as exc:
            raise HTTPException(status_code=404, detail=str(exc)) from exc

    @app.post("/api/config/load-file", response_model=PipelineUiConfig)
    async def load_config_file(payload: LoadFileRequest) -> PipelineUiConfig:
        try:
            return file_ui_config(payload.path)
        except Exception as exc:
            raise HTTPException(status_code=400, detail=str(exc)) from exc

    @app.get("/api/config/current", response_model=PipelineUiConfig)
    async def get_current_config() -> PipelineUiConfig:
        return service.get_config()

    @app.get("/api/config/visualized")
    async def get_visualized_config() -> dict:
        cfg = service.get_config()
        return cfg.model_dump()

    @app.post("/api/config/current", response_model=PipelineUiConfig)
    async def set_current_config(config: PipelineUiConfig) -> PipelineUiConfig:
        try:
            return service.set_config(config)
        except Exception as exc:
            raise HTTPException(status_code=400, detail=str(exc)) from exc

    @app.get("/api/status")
    async def get_status() -> dict:
        status = service.get_status()
        return {
            "running": status.running,
            "started_at": status.started_at,
            "ended_at": status.ended_at,
            "last_error": status.last_error,
            "last_stats": status.last_stats or {},
            "current_log_file": status.current_log_file,
        }

    @app.post("/api/run")
    async def run_pipeline(config: PipelineUiConfig) -> dict:
        try:
            status = await service.start(config)
            return {
                "running": status.running,
                "started_at": status.started_at,
                "current_log_file": status.current_log_file,
            }
        except Exception as exc:
            raise HTTPException(status_code=409, detail=str(exc)) from exc

    @app.post("/api/stop")
    async def stop_pipeline() -> dict:
        status = await service.stop()
        return {
            "running": status.running,
            "ended_at": status.ended_at,
            "last_error": status.last_error,
        }

    @app.get("/api/logs", response_model=LogsResponse)
    async def get_logs(since: int = Query(default=0, ge=0)) -> LogsResponse:
        chunks, next_idx = service.get_logs(since)
        return LogsResponse(chunks=chunks, next_index=next_idx)

    @app.get("/api/records/summary")
    async def get_records_summary() -> dict:
        cfg = service.get_config()
        return PersistenceBackend.records_summary(
            backend=cfg.persist.backend,
            db_path=str(Path(cfg.persist.db_path)),
        )

    @app.post("/api/records/query", response_model=SqlQueryResponse)
    async def run_records_query(payload: SqlQueryRequest) -> SqlQueryResponse:
        sql = (payload.sql or "").strip()
        if not sql:
            return SqlQueryResponse(columns=[], rows=[], error="SQL is empty")

        cfg = service.get_config()
        try:
            columns, rows = PersistenceBackend.query_records(
                backend=cfg.persist.backend,
                db_path=str(Path(cfg.persist.db_path)),
                sql=sql,
                limit=payload.limit,
            )
            return SqlQueryResponse(columns=columns, rows=rows, error="")
        except Exception as exc:
            return SqlQueryResponse(columns=[], rows=[], error=f"{type(exc).__name__}: {exc}")

    return app
