"""Gremlin v2 workbook explainer API."""

from __future__ import annotations

import hashlib
import json
import logging
from typing import Any, List, Optional
from uuid import uuid4

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from pydantic import BaseModel, Field

from ... import settings as saas_settings
from ...auth_security import require_account
from ...db import AsyncSessionLocal
from ..progress import get_redis
from ...services import model_explainer_service as explainer_service
from .gremlin import get_gremlin_tier

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v2/gremlin", tags=["gremlin"])

EXPLAIN_CACHE_TTL_SECONDS = 300
EXPLAIN_JOB_TTL_SECONDS = 900


def _job_key(job_id: str) -> str:
    return f"gremlin:explainer:job:{job_id}"


def _cache_key(payload_hash: str) -> str:
    return f"gremlin:explainer:cache:{payload_hash}"


def _hash_payload(sheets_metadata: List[dict], cells: List[dict]) -> str:
    payload = {
        "sheets": sheets_metadata,
        "cells": [
            {
                "sheet": c.get("sheet"),
                "a1": c.get("a1"),
                "formula": c.get("formula"),
            }
            for c in cells
        ],
    }
    raw = json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16]


class WorkbookCell(BaseModel):
    a1: str
    formula: Optional[str] = None
    display_value: Optional[Any] = None
    sheet: str


class SheetMetadata(BaseModel):
    name: str
    row_count: Optional[int] = None
    col_count: Optional[int] = None
    headers: List[Any] = Field(default_factory=list)


class ExplainWorkbookRequest(BaseModel):
    spreadsheet_id: str
    sheet_names: Optional[List[str]] = None
    max_sheets: int = 10
    write_tabs: bool = True
    sheets_metadata: List[SheetMetadata]
    cells: List[WorkbookCell]


class ExplainWorkbookStartResponse(BaseModel):
    job_id: str
    status: str


class ExplainWorkbookStatusResponse(BaseModel):
    status: str
    model_map: Optional[List[dict]] = None
    inputs_map: Optional[List[dict]] = None
    plain_english_summary: Optional[str] = None
    error: Optional[str] = None


def _ensure_enabled() -> None:
    if not saas_settings.GREMLIN_V2_ENABLED:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Gremlin v2 disabled")
    if not saas_settings.GREMLIN_V2_EXPLAINER:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model explainer disabled")


async def _run_explainer_job(
    job_id: str,
    spreadsheet_id: str,
    sheet_names: List[str],
    sheets_metadata: List[dict],
    cells: List[dict],
    cache_key: Optional[str],
    tier: str,
    account_id: str,
) -> None:
    redis = await get_redis()
    job_key = _job_key(job_id)

    try:
        model_map, inputs_map, context = explainer_service.build_workbook_maps(
            spreadsheet_id,
            cells,
            sheets_metadata,
            sheet_names,
        )
        async with AsyncSessionLocal() as db:
            summary = await explainer_service.summarize_workbook(context, tier, account_id, db)
        payload = {
            "status": "complete",
            "model_map": model_map,
            "inputs_map": inputs_map,
            "plain_english_summary": summary,
        }
        raw_payload = json.dumps(payload, ensure_ascii=True)
        if redis:
            await redis.setex(job_key, EXPLAIN_JOB_TTL_SECONDS, raw_payload)
            if cache_key:
                await redis.setex(cache_key, EXPLAIN_CACHE_TTL_SECONDS, raw_payload)
    except Exception as exc:
        logger.exception("Model explainer job failed: %s", exc)
        if redis:
            await redis.setex(
                job_key,
                EXPLAIN_JOB_TTL_SECONDS,
                json.dumps({"status": "failed", "error": str(exc)}, ensure_ascii=True),
            )


@router.post("/explain-workbook", response_model=ExplainWorkbookStartResponse)
async def explain_workbook(
    request: ExplainWorkbookRequest,
    background_tasks: BackgroundTasks,
    account_id: str = Depends(require_account),
):
    _ensure_enabled()

    if request.max_sheets and len(request.sheets_metadata) > request.max_sheets:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Too many sheets requested (max {request.max_sheets}).",
        )

    if len(request.cells) > 5000:
        raise HTTPException(
            status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
            detail="Too many formula cells. Select specific sheets to scan.",
        )

    redis = await get_redis()
    if not redis:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Explainer unavailable")

    sheet_names = request.sheet_names or [meta.name for meta in request.sheets_metadata if meta.name]
    sheets_metadata = [meta.model_dump() for meta in request.sheets_metadata]
    cells = [cell.model_dump() for cell in request.cells]

    cache_key = None
    payload_hash = _hash_payload(sheets_metadata, cells)
    cache_key = _cache_key(payload_hash)
    cached = await redis.get(cache_key)
    if cached:
        job_id = str(uuid4())
        await redis.setex(_job_key(job_id), EXPLAIN_JOB_TTL_SECONDS, cached)
        return ExplainWorkbookStartResponse(job_id=job_id, status="complete")

    job_id = str(uuid4())
    await redis.setex(
        _job_key(job_id),
        EXPLAIN_JOB_TTL_SECONDS,
        json.dumps({"status": "processing"}, ensure_ascii=True),
    )

    async with AsyncSessionLocal() as db:
        tier = await get_gremlin_tier(account_id, db)
    background_tasks.add_task(
        _run_explainer_job,
        job_id,
        request.spreadsheet_id,
        sheet_names,
        sheets_metadata,
        cells,
        cache_key,
        tier,
        account_id,
    )

    return ExplainWorkbookStartResponse(job_id=job_id, status="processing")


@router.get("/explain-workbook/{job_id}", response_model=ExplainWorkbookStatusResponse)
async def explain_workbook_status(
    job_id: str,
    account_id: str = Depends(require_account),
):
    del account_id
    _ensure_enabled()

    redis = await get_redis()
    if not redis:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Explainer unavailable")

    raw = await redis.get(_job_key(job_id))
    if not raw:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Explainer job not found")

    payload = json.loads(raw)
    return ExplainWorkbookStatusResponse(
        status=payload.get("status", "processing"),
        model_map=payload.get("model_map"),
        inputs_map=payload.get("inputs_map"),
        plain_english_summary=payload.get("plain_english_summary"),
        error=payload.get("error"),
    )
