from __future__ import annotations

from fastapi import APIRouter, HTTPException

from matyan_backend.deps import FdbDb, KafkaProducerDep  # noqa: TC001
from matyan_backend.kafka.producer import emit_control_event
from matyan_backend.storage import entities
from matyan_backend.storage.runs import get_run_meta

from ._pydantic_models import (
    TagCreateIn,
    TagGetOut,
    TagGetRunsOut,
    TagListOut,
    TagUpdateIn,
    TagUpdateOut,
)

tags_router = APIRouter(prefix="/tags")


def _tag_to_out(tag: dict, run_count: int | None = None) -> dict:
    return {
        "id": tag["id"],
        "name": tag.get("name", ""),
        "color": tag.get("color"),
        "description": tag.get("description"),
        "run_count": run_count if run_count is not None else 0,
        "archived": tag.get("is_archived", False),
    }


@tags_router.get("/", response_model=TagListOut)
async def get_tags_list_api(db: FdbDb) -> list[dict]:
    tags = entities.list_tags(db)
    result = []
    for t in tags:
        run_hashes = entities.get_runs_for_tag(db, t["id"])
        result.append(_tag_to_out(t, run_count=len(run_hashes)))
    return result


@tags_router.get("/search/", response_model=TagListOut)
async def search_tags_by_name_api(db: FdbDb, q: str = "") -> list[dict]:
    tags = entities.list_tags(db)
    query = q.strip().lower()
    if query:
        tags = [t for t in tags if query in t.get("name", "").lower()]
    result = []
    for t in tags:
        run_hashes = entities.get_runs_for_tag(db, t["id"])
        result.append(_tag_to_out(t, run_count=len(run_hashes)))
    return result


@tags_router.post("/", response_model=TagUpdateOut)
async def create_tag_api(tag_in: TagCreateIn, db: FdbDb) -> dict[str, str]:
    try:
        tag = entities.create_tag(
            db,
            tag_in.name.strip(),
            color=tag_in.color.strip() if tag_in.color else None,
            description=tag_in.description.strip() if tag_in.description else None,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e
    return {"id": tag["id"], "status": "OK"}


@tags_router.get("/{tag_id}/", response_model=TagGetOut)
async def get_tag_api(tag_id: str, db: FdbDb) -> dict:
    tag = entities.get_tag(db, tag_id)
    if not tag:
        raise HTTPException(status_code=404)
    run_hashes = entities.get_runs_for_tag(db, tag_id)
    return _tag_to_out(tag, run_count=len(run_hashes))


@tags_router.put("/{tag_id}/", response_model=TagUpdateOut)
async def update_tag_properties_api(tag_id: str, tag_in: TagUpdateIn, db: FdbDb) -> dict[str, str]:
    tag = entities.get_tag(db, tag_id)
    if not tag:
        raise HTTPException(status_code=404)

    updates: dict = {}
    if tag_in.name:
        updates["name"] = tag_in.name.strip()
    if tag_in.color is not None:
        updates["color"] = tag_in.color.strip()
    if tag_in.description is not None:
        updates["description"] = tag_in.description.strip()
    if tag_in.archived is not None:
        updates["is_archived"] = tag_in.archived

    if updates:
        entities.update_tag(db, tag_id, **updates)

    return {"id": tag_id, "status": "OK"}


@tags_router.delete("/{tag_id}/")
async def delete_tag_api(tag_id: str, db: FdbDb, producer: KafkaProducerDep) -> None:
    tag = entities.get_tag(db, tag_id)
    if not tag:
        raise HTTPException(status_code=404)
    entities.delete_tag(db, tag_id)
    await emit_control_event(producer, "tag_deleted", tag_id=tag_id)


@tags_router.get("/{tag_id}/runs/", response_model=TagGetRunsOut)
async def get_tagged_runs_api(tag_id: str, db: FdbDb) -> dict:
    tag = entities.get_tag(db, tag_id)
    if not tag:
        raise HTTPException(status_code=404)

    run_hashes = entities.get_runs_for_tag(db, tag_id)
    tag_runs = []
    for rh in run_hashes:
        meta = get_run_meta(db, rh)
        exp_name: str | None = None
        exp_id = meta.get("experiment_id")
        if exp_id:
            exp = entities.get_experiment(db, exp_id)
            exp_name = exp.get("name") if exp else None
        tag_runs.append(
            {
                "run_id": rh,
                "name": meta.get("name", ""),
                "experiment": exp_name,
                "creation_time": meta.get("created_at", 0),
                "end_time": meta.get("finalized_at"),
            },
        )

    return {"id": tag_id, "runs": tag_runs}
