from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from ...auth_security import AdminContext, require_admin
from ...db import get_session
from ...repos.saas_event_repo import (
    SaaSEventOverview,
    SaaSEventOverviewDay,
    SaaSEventOverviewFailure,
    SaaSEventOverviewFeature,
    SaaSEventOverviewKPIs,
    SaaSEventLogRow,
    SaaSEventRepo,
)

router = APIRouter(prefix="/admin/telemetry", tags=["Admin Telemetry"])


class TelemetryOverviewKpis(BaseModel):
    events_24h: int
    error_rate: float
    unique_orgs: int
    p95_build_duration_ms: Optional[float] = None


class TelemetryOverviewDay(BaseModel):
    date: str
    started: int
    success: int
    failed: int


class TelemetryOverviewFeature(BaseModel):
    feature: str
    count: int


class TelemetryOverviewFailure(BaseModel):
    ts: datetime
    feature: Optional[str] = None
    action: str
    error_code: Optional[str] = None
    duration_ms: Optional[int] = None
    org_id: str


class TelemetryOverviewResponse(BaseModel):
    kpis: TelemetryOverviewKpis
    events_by_day: List[TelemetryOverviewDay]
    top_features: List[TelemetryOverviewFeature]
    recent_failures: List[TelemetryOverviewFailure]


class TelemetryLogEvent(BaseModel):
    id: str
    ts: datetime
    org_id: str
    action: str
    status: str
    feature: Optional[str] = None
    error_code: Optional[str] = None
    error_stage: Optional[str] = None
    error_hint: Optional[str] = None
    duration_ms: Optional[int] = None
    rows: Optional[int] = None
    source_version: Optional[str] = None


class TelemetryLogResponse(BaseModel):
    total: int
    events: List[TelemetryLogEvent]


_ALLOWED_STATUS = {"started", "success", "failed"}


def _normalize_query_dt(value: Optional[datetime]) -> Optional[datetime]:
    if value is None:
        return None
    if value.tzinfo is None:
        return value.replace(tzinfo=timezone.utc)
    return value.astimezone(timezone.utc)


def _parse_csv(value: Optional[str]) -> Optional[List[str]]:
    if not value:
        return None
    items = [item.strip() for item in value.split(",") if item and item.strip()]
    return items or None


def _normalize_status(values: Optional[List[str]]) -> Optional[List[str]]:
    if not values:
        return None
    normalized = [value.strip().lower() for value in values if value and value.strip()]
    invalid = [value for value in normalized if value not in _ALLOWED_STATUS]
    if invalid:
        raise HTTPException(
            status_code=400,
            detail={
                "error": "INVALID_STATUS",
                "message": f"Unsupported status values: {', '.join(invalid)}",
            },
        )
    return normalized or None


def _strip_action_prefix(action: str, feature: Optional[str]) -> str:
    if not feature:
        return action
    prefix = f"{feature}_"
    if action.startswith(prefix):
        return action[len(prefix) :]
    return action


def _to_kpis(kpis: SaaSEventOverviewKPIs) -> TelemetryOverviewKpis:
    return TelemetryOverviewKpis(
        events_24h=kpis.events_24h,
        error_rate=kpis.error_rate,
        unique_orgs=kpis.unique_orgs,
        p95_build_duration_ms=kpis.p95_build_duration_ms,
    )


def _to_day(row: SaaSEventOverviewDay) -> TelemetryOverviewDay:
    return TelemetryOverviewDay(
        date=row.date,
        started=row.started,
        success=row.success,
        failed=row.failed,
    )


def _to_feature(row: SaaSEventOverviewFeature) -> TelemetryOverviewFeature:
    return TelemetryOverviewFeature(feature=row.feature, count=row.count)


def _to_failure(row: SaaSEventOverviewFailure) -> TelemetryOverviewFailure:
    return TelemetryOverviewFailure(
        ts=row.ts,
        feature=row.feature,
        action=_strip_action_prefix(row.action, row.feature),
        error_code=row.error_code,
        duration_ms=row.duration_ms,
        org_id=row.org_id,
    )


def _to_log_event(row: SaaSEventLogRow) -> TelemetryLogEvent:
    return TelemetryLogEvent(
        id=row.id,
        ts=row.ts,
        org_id=row.org_id,
        action=row.action,
        status=row.status,
        feature=row.feature,
        error_code=row.error_code,
        error_stage=row.error_stage,
        error_hint=row.error_hint,
        duration_ms=row.duration_ms,
        rows=row.rows,
        source_version=row.source_version,
    )


@router.get("/overview", response_model=TelemetryOverviewResponse)
async def telemetry_overview(
    since: Optional[datetime] = Query(None, description="Lower bound timestamp (ISO8601)"),
    until: Optional[datetime] = Query(None, description="Upper bound timestamp (ISO8601)"),
    features: Optional[str] = Query(None, description="CSV list of features"),
    status: Optional[str] = Query(None, description="CSV list of statuses"),
    limit: int = Query(25, ge=1, le=200),
    _: AdminContext = Depends(require_admin),
    session: AsyncSession = Depends(get_session),
) -> TelemetryOverviewResponse:
    now = datetime.now(timezone.utc)
    since_dt = _normalize_query_dt(since) or (now - timedelta(days=7))
    until_dt = _normalize_query_dt(until) or now

    if since_dt > until_dt:
        raise HTTPException(
            status_code=400,
            detail={"error": "INVALID_RANGE", "message": "since must be <= until"},
        )

    feature_list = _parse_csv(features)
    status_list = _normalize_status(_parse_csv(status))

    repo = SaaSEventRepo(session)
    overview: SaaSEventOverview = await repo.overview_stats(
        since=since_dt,
        until=until_dt,
        features=feature_list,
        status=status_list,
        limit=limit,
    )

    return TelemetryOverviewResponse(
        kpis=_to_kpis(overview.kpis),
        events_by_day=[_to_day(row) for row in overview.events_by_day],
        top_features=[_to_feature(row) for row in overview.top_features],
        recent_failures=[_to_failure(row) for row in overview.recent_failures],
    )


@router.get("/events", response_model=TelemetryLogResponse)
async def telemetry_events(
    since: Optional[datetime] = Query(None, description="Lower bound timestamp (ISO8601)"),
    until: Optional[datetime] = Query(None, description="Upper bound timestamp (ISO8601)"),
    feature: Optional[str] = Query(None),
    action: Optional[str] = Query(None),
    error_code: Optional[str] = Query(None),
    status: Optional[str] = Query(None, description="CSV list of statuses"),
    org_id: Optional[str] = Query(None),
    source_version: Optional[str] = Query(None),
    limit: int = Query(100, ge=1, le=500),
    offset: int = Query(0, ge=0),
    _: AdminContext = Depends(require_admin),
    session: AsyncSession = Depends(get_session),
) -> TelemetryLogResponse:
    now = datetime.now(timezone.utc)
    since_dt = _normalize_query_dt(since) or (now - timedelta(days=1))
    until_dt = _normalize_query_dt(until) or now

    if since_dt > until_dt:
        raise HTTPException(
            status_code=400,
            detail={"error": "INVALID_RANGE", "message": "since must be <= until"},
        )

    status_list = _normalize_status(_parse_csv(status))

    repo = SaaSEventRepo(session)
    events, total = await repo.list_events(
        since=since_dt,
        until=until_dt,
        feature=feature,
        action=action,
        error_code=error_code,
        status=status_list,
        org_id=org_id,
        source_version=source_version,
        limit=limit,
        offset=offset,
    )

    return TelemetryLogResponse(
        total=total,
        events=[_to_log_event(row) for row in events],
    )
