"""FastAPI server for FOMO Guard."""

import logging
from datetime import datetime
from typing import Any

from fastapi import FastAPI, HTTPException, Query, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from pydantic import BaseModel, field_validator

from .config import get_config
from .core.analyzer import FOMOAnalyzer, AnalysisError
from .core.history import HistoryTracker
from .core.scorer import FOMOScore, FOMOScorer
from .providers.base import CoinNotFoundError, RateLimitError, ProviderError
from .providers.coingecko import validate_symbol
from .utils.cooldown import CooldownTracker

logger = logging.getLogger(__name__)

app = FastAPI(
    title="FOMO Guard API",
    description="API to help crypto newcomers avoid FOMO-driven decisions",
    version="0.1.0",
    docs_url="/docs",
    redoc_url="/redoc",
)

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# Exception handlers
@app.exception_handler(CoinNotFoundError)
async def coin_not_found_handler(request: Request, exc: CoinNotFoundError):
    return JSONResponse(
        status_code=404,
        content={"error": "coin_not_found", "detail": str(exc)},
    )


@app.exception_handler(RateLimitError)
async def rate_limit_handler(request: Request, exc: RateLimitError):
    return JSONResponse(
        status_code=429,
        content={
            "error": "rate_limited",
            "detail": str(exc),
            "retry_after": exc.retry_after,
        },
        headers={"Retry-After": str(exc.retry_after)} if exc.retry_after else {},
    )


@app.exception_handler(AnalysisError)
async def analysis_error_handler(request: Request, exc: AnalysisError):
    return JSONResponse(
        status_code=400,
        content={"error": "analysis_failed", "detail": str(exc)},
    )


# Response models
class ErrorResponse(BaseModel):
    """Error response."""

    error: str
    detail: str


class HealthResponse(BaseModel):
    """Health check response."""

    status: str
    version: str
    timestamp: datetime


class ScoreResponse(BaseModel):
    """Simple score response."""

    symbol: str
    score: float
    verdict: str
    risk_level: str
    timestamp: datetime
    warnings: list[str] = []


class SignalBreakdown(BaseModel):
    """Signal breakdown in analysis response."""

    category: str
    score: float
    weight: float
    risk_level: str
    summary: str
    available: bool


class AnalysisResponse(BaseModel):
    """Full analysis response."""

    symbol: str
    score: float
    verdict: str
    risk_level: str
    recommendation: str
    suggested_cooldown_hours: int
    signals: list[SignalBreakdown]
    timestamp: datetime
    warnings: list[str] = []


class HistoryEvent(BaseModel):
    """Historical FOMO event."""

    date: datetime
    price_at_event: float
    ath_proximity: float
    price_change_24h: float
    price_change_7d: float
    change_after_7d: float | None
    change_after_30d: float | None


class HistoryResponse(BaseModel):
    """History response."""

    symbol: str
    events: list[HistoryEvent]
    summary: dict[str, Any]


class CooldownRequest(BaseModel):
    """Cooldown registration request."""

    symbol: str
    hours: int | None = None

    @field_validator("symbol")
    @classmethod
    def validate_symbol_format(cls, v: str) -> str:
        return validate_symbol(v)


class CooldownResponse(BaseModel):
    """Cooldown response."""

    symbol: str
    started: str
    expires: str
    remaining: str


class CooldownStatusResponse(BaseModel):
    """Active cooldowns response."""

    cooldowns: list[CooldownResponse]


class ValidateResponse(BaseModel):
    """Symbol validation response."""

    symbol: str
    valid: bool
    message: str | None = None


# Endpoints
@app.get("/api/v1/health", response_model=HealthResponse)
async def health_check():
    """Health check endpoint."""
    return HealthResponse(
        status="healthy",
        version="0.1.0",
        timestamp=datetime.now(),
    )


@app.get("/api/v1/validate/{symbol}", response_model=ValidateResponse)
async def validate_coin(symbol: str):
    """Validate if a cryptocurrency symbol exists."""
    try:
        normalized = validate_symbol(symbol)
    except ValueError as e:
        return ValidateResponse(symbol=symbol, valid=False, message=str(e))

    async with FOMOAnalyzer() as analyzer:
        exists = await analyzer.validate_symbol(normalized)

    if exists:
        return ValidateResponse(symbol=normalized, valid=True)
    else:
        return ValidateResponse(
            symbol=normalized, valid=False, message="Coin not found on CoinGecko"
        )


@app.get("/api/v1/score/{symbol}", response_model=ScoreResponse)
async def get_score(symbol: str):
    """Get FOMO score for a cryptocurrency."""
    async with FOMOAnalyzer() as analyzer:
        fomo_score = await analyzer.analyze(symbol)

    return ScoreResponse(
        symbol=fomo_score.symbol,
        score=fomo_score.score,
        verdict=fomo_score.verdict.value,
        risk_level=fomo_score.risk_level.value,
        timestamp=fomo_score.timestamp,
        warnings=fomo_score.warnings,
    )


@app.get("/api/v1/check/{symbol}", response_model=AnalysisResponse)
async def check_fomo(
    symbol: str,
    detailed: bool = Query(False, description="Include detailed indicator breakdown"),
):
    """Full FOMO analysis for a cryptocurrency."""
    async with FOMOAnalyzer() as analyzer:
        fomo_score = await analyzer.analyze(symbol, detailed=detailed)

    signals = [
        SignalBreakdown(
            category=s.category.value,
            score=s.score,
            weight=s.weight,
            risk_level=s.risk_level.value,
            summary=s.summary,
            available=s.available,
        )
        for s in fomo_score.signal_results
    ]

    return AnalysisResponse(
        symbol=fomo_score.symbol,
        score=fomo_score.score,
        verdict=fomo_score.verdict.value,
        risk_level=fomo_score.risk_level.value,
        recommendation=fomo_score.recommendation,
        suggested_cooldown_hours=fomo_score.suggested_cooldown_hours,
        signals=signals,
        timestamp=fomo_score.timestamp,
        warnings=fomo_score.warnings,
    )


@app.get("/api/v1/history/{symbol}", response_model=HistoryResponse)
async def get_history(
    symbol: str,
    days: int = Query(90, description="Historical lookback in days", ge=7, le=365),
):
    """Get historical FOMO events for a cryptocurrency."""
    # Validate symbol first
    try:
        symbol = validate_symbol(symbol)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    tracker = HistoryTracker()
    events = await tracker.get_fomo_events(symbol, days)

    history_events = [
        HistoryEvent(
            date=e.date,
            price_at_event=e.price_at_event,
            ath_proximity=e.ath_proximity,
            price_change_24h=e.price_change_24h,
            price_change_7d=e.price_change_7d,
            change_after_7d=e.change_after_7d,
            change_after_30d=e.change_after_30d,
        )
        for e in events
    ]

    # Calculate summary
    summary: dict[str, Any] = {"total_events": len(events)}
    if events:
        changes_7d = [e.change_after_7d for e in events if e.change_after_7d is not None]
        changes_30d = [e.change_after_30d for e in events if e.change_after_30d is not None]

        if changes_7d:
            summary["avg_change_7d"] = round(sum(changes_7d) / len(changes_7d), 2)
            summary["pct_negative_7d"] = round(
                sum(1 for c in changes_7d if c < 0) / len(changes_7d) * 100, 1
            )

        if changes_30d:
            summary["avg_change_30d"] = round(sum(changes_30d) / len(changes_30d), 2)
            summary["pct_negative_30d"] = round(
                sum(1 for c in changes_30d if c < 0) / len(changes_30d) * 100, 1
            )

    return HistoryResponse(
        symbol=symbol.upper(),
        events=history_events,
        summary=summary,
    )


@app.post("/api/v1/cool", response_model=CooldownResponse)
async def register_cooldown(request: CooldownRequest):
    """Register a cooling period for a cryptocurrency."""
    config = get_config()
    tracker = CooldownTracker()

    hours = request.hours or config.settings.default_cooldown_hours
    cooldown = tracker.add_cooldown(request.symbol, hours)

    return CooldownResponse(
        symbol=cooldown["symbol"],
        started=cooldown["started"],
        expires=cooldown["expires"],
        remaining=f"{hours} hours",
    )


@app.get("/api/v1/cool/status", response_model=CooldownStatusResponse)
async def get_cooldown_status():
    """Get active cooldowns."""
    tracker = CooldownTracker()
    active = tracker.get_active_cooldowns()

    cooldowns = [
        CooldownResponse(
            symbol=cd["symbol"],
            started=cd["started"],
            expires=cd["expires"],
            remaining=cd["remaining"],
        )
        for cd in active
    ]

    return CooldownStatusResponse(cooldowns=cooldowns)


@app.delete("/api/v1/cool/{symbol}")
async def remove_cooldown(symbol: str):
    """Remove a cooldown."""
    try:
        symbol = validate_symbol(symbol)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    tracker = CooldownTracker()
    removed = tracker.remove_cooldown(symbol)

    if removed:
        return {"status": "removed", "symbol": symbol.upper()}
    else:
        raise HTTPException(status_code=404, detail=f"No cooldown found for {symbol}")


# Clear cache endpoint
@app.post("/api/v1/cache/clear")
async def clear_cache():
    """Clear the response cache."""
    from .providers.base import ResponseCache

    cache = ResponseCache()
    cache.clear()
    return {"status": "cleared"}


# Root redirect to docs
@app.get("/")
async def root():
    """Redirect to API documentation."""
    return {
        "message": "Welcome to FOMO Guard API",
        "docs": "/docs",
        "health": "/api/v1/health",
    }
