from __future__ import annotations

import inspect
from collections.abc import Awaitable, Callable
from datetime import datetime
from typing import Any

from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel, ConfigDict, Field

from .manager import UIBuilderManager

DataProvider = Callable[[], dict[str, Any] | Awaitable[dict[str, Any]]]


class ConfigPayload(BaseModel):
    model_config = ConfigDict(extra="allow")

    version: int = 20
    project_name: str
    data_source: str
    canvas: dict[str, Any] = Field(default_factory=dict)
    tabs: list[dict[str, Any]] = Field(default_factory=list)
    components: list[dict[str, Any]] = Field(default_factory=list)
    triggers: list[dict[str, Any]] = Field(default_factory=list)
    shortcut_sequences: list[dict[str, Any]] = Field(default_factory=list)
    prematch_groups: list[dict[str, Any]] = Field(default_factory=list)
    prematch_buttons: list[dict[str, Any]] = Field(default_factory=list)
    quick_panel_selectors: list[dict[str, Any]] = Field(default_factory=list)


class BackupPayload(BaseModel):
    name: str


def create_ui_builder_router(
    manager: UIBuilderManager,
    data_providers: dict[str, DataProvider],
    *,
    source_labels: dict[str, str] | None = None,
    prefix: str = "/api/ui-builder",
    access_dependency: Callable[..., Any] | None = None,
    read_only: bool = False,
    publish_manager: UIBuilderManager | None = None,
) -> APIRouter:
    dependencies = [Depends(access_dependency)] if access_dependency else []
    router = APIRouter(
        prefix=prefix,
        tags=["UI Builder"],
        dependencies=dependencies,
    )
    source_labels = source_labels or {}

    @router.get("/config")
    async def get_config() -> dict[str, Any]:
        return manager.load()

    if not read_only:
        @router.post("/config")
        async def save_config(payload: ConfigPayload) -> dict[str, Any]:
            saved = manager.save(payload.model_dump())
            return {"ok": True, "config": saved}

        @router.post("/import")
        async def import_config(payload: ConfigPayload) -> dict[str, Any]:
            saved = manager.import_config(payload.model_dump())
            return {"ok": True, "config": saved}

        @router.get("/export")
        async def export_config() -> JSONResponse:
            return JSONResponse(
                manager.load(),
                headers={
                    "Content-Disposition":
                    'attachment; filename="ui_builder_draft.json"'
                },
            )

        @router.get("/backups")
        async def list_backups() -> dict[str, Any]:
            return {"items": manager.list_backups()}

        @router.post("/backups/restore")
        async def restore_backup(payload: BackupPayload) -> dict[str, Any]:
            try:
                restored = manager.restore_backup(payload.name)
            except (FileNotFoundError, ValueError, OSError):
                raise HTTPException(status_code=404, detail="Backup not found")
            return {"ok": True, "config": restored}

        if publish_manager is not None:
            @router.post("/publish")
            async def publish_config() -> dict[str, Any]:
                published = publish_manager.save(
                    manager.load(),
                    create_backup=True,
                )
                return {
                    "ok": True,
                    "config": published,
                    "published_at": datetime.now().isoformat(timespec="seconds"),
                }

    @router.get("/sources")
    async def list_sources() -> dict[str, Any]:
        return {
            "items": [
                {"id": key, "label": source_labels.get(key, key)}
                for key in data_providers
            ]
        }

    @router.get("/data/{source}")
    async def get_data(source: str) -> dict[str, Any]:
        provider = data_providers.get(source)
        if provider is None:
            raise HTTPException(status_code=404, detail="Unknown data source")
        try:
            result = provider()
            if inspect.isawaitable(result):
                result = await result
            if not isinstance(result, dict):
                raise TypeError("Data provider must return a dict")
            return result
        except HTTPException:
            raise
        except Exception as exc:
            raise HTTPException(status_code=500, detail=str(exc)) from exc

    return router
