from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, field_validator, model_validator

from ..models import SaaSEventStatus


MULTI_STEP_ACTIONS = {
    "match_started",
    "match_completed",
    "sf_push_started",
    "sf_push_completed",
    "sf_push_failed",
    "enrichment_started",
    "enrichment_completed",
    "enrichment_failed",
    "credits_refunded",
}


def _default_ts() -> datetime:
    return datetime.now(timezone.utc)


class SaaSEventPayload(BaseModel):
    id: Optional[str] = None
    ts: datetime = Field(default_factory=_default_ts)
    org_id: str = Field(min_length=1, max_length=128)
    user_email: str = Field(min_length=1, max_length=256)  # Allow placeholders like 'sheets_user'
    sheet_id: str = Field(min_length=1, max_length=256)
    tab_name: Optional[str] = Field(default=None, max_length=256)
    source_version: Optional[str] = Field(default=None, max_length=32)
    source: Optional[str] = Field(default=None, max_length=64)
    action: str = Field(min_length=1, max_length=128)
    status: SaaSEventStatus
    rows: Optional[int] = None
    duration_ms: Optional[int] = None
    credits_delta: Optional[int] = None
    job_id: Optional[str] = Field(default=None, max_length=128)
    idempotency_key: str = Field(min_length=1, max_length=160)
    attrs: Dict[str, Any] = Field(default_factory=dict)

    @field_validator("source", mode="before")
    @classmethod
    def _default_source(cls, value: Optional[str]) -> str:
        if value is None or str(value).strip() == "":
            return "sheets_addon"
        return str(value)

    @field_validator("rows", "duration_ms", "credits_delta", mode="before")
    @classmethod
    def _coerce_int(cls, value: Any) -> Optional[int]:
        if value is None or value == "":
            return None
        try:
            return int(value)
        except (TypeError, ValueError) as exc:
            raise ValueError("Must be an integer") from exc

    @field_validator("attrs", mode="before")
    @classmethod
    def _ensure_dict(cls, value: Any) -> Dict[str, Any]:
        if value is None:
            return {}
        if isinstance(value, dict):
            return value
        raise ValueError("attrs must be an object")

    @model_validator(mode="after")
    def _validate_job_id(self) -> "SaaSEventPayload":
        if self.action in MULTI_STEP_ACTIONS and not self.job_id:
            raise ValueError(f"job_id is required for action '{self.action}'")
        return self

    def normalized_ts(self) -> datetime:
        """Return timestamp in UTC with tzinfo."""
        if self.ts.tzinfo is None:
            return self.ts.replace(tzinfo=timezone.utc)
        return self.ts.astimezone(timezone.utc)


class SaaSEventBatchRequest(BaseModel):
    events: List[SaaSEventPayload]

    @model_validator(mode="after")
    def _require_events(self) -> "SaaSEventBatchRequest":
        if not self.events:
            raise ValueError("events cannot be empty")
        return self


class SaaSEventBatchResponse(BaseModel):
    inserted: int
    duplicates: int


class SaaSEventSummaryAction(BaseModel):
    action: str
    count: int


class SaaSEventSummaryResponse(BaseModel):
    org_id: str
    total_events: int
    failures: int
    credits_used: int
    last_event_at: Optional[datetime]
    top_actions: List[SaaSEventSummaryAction]


class SaaSEventFailureResponse(BaseModel):
    id: str
    ts: datetime
    action: str
    status: SaaSEventStatus
    sheet_id: str
    tab_name: Optional[str]
    user_email: str
    source_version: Optional[str]
    attrs: Dict[str, Any]


class SaaSEventDailyPoint(BaseModel):
    day: datetime
    events: int
    credits_used: int
    failures: int


class SaaSEventDailySeriesResponse(BaseModel):
    org_id: str
    points: List[SaaSEventDailyPoint]
