from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, Field


ChangeAction = Literal["upsert", "delete"]
ChangeDomain = Literal["global", "report"]
EnterpriseRole = Literal["admin", "zootech", "viewer"]
ConflictResolution = Literal["keep_orchestrator", "keep_hub"]


class ChangeEventIn(BaseModel):
    event_id: str
    seq: int
    domain: ChangeDomain = "global"
    table: str = Field(alias="table")
    record_id: str
    action: ChangeAction
    version: int = 1
    content_hash: str = ""
    payload: dict[str, Any] = Field(default_factory=dict)
    emitted_at: datetime
    origin_site_id: str | None = None

    model_config = {"populate_by_name": True}


class ChangeEventOut(BaseModel):
    event_id: str
    seq: int
    domain: ChangeDomain
    table: str
    record_id: str
    action: ChangeAction
    version: int
    content_hash: str
    payload: dict[str, Any]
    emitted_at: datetime
    origin_site_id: str | None = None


class PushChangesRequest(BaseModel):
    events: list[ChangeEventIn]


class PushChangesResponse(BaseModel):
    applied_event_ids: list[str]
    held_event_ids: list[str]
    conflicts: list[str]


class PullChangesRequest(BaseModel):
    cursor: int = 0
    limit: int = 100


class PullChangesResponse(BaseModel):
    events: list[ChangeEventOut]
    next_cursor: int


class AckChangesRequest(BaseModel):
    event_ids: list[str]
    direction: Literal["inbound", "outbound"] = "inbound"


class AckChangesResponse(BaseModel):
    last_acked_seq: int


class SyncCapabilitiesResponse(BaseModel):
    protocol_version: str = "1.0"
    domains: list[str] = Field(default_factory=lambda: ["global", "report"])


class HeartbeatRequest(BaseModel):
    wesp_version: str | None = None
    lag_seconds: int | None = None


class HeartbeatResponse(BaseModel):
    status: str = "ok"
    server_time: datetime


class PairStartRequest(BaseModel):
    enterprise_id: str
    farm_name: str


class PairStartResponse(BaseModel):
    session_id: str
    code: str
    expires_at: datetime


class PairConfirmRequest(BaseModel):
    code: str
    hub_site_id: str
    hub_name: str | None = None
    hub_url: str | None = None


class PairConfirmResponse(BaseModel):
    farm_hub_id: str
    hub_site_id: str
    api_key: str
    enterprise_id: str


class ConflictSummary(BaseModel):
    id: str
    table_name: str
    record_id: str
    farm_hub_id: str | None
    status: str
    created_at: datetime


class ConflictDetail(ConflictSummary):
    orchestrator_snapshot: dict[str, Any]
    hub_snapshot: dict[str, Any]
    held_event_ids: list[str]


class ResolveConflictRequest(BaseModel):
    resolution: ConflictResolution


class EnterpriseOut(BaseModel):
    id: str
    name: str
    slug: str
    status: str


class FarmHubOut(BaseModel):
    id: str
    enterprise_id: str
    name: str
    hub_site_id: str
    url: str | None
    status: str
    last_seen: datetime | None
    wesp_version: str | None


@dataclass
class HubAuthContext:
    farm_hub_id: str
    hub_site_id: str
    enterprise_id: str
    farm_hub: Any


@dataclass
class TenantContext:
    enterprise_id: str
    enterprise_role: EnterpriseRole | None
    user_id: str | None
    farm_hub_ids: list[str] | None
