from __future__ import annotations

from typing import TYPE_CHECKING, Any

from pydantic import BaseModel, Field

from niannian.external_data.capability import CapabilityDeniedError
from niannian.external_data.service import ExternalDataService

if TYPE_CHECKING:
    from niannian import NianNian


class ExternalDataBody(BaseModel):
    operation: str = "add"
    protocol: str = ""
    capability_id: str = ""
    project: str
    function: str
    payload: Any = None
    record_id: str = ""
    dedup_key: str = ""
    occurred_at: str = ""
    caller: dict[str, Any] = Field(default_factory=dict)
    provenance: dict[str, Any] | None = None
    learn: dict[str, Any] | None = None
    storage_target: str = "cloud"
    local_dir: str = ""
    tags: list[str] = Field(default_factory=list)

    def to_apply_dict(self) -> dict[str, Any]:
        data = self.model_dump(exclude_none=True)
        if not data.get("protocol"):
            data.pop("protocol", None)
        return data


class ArchiveExternalBody(BaseModel):
    project: str
    function: str
    storage_target: str = "cloud"
    local_dir: str = ""


def create_external_data_router(nn: NianNian):
    from fastapi import APIRouter, HTTPException

    router = APIRouter(prefix="/v1/external-data", tags=["external-data"])

    def _svc() -> ExternalDataService:
        return ExternalDataService(nn.home, config=nn._config, memory_store=nn._store)

    @router.post("")
    def post_external_data(body: ExternalDataBody) -> dict[str, Any]:
        try:
            result = _svc().apply(body.to_apply_dict())
            return result.to_dict()
        except CapabilityDeniedError as exc:
            raise HTTPException(status_code=403, detail=str(exc)) from exc
        except ValueError as exc:
            raise HTTPException(status_code=422, detail=str(exc)) from exc

    @router.delete("/{record_id}")
    def delete_external_data(
        record_id: str,
        project: str,
        function: str,
        capability_id: str,
        storage_target: str = "cloud",
        local_dir: str = "",
        caller_json: str = "{}",
    ) -> dict[str, Any]:
        import json

        try:
            caller = json.loads(caller_json) if caller_json else {}
        except json.JSONDecodeError:
            caller = {}
        try:
            result = _svc().apply(
                {
                    "operation": "delete",
                    "capability_id": capability_id,
                    "project": project,
                    "function": function,
                    "record_id": record_id,
                    "storage_target": storage_target,
                    "local_dir": local_dir,
                    "caller": caller,
                    "payload": {},
                }
            )
            return result.to_dict()
        except CapabilityDeniedError as exc:
            raise HTTPException(status_code=403, detail=str(exc)) from exc
        except ValueError as exc:
            raise HTTPException(status_code=422, detail=str(exc)) from exc

    @router.put("/{record_id}")
    def overwrite_external_data(record_id: str, body: ExternalDataBody) -> dict[str, Any]:
        data = body.to_apply_dict()
        data["operation"] = "overwrite"
        data["record_id"] = record_id
        try:
            result = _svc().apply(data)
            return result.to_dict()
        except CapabilityDeniedError as exc:
            raise HTTPException(status_code=403, detail=str(exc)) from exc
        except ValueError as exc:
            raise HTTPException(status_code=422, detail=str(exc)) from exc

    @router.post("/archive")
    def archive_external_to_l2(body: ArchiveExternalBody) -> dict[str, Any]:
        """Chat 触发：将外部数据从 L1 迁至 NAS L2，目录 {memory_space}/{function}/"""
        return _svc().archive_to_l2(
            body.project,
            body.function,
            storage_target=body.storage_target,
            local_dir=body.local_dir,
        )

    @router.get("")
    def list_external_data(project: str, function: str) -> dict[str, Any]:
        rows = _svc().list_records(project, function)
        return {"project": project, "function": function, "count": len(rows), "records": rows}

    return router
