"""Travel legs for the Kalendarz tab (AV-009, task 2): manually-added
flight/car/bus/train segments with a cost, on migration 005's
journey_travel_legs table. Unlike RouteRevision/JourneyPlan/AnalysisRun/
AnalysisScenario, a travel leg carries no derived analysis and is not part
of that append-only lineage -- it is a plain mutable row, edited and
deleted in place through this one repository, never versioned.

Each instance owns its own connection (same per-request-connection
philosophy as JourneyPlanRepository/JourneyRouteRepository)."""

from __future__ import annotations

import uuid
from dataclasses import dataclass
from typing import Any

from .persistence import JourneySqliteDatabase, _now, _transaction, _validate_identifier

TRAVEL_LEG_KINDS = ("FLIGHT", "CAR", "BUS", "TRAIN")


@dataclass(frozen=True)
class TravelLeg:
    travel_leg_id: str
    journey_id: str
    kind: str
    origin: str
    destination: str
    departure_local: str
    arrival_local: str
    cost_amount: float | None
    cost_currency: str
    created_at: str
    updated_at: str
    # AV-038 (migration 011): what a booking has.
    title: str | None = None
    description: str | None = None
    reservation_number: str | None = None
    link: str | None = None

    def to_dict(self) -> dict[str, Any]:
        return {
            "travel_leg_id": self.travel_leg_id,
            "journey_id": self.journey_id,
            "kind": self.kind,
            "origin": self.origin,
            "destination": self.destination,
            "departure_local": self.departure_local,
            "arrival_local": self.arrival_local,
            "cost_amount": self.cost_amount,
            "cost_currency": self.cost_currency,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
            "title": self.title,
            "description": self.description,
            "reservation_number": self.reservation_number,
            "link": self.link,
        }


@dataclass(frozen=True)
class TravelLegFields:
    """The fields a caller may set/change on a travel leg -- shared by
    create and update so both validate identically."""

    kind: str
    origin: str
    destination: str
    departure_local: str
    arrival_local: str
    cost_amount: float | None = None
    cost_currency: str = "PLN"
    title: str | None = None
    description: str | None = None
    reservation_number: str | None = None
    link: str | None = None

    def __post_init__(self) -> None:
        if self.link and not self.link.startswith(("http://", "https://")):
            raise ValueError("link must start with http:// or https://")
        if self.kind not in TRAVEL_LEG_KINDS:
            raise ValueError(f"kind must be one of {TRAVEL_LEG_KINDS}")
        if not self.origin or not self.origin.strip():
            raise ValueError("origin must be non-empty")
        if not self.destination or not self.destination.strip():
            raise ValueError("destination must be non-empty")
        if not self.departure_local or not self.arrival_local:
            raise ValueError("departure_local and arrival_local are required")
        if self.arrival_local <= self.departure_local:
            raise ValueError("arrival_local must be after departure_local")
        if self.cost_amount is not None and self.cost_amount < 0:
            raise ValueError("cost_amount must be non-negative")
        if not self.cost_currency or not self.cost_currency.strip():
            raise ValueError("cost_currency must be non-empty")


def new_travel_leg_id() -> str:
    return f"journey-travel-leg-v0_1:{uuid.uuid4()}"


def _leg_from_row(row) -> TravelLeg:
    return TravelLeg(
        travel_leg_id=row["travel_leg_id"],
        journey_id=row["journey_id"],
        kind=row["kind"],
        origin=row["origin"],
        destination=row["destination"],
        departure_local=row["departure_local"],
        arrival_local=row["arrival_local"],
        cost_amount=row["cost_amount"],
        cost_currency=row["cost_currency"],
        created_at=row["created_at"],
        updated_at=row["updated_at"],
        title=row["title"],
        description=row["description"],
        reservation_number=row["reservation_number"],
        link=row["link"],
    )


class TravelLegRepository:
    def __init__(self, database: JourneySqliteDatabase):
        self._connection = database.connect()

    def close(self) -> None:
        self._connection.close()

    def _require_journey(self, journey_id: str, owner_id: str) -> None:
        row = self._connection.execute(
            "SELECT 1 FROM journeys WHERE journey_id = ? AND owner_id = ?",
            (journey_id, owner_id),
        ).fetchone()
        if row is None:
            raise KeyError(journey_id)

    def list_for_journey(self, *, journey_id: str, owner_id: str) -> tuple[TravelLeg, ...]:
        self._require_journey(journey_id, owner_id)
        rows = self._connection.execute(
            "SELECT * FROM journey_travel_legs WHERE journey_id = ? ORDER BY departure_local",
            (journey_id,),
        ).fetchall()
        return tuple(_leg_from_row(row) for row in rows)

    def create(self, *, journey_id: str, owner_id: str, fields: TravelLegFields) -> TravelLeg:
        _validate_identifier(journey_id, "journey")
        self._require_journey(journey_id, owner_id)
        created_at = _now()
        leg = TravelLeg(
            travel_leg_id=new_travel_leg_id(),
            journey_id=journey_id,
            kind=fields.kind,
            origin=fields.origin,
            destination=fields.destination,
            departure_local=fields.departure_local,
            arrival_local=fields.arrival_local,
            cost_amount=fields.cost_amount,
            cost_currency=fields.cost_currency,
            created_at=created_at,
            updated_at=created_at,
            title=fields.title,
            description=fields.description,
            reservation_number=fields.reservation_number,
            link=fields.link,
        )
        with _transaction(self._connection):
            self._connection.execute(
                """INSERT INTO journey_travel_legs(
                       travel_leg_id, journey_id, kind, origin, destination,
                       departure_local, arrival_local, cost_amount, cost_currency,
                       created_at, updated_at, title, description, reservation_number, link
                   ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    leg.travel_leg_id,
                    leg.journey_id,
                    leg.kind,
                    leg.origin,
                    leg.destination,
                    leg.departure_local,
                    leg.arrival_local,
                    leg.cost_amount,
                    leg.cost_currency,
                    leg.created_at,
                    leg.updated_at,
                    leg.title,
                    leg.description,
                    leg.reservation_number,
                    leg.link,
                ),
            )
        return leg

    def update(
        self, *, travel_leg_id: str, journey_id: str, owner_id: str, fields: TravelLegFields
    ) -> TravelLeg:
        self._require_journey(journey_id, owner_id)
        updated_at = _now()
        with _transaction(self._connection):
            cursor = self._connection.execute(
                """UPDATE journey_travel_legs SET
                       kind = ?, origin = ?, destination = ?, departure_local = ?,
                       arrival_local = ?, cost_amount = ?, cost_currency = ?, updated_at = ?,
                       title = ?, description = ?, reservation_number = ?, link = ?
                   WHERE travel_leg_id = ? AND journey_id = ?""",
                (
                    fields.kind,
                    fields.origin,
                    fields.destination,
                    fields.departure_local,
                    fields.arrival_local,
                    fields.cost_amount,
                    fields.cost_currency,
                    updated_at,
                    fields.title,
                    fields.description,
                    fields.reservation_number,
                    fields.link,
                    travel_leg_id,
                    journey_id,
                ),
            )
            if cursor.rowcount != 1:
                raise KeyError(travel_leg_id)
        row = self._connection.execute(
            "SELECT * FROM journey_travel_legs WHERE travel_leg_id = ?", (travel_leg_id,)
        ).fetchone()
        return _leg_from_row(row)

    def delete(self, *, travel_leg_id: str, journey_id: str, owner_id: str) -> None:
        self._require_journey(journey_id, owner_id)
        with _transaction(self._connection):
            cursor = self._connection.execute(
                "DELETE FROM journey_travel_legs WHERE travel_leg_id = ? AND journey_id = ?",
                (travel_leg_id, journey_id),
            )
            if cursor.rowcount != 1:
                raise KeyError(travel_leg_id)


def travel_leg_fields_from_payload(payload: dict[str, Any]) -> TravelLegFields:
    return TravelLegFields(
        kind=payload["kind"],
        origin=payload["origin"],
        destination=payload["destination"],
        departure_local=payload["departure_local"],
        arrival_local=payload["arrival_local"],
        cost_amount=payload.get("cost_amount"),
        cost_currency=payload.get("cost_currency") or "PLN",
        title=_optional_text(payload.get("title")),
        description=_optional_text(payload.get("description")),
        reservation_number=_optional_text(payload.get("reservation_number")),
        link=_optional_text(payload.get("link")),
    )


def _optional_text(value: Any) -> str | None:
    """Blank is absent: an empty field is stored as NULL, never as ''."""
    if value is None:
        return None
    text = str(value).strip()
    return text or None
