"""Metrics API endpoints — CRUD for metric definitions stored in DuckDB."""

from typing import List, Optional

import duckdb
import yaml
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from pydantic import BaseModel

from app.auth.access import require_admin
from app.auth.dependencies import get_current_user, _get_db
from src.repositories.metrics import MetricRepository

router = APIRouter(tags=["metrics"])


class MetricCreate(BaseModel):
    id: str
    name: str
    display_name: str
    category: str
    sql: str
    description: Optional[str] = None
    type: str = "sum"
    unit: Optional[str] = None
    grain: str = "monthly"
    table_name: Optional[str] = None
    tables: Optional[List[str]] = None
    expression: Optional[str] = None
    time_column: Optional[str] = None
    dimensions: Optional[List[str]] = None
    filters: Optional[List[str]] = None
    synonyms: Optional[List[str]] = None
    notes: Optional[List[str]] = None
    sql_variants: Optional[dict] = None
    validation: Optional[dict] = None
    source: str = "manual"


@router.get("/api/metrics")
async def list_metrics(
    category: Optional[str] = None,
    user: dict = Depends(get_current_user),
    conn: duckdb.DuckDBPyConnection = Depends(_get_db),
):
    """List all metric definitions, optionally filtered by category."""
    repo = MetricRepository(conn)
    metrics = repo.list(category=category)
    return {"metrics": metrics, "count": len(metrics)}


@router.get("/api/metrics/{metric_id:path}")
async def get_metric(
    metric_id: str,
    user: dict = Depends(get_current_user),
    conn: duckdb.DuckDBPyConnection = Depends(_get_db),
):
    """Get a single metric definition by ID."""
    repo = MetricRepository(conn)
    metric = repo.get(metric_id)
    if metric is None:
        raise HTTPException(status_code=404, detail=f"Metric '{metric_id}' not found")
    return metric


@router.post("/api/admin/metrics", status_code=201)
async def create_or_update_metric(
    body: MetricCreate,
    user: dict = Depends(require_admin),
    conn: duckdb.DuckDBPyConnection = Depends(_get_db),
):
    """Create or update a metric definition (admin only)."""
    repo = MetricRepository(conn)
    metric = repo.create(
        id=body.id,
        name=body.name,
        display_name=body.display_name,
        category=body.category,
        sql=body.sql,
        description=body.description,
        type=body.type,
        unit=body.unit,
        grain=body.grain,
        table_name=body.table_name,
        tables=body.tables,
        expression=body.expression,
        time_column=body.time_column,
        dimensions=body.dimensions,
        filters=body.filters,
        synonyms=body.synonyms,
        notes=body.notes,
        sql_variants=body.sql_variants,
        validation=body.validation,
        source=body.source,
    )
    return metric


@router.delete("/api/admin/metrics/{metric_id:path}")
async def delete_metric(
    metric_id: str,
    user: dict = Depends(require_admin),
    conn: duckdb.DuckDBPyConnection = Depends(_get_db),
):
    """Delete a metric definition by ID (admin only)."""
    repo = MetricRepository(conn)
    deleted = repo.delete(metric_id)
    if not deleted:
        raise HTTPException(status_code=404, detail=f"Metric '{metric_id}' not found")
    return {"status": "deleted", "id": metric_id}


@router.post("/api/admin/metrics/import", status_code=200)
async def import_metrics(
    file: UploadFile = File(...),
    user: dict = Depends(require_admin),
    conn: duckdb.DuckDBPyConnection = Depends(_get_db),
):
    """Import metrics from uploaded YAML file."""
    content = await file.read()
    try:
        data = yaml.safe_load(content)
    except yaml.YAMLError as e:
        raise HTTPException(status_code=400, detail=f"Invalid YAML: {e}")

    if not data:
        raise HTTPException(status_code=400, detail="Empty YAML file")

    metric_list = data if isinstance(data, list) else [data]
    repo = MetricRepository(conn)
    count = 0

    for metric in metric_list:
        if not isinstance(metric, dict):
            continue
        name = metric.get("name")
        category = metric.get("category")
        if not name or not category:
            raise HTTPException(
                status_code=400,
                detail="Each metric must have 'name' and 'category' fields",
            )

        metric_id = f"{category}/{name}"
        table_name = metric.pop("table", None) or metric.get("table_name")

        # Collect sql_by_* variants
        sql_variants = {}
        for key in list(metric.keys()):
            if key.startswith("sql_by_"):
                sql_variants[key[4:]] = metric.pop(key)

        repo.create(
            id=metric_id,
            name=name,
            display_name=metric.get("display_name", name),
            category=category,
            description=metric.get("description"),
            type=metric.get("type", "sum"),
            unit=metric.get("unit"),
            grain=metric.get("grain", "monthly"),
            table_name=table_name,
            tables=metric.get("tables"),
            expression=metric.get("expression"),
            time_column=metric.get("time_column"),
            dimensions=metric.get("dimensions"),
            filters=metric.get("filters"),
            synonyms=metric.get("synonyms"),
            notes=metric.get("notes"),
            sql=metric.get("sql", ""),
            sql_variants=sql_variants if sql_variants else None,
            validation=metric.get("validation"),
            source="yaml_import",
        )
        count += 1

    return {"status": "imported", "count": count}
