from __future__ import annotations

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

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

from ...auth_core import get_current_identity
from ...db import get_session
from ...repos.saas_event_repo import SaaSEventRepo
from ...services.saas_event_service import SaaSEventService
from ...telemetry.schemas import (
    SaaSEventBatchRequest,
    SaaSEventBatchResponse,
    SaaSEventDailyPoint,
    SaaSEventDailySeriesResponse,
    SaaSEventFailureResponse,
    SaaSEventSummaryResponse,
)

logger = logging.getLogger(__name__)

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


def _ensure_org(identity: dict) -> str:
    org_id = (identity or {}).get("org_id")
    if not org_id:
        raise HTTPException(
            status_code=403,
            detail={"error": "ORG_REQUIRED", "message": "No organization selected"},
        )
    return org_id


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)


@router.post(
    "/events:batch",
    response_model=SaaSEventBatchResponse,
    summary="Record telemetry events",
)
async def ingest_events_batch(
    payload: SaaSEventBatchRequest,
    identity: dict = Depends(get_current_identity),
    session: AsyncSession = Depends(get_session),
):
    """Ingest a batch of SaaS activity events with idempotent upsert semantics."""
    org_id = _ensure_org(identity)

    mismatched = [event.org_id for event in payload.events if event.org_id != org_id]
    if mismatched:
        logger.warning(
            "Rejecting telemetry batch due to org mismatch",
            extra={
                "expected_org": org_id,
                "received_orgs": list(set(mismatched)),
                "event_count": len(payload.events),
            },
        )
        raise HTTPException(
            status_code=400,
            detail={
                "error": "ORG_MISMATCH",
                "message": "Event org_id does not match authenticated org",
            },
        )

    service = SaaSEventService(SaaSEventRepo(session))
    inserted, duplicates = await service.ingest_batch(payload.events)
    return SaaSEventBatchResponse(inserted=inserted, duplicates=duplicates)


@router.get(
    "/summary",
    response_model=SaaSEventSummaryResponse,
    summary="Aggregate usage summary",
)
async def telemetry_summary(
    identity: dict = Depends(get_current_identity),
    session: AsyncSession = Depends(get_session),
    since: Optional[datetime] = Query(
        None,
        description="Lower bound timestamp (ISO8601); defaults to 30 days ago in client.",
    ),
    actions_limit: int = Query(
        5, ge=1, le=20, description="Top action counts to include in the response."
    ),
):
    org_id = _ensure_org(identity)
    service = SaaSEventService(SaaSEventRepo(session))
    summary = await service.summary(
        org_id, since=_normalize_query_dt(since), actions_limit=actions_limit
    )
    return SaaSEventSummaryResponse(
        org_id=summary.org_id,
        total_events=summary.total_events,
        failures=summary.failures,
        credits_used=summary.credits_used,
        last_event_at=summary.last_event_at,
        top_actions=[
            {"action": action, "count": count} for action, count in summary.top_actions
        ],
    )


@router.get(
    "/failures",
    response_model=List[SaaSEventFailureResponse],
    summary="Recent failure events",
)
async def telemetry_failures(
    identity: dict = Depends(get_current_identity),
    session: AsyncSession = Depends(get_session),
    since: Optional[datetime] = Query(
        None, description="Limit failures to events on/after this timestamp."
    ),
    action: Optional[str] = Query(None, description="Filter failures by action name."),
    limit: int = Query(
        50, ge=1, le=200, description="Maximum number of failure events to return."
    ),
):
    org_id = _ensure_org(identity)
    service = SaaSEventService(SaaSEventRepo(session))
    failures = await service.recent_failures(
        org_id,
        since=_normalize_query_dt(since),
        action=action,
        limit=limit,
    )
    return [
        SaaSEventFailureResponse(
            id=failure.id,
            ts=failure.ts,
            action=failure.action,
            status=failure.status,
            sheet_id=failure.sheet_id,
            tab_name=failure.tab_name,
            user_email=failure.user_email,
            source_version=failure.source_version,
            attrs=dict(failure.attrs or {}),
        )
        for failure in failures
    ]


@router.get(
    "/credits/daily",
    response_model=SaaSEventDailySeriesResponse,
    summary="Daily credits and failure rollup",
)
async def telemetry_daily_credits(
    identity: dict = Depends(get_current_identity),
    session: AsyncSession = Depends(get_session),
    days: int = Query(
        30, ge=1, le=180, description="Number of days of history to return."
    ),
):
    org_id = _ensure_org(identity)
    service = SaaSEventService(SaaSEventRepo(session))
    rows = await service.daily_rollup(org_id, days=days)
    points = [
        SaaSEventDailyPoint(
            day=row["day"],
            events=int(row["events"]),
            credits_used=int(row["credits_used"]),
            failures=int(row["failures"]),
        )
        for row in rows
    ]
    return SaaSEventDailySeriesResponse(org_id=org_id, points=points)
