"""Pydantic event and API contracts for the FastLane integration.

Aligned with BASF Fastlane "Asset API for carriers" v2.0.0
(getPublishedVersion / newAsset / updateAsset + reviewCompleted webhook).
"""
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Optional, Literal
from uuid import uuid4

from pydantic import BaseModel, Field


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


class AllocationStatus(str, Enum):
    PENDING = "pending"
    COMPLETED = "completed"
    FAILED = "failed"
    BLOCKED = "blocked"


# ---------------------------------------------------------------------------
# ERP → Command API (POST /tank-allocation)
# ---------------------------------------------------------------------------
class TankAllocationCommand(BaseModel):
    """Payload sent by ERP when a tank is allocated to a BASF job."""

    job_id: str
    tank_id: str                       # internal ERP tank id
    licence_number: str                # ERP tank_no / plate number (Fastlane identifier)
    nationality: Optional[str] = None  # Not required for tanks (per client A12)
    vehicle_type: int = 311            # Fastlane tank type (311=20ft, 312=32ft swap, 313/314 variants)
    is_dangerous_goods: bool = False   # controls locationList (1 = non-DG, 2 = DG)
    location_list: Optional[list[str]] = None
    fields: dict[str, Any] = Field(default_factory=dict)   # Fastlane master-data fields
    owner: Optional[dict[str, str]] = None                 # {name,street,zipCode,city,country}
    # Optional supporting documents to upload right after upsert (client A7/A8):
    #   [{ "name": "inspection.pdf", "fields": ["general_inspection"],
    #      "content_base64": "..." }, ...]
    documents: list[dict[str, Any]] = Field(default_factory=list)
    # Client A9: only trigger commitAsset when the ERP explicitly says the
    # asset is ready (all required docs uploaded & accepted by BASF).
    commit_ready: bool = False
    basf_ref: Optional[str] = None
    requested_by: Optional[str] = None
    fastlane_asset_id: Optional[int] = None  # If ERP already knows the FastLane asset id → skip lookup
    metadata: dict[str, Any] = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Kafka event: fastlane.tank.allocate.requested
# ---------------------------------------------------------------------------
class TankAllocationRequestedEvent(BaseModel):
    event_id: str = Field(default_factory=lambda: str(uuid4()))
    event_type: Literal["fastlane.tank.allocate.requested"] = (
        "fastlane.tank.allocate.requested"
    )
    occurred_at: datetime = Field(default_factory=_utcnow)

    job_id: str
    tank_id: str
    licence_number: str
    nationality: Optional[str] = None
    vehicle_type: int = 311
    is_dangerous_goods: bool = False
    location_list: Optional[list[str]] = None
    fields: dict[str, Any] = Field(default_factory=dict)
    owner: Optional[dict[str, str]] = None
    documents: list[dict[str, Any]] = Field(default_factory=list)
    commit_ready: bool = False
    basf_ref: Optional[str] = None
    requested_by: Optional[str] = None
    fastlane_asset_id: Optional[int] = None  # If ERP already knows the FastLane asset id → skip lookup
    metadata: dict[str, Any] = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Fastlane → Webhook (Section 5 of the spec)
# ---------------------------------------------------------------------------
class FastlaneNotificationPayload(BaseModel):
    id: int
    incrementalVersion: int
    status: str
    locationList: list[str] = Field(default_factory=list)
    licence_number: Optional[str] = None
    nationality: Optional[str] = None
    reviewDetails: Optional[str] = None


class FastlaneNotification(BaseModel):
    publicKey: Optional[str] = None
    type: str                          # asset:reviewCompleted | asset:reviewReverted
    payload: FastlaneNotificationPayload


# ---------------------------------------------------------------------------
# Kafka status event
# ---------------------------------------------------------------------------
class TankAllocationStatusEvent(BaseModel):
    event_id: str = Field(default_factory=lambda: str(uuid4()))
    event_type: str
    occurred_at: datetime = Field(default_factory=_utcnow)

    job_id: Optional[str] = None
    tank_id: Optional[str] = None
    status: AllocationStatus
    fastlane_id: Optional[int] = None
    licence_number: Optional[str] = None
    nationality: Optional[str] = None
    incremental_version: Optional[int] = None
    message: Optional[str] = None
    raw: dict[str, Any] = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Payload POSTed by the Status Listener to the ERP status endpoint
# ---------------------------------------------------------------------------
class ErpStatusUpdate(BaseModel):
    job_id: Optional[str] = None
    tank_id: Optional[str] = None
    licence_number: Optional[str] = None
    status: AllocationStatus
    fastlane_id: Optional[int] = None
    incremental_version: Optional[int] = None
    message: Optional[str] = None
    occurred_at: datetime = Field(default_factory=_utcnow)


# ===========================================================================
# Single-task pipeline events (James's minion pattern)
# ---------------------------------------------------------------------------
# Each event carries the immutable envelope (job_id, tank_id, licence_number,
# vehicle_type, is_dangerous_goods, owner, location_list, documents, ...)
# plus the outputs of the step that just ran (fastlane_id, incremental_version).
# The next minion needs nothing more than this to do its one job.
# ===========================================================================
class _PipelineEnvelope(BaseModel):
    event_id: str = Field(default_factory=lambda: str(uuid4()))
    occurred_at: datetime = Field(default_factory=_utcnow)

    # Business identity — copied unchanged across every hop.
    job_id: str
    tank_id: str
    licence_number: str
    nationality: Optional[str] = None
    vehicle_type: int = 311
    is_dangerous_goods: bool = False
    location_list: Optional[list[str]] = None
    fields: dict[str, Any] = Field(default_factory=dict)
    owner: Optional[dict[str, str]] = None
    documents: list[dict[str, Any]] = Field(default_factory=list)
    commit_ready: bool = False
    basf_ref: Optional[str] = None
    requested_by: Optional[str] = None
    metadata: dict[str, Any] = Field(default_factory=dict)

    # Retry counter — bumped by each minion; step-DLQ when > max_retries.
    attempts: int = 0


class AssetLookupDoneEvent(_PipelineEnvelope):
    """Emitted by lookup-minion. Feeds upsert-minion."""
    event_type: Literal["fastlane.asset.lookup.done"] = "fastlane.asset.lookup.done"
    fastlane_asset_id: Optional[int] = None            # None → newAsset path
    incremental_version: Optional[int] = None


class AssetUpsertedEvent(_PipelineEnvelope):
    """Emitted by upsert-minion. Feeds file-upload-minion."""
    event_type: Literal["fastlane.asset.upserted"] = "fastlane.asset.upserted"
    fastlane_asset_id: int
    incremental_version: int
    was_created: bool = False    # true=newAsset, false=updateAsset


class AssetDocsUploadedEvent(_PipelineEnvelope):
    """Emitted by file-upload-minion. Terminal for the "upsert path"; the
    commit is triggered later, out-of-band, by a DocsAcceptedEvent."""
    event_type: Literal["fastlane.asset.docs.uploaded"] = "fastlane.asset.docs.uploaded"
    fastlane_asset_id: int
    incremental_version: int
    documents_uploaded: int = 0


class DocsAcceptedEvent(BaseModel):
    """Emitted by webhook when BASF confirms the document review is complete.

    This is the ONLY trigger for commit-minion in production. `fastlane_callback.php`
    is no longer allowed to orchestrate commit.
    """
    event_id: str = Field(default_factory=lambda: str(uuid4()))
    event_type: Literal["fastlane.docs.accepted"] = "fastlane.docs.accepted"
    occurred_at: datetime = Field(default_factory=_utcnow)

    fastlane_asset_id: int
    incremental_version: int
    licence_number: Optional[str] = None
    location_list: Optional[list[str]] = None
    # Envelope for tracking back to the ERP (optional; webhook may not know these).
    job_id: Optional[str] = None
    tank_id: Optional[str] = None
    attempts: int = 0
