"""
Vision API (Terminal-only).

- Trader + Tycoon: can generate Vision JSON (cached in DB)
- Scout: receives a 403 with an upgrade teaser message
"""

from __future__ import annotations

import json
from datetime import datetime, timedelta
from typing import Any, Optional

from fastapi import APIRouter, HTTPException, Query, status
from pydantic import BaseModel, Field
from sqlalchemy import and_, select

from app.api.deps import CurrentUser, Database
from app.models.llm_artifact import LLMArtifact
from app.models.subscription import Subscription, SubscriptionTier
from app.services.llm_gateway import LLMGatewayError
from app.services.llm_vision import (
    VISION_PROMPT_VERSION,
    YIELD_LANDING_PROMPT_VERSION,
    VisionResult,
    YieldLandingConfig,
    generate_vision,
    generate_yield_landing,
)


router = APIRouter(prefix="/llm", tags=["LLM Vision"])


class VisionResponse(BaseModel):
    domain: str
    cached: bool
    model: str
    prompt_version: str
    generated_at: str
    result: VisionResult


class YieldLandingPreviewResponse(BaseModel):
    domain: str
    cached: bool
    model: str
    prompt_version: str
    generated_at: str
    result: YieldLandingConfig


async def _get_or_create_subscription(db: Database, user_id: int) -> Subscription:
    res = await db.execute(select(Subscription).where(Subscription.user_id == user_id))
    sub = res.scalar_one_or_none()
    if sub:
        return sub
    sub = Subscription(user_id=user_id, tier=SubscriptionTier.SCOUT, max_domains=5, check_frequency="daily")
    db.add(sub)
    await db.commit()
    await db.refresh(sub)
    return sub


def _require_trader_or_higher(sub: Subscription) -> None:
    if sub.tier not in (SubscriptionTier.TRADER, SubscriptionTier.TYCOON):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Vision is available on Trader and Tycoon plans. Upgrade to unlock.",
        )


@router.get("/vision", response_model=VisionResponse)
async def get_vision(
    current_user: CurrentUser,
    db: Database,
    domain: str = Query(..., min_length=3, max_length=255),
    refresh: bool = Query(False, description="Bypass cache and regenerate"),
):
    sub = await _get_or_create_subscription(db, current_user.id)
    _require_trader_or_higher(sub)

    normalized = domain.strip().lower()
    now = datetime.utcnow()
    ttl_days = 30

    if not refresh:
        cached = (
            await db.execute(
                select(LLMArtifact)
                .where(
                    and_(
                        LLMArtifact.kind == "vision_v1",
                        LLMArtifact.domain == normalized,
                        LLMArtifact.prompt_version == VISION_PROMPT_VERSION,
                        (LLMArtifact.expires_at.is_(None) | (LLMArtifact.expires_at > now)),
                    )
                )
                .order_by(LLMArtifact.created_at.desc())
                .limit(1)
            )
        ).scalar_one_or_none()
        if cached:
            try:
                payload = json.loads(cached.payload_json)
                result = VisionResult.model_validate(payload)
            except Exception:
                # Corrupt cache: regenerate.
                cached = None
            else:
                return VisionResponse(
                    domain=normalized,
                    cached=True,
                    model=cached.model,
                    prompt_version=cached.prompt_version,
                    generated_at=cached.created_at.isoformat(),
                    result=result,
                )

    try:
        result, model_used = await generate_vision(normalized)
    except LLMGatewayError as e:
        raise HTTPException(status_code=502, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Vision generation failed: {e}")

    artifact = LLMArtifact(
        user_id=current_user.id,
        kind="vision_v1",
        domain=normalized,
        prompt_version=VISION_PROMPT_VERSION,
        model=model_used,
        payload_json=result.model_dump_json(),
        created_at=now,
        updated_at=now,
        expires_at=now + timedelta(days=ttl_days),
    )
    db.add(artifact)
    await db.commit()

    return VisionResponse(
        domain=normalized,
        cached=False,
        model=model_used,
        prompt_version=VISION_PROMPT_VERSION,
        generated_at=now.isoformat(),
        result=result,
    )


@router.get("/yield/landing-preview", response_model=YieldLandingPreviewResponse)
async def get_yield_landing_preview(
    current_user: CurrentUser,
    db: Database,
    domain: str = Query(..., min_length=3, max_length=255),
    refresh: bool = Query(False, description="Bypass cache and regenerate"),
):
    """
    Generate a Yield landing page configuration preview for Terminal UX.

    Trader + Tycoon: allowed.
    Scout: blocked (upgrade teaser).
    """
    sub = await _get_or_create_subscription(db, current_user.id)
    _require_trader_or_higher(sub)

    normalized = domain.strip().lower()
    now = datetime.utcnow()
    ttl_days = 30

    if not refresh:
        cached = (
            await db.execute(
                select(LLMArtifact)
                .where(
                    and_(
                        LLMArtifact.kind == "yield_landing_preview_v1",
                        LLMArtifact.domain == normalized,
                        LLMArtifact.prompt_version == YIELD_LANDING_PROMPT_VERSION,
                        (LLMArtifact.expires_at.is_(None) | (LLMArtifact.expires_at > now)),
                    )
                )
                .order_by(LLMArtifact.created_at.desc())
                .limit(1)
            )
        ).scalar_one_or_none()
        if cached:
            try:
                payload = json.loads(cached.payload_json)
                result = YieldLandingConfig.model_validate(payload)
            except Exception:
                cached = None
            else:
                return YieldLandingPreviewResponse(
                    domain=normalized,
                    cached=True,
                    model=cached.model,
                    prompt_version=cached.prompt_version,
                    generated_at=cached.created_at.isoformat(),
                    result=result,
                )

    try:
        result, model_used = await generate_yield_landing(normalized)
    except LLMGatewayError as e:
        raise HTTPException(status_code=502, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Landing preview generation failed: {e}")

    artifact = LLMArtifact(
        user_id=current_user.id,
        kind="yield_landing_preview_v1",
        domain=normalized,
        prompt_version=YIELD_LANDING_PROMPT_VERSION,
        model=model_used,
        payload_json=result.model_dump_json(),
        created_at=now,
        updated_at=now,
        expires_at=now + timedelta(days=ttl_days),
    )
    db.add(artifact)
    await db.commit()

    return YieldLandingPreviewResponse(
        domain=normalized,
        cached=False,
        model=model_used,
        prompt_version=YIELD_LANDING_PROMPT_VERSION,
        generated_at=now.isoformat(),
        result=result,
    )

