"""UI-independent Journey container commands."""

from __future__ import annotations

from dataclasses import dataclass, replace

from .contracts import Journey, JourneyActivityIntent, JourneyDateIntent
from .persistence import JourneySqliteRepository, _now, new_journey_id


@dataclass(frozen=True)
class CreateJourney:
    owner_id: str
    title: str
    destination_label: str | None = None
    country_label: str | None = None
    date_intent: JourneyDateIntent = JourneyDateIntent.unspecified()
    activity_intent: JourneyActivityIntent | None = None
    creation_request_id: str | None = None

    def __post_init__(self) -> None:
        _validate_metadata(self.owner_id, self.title, self.destination_label, self.country_label)
        _validate_intents(self.date_intent, self.activity_intent)


@dataclass(frozen=True)
class UpdateJourneyMetadata:
    owner_id: str
    journey_id: str
    title: str
    destination_label: str | None = None
    country_label: str | None = None
    date_intent: JourneyDateIntent = JourneyDateIntent.unspecified()
    activity_intent: JourneyActivityIntent | None = None

    def __post_init__(self) -> None:
        _validate_metadata(self.owner_id, self.title, self.destination_label, self.country_label)
        _validate_intents(self.date_intent, self.activity_intent)
        if not self.journey_id or self.journey_id != self.journey_id.strip():
            raise ValueError("journey identifier is malformed")


@dataclass(frozen=True)
class CreatedJourney:
    journey: Journey
    created: bool


class JourneyCreatorService:
    """Create and edit durable containers without introducing route/plan records."""

    def __init__(self, repository: JourneySqliteRepository):
        self._repository = repository

    def create_journey(self, command: CreateJourney) -> CreatedJourney:
        if command.creation_request_id is not None:
            existing = self._repository.get_by_creation_request(
                owner_id=command.owner_id, creation_request_id=command.creation_request_id
            )
            if existing is not None:
                if _creation_semantics(existing) != _creation_semantics_from_command(command):
                    raise ValueError(
                        "creation request ID conflicts with existing Journey semantics"
                    )
                return CreatedJourney(existing, created=False)
        now = _now()
        journey = Journey(
            journey_id=new_journey_id(),
            title=command.title,
            owner_id=command.owner_id,
            destination_label=command.destination_label,
            country_label=command.country_label,
            date_intent=command.date_intent,
            activity_intent=command.activity_intent,
            created_at=now,
            updated_at=now,
        )
        self._repository.create(journey, creation_request_id=command.creation_request_id)
        return CreatedJourney(journey, created=True)

    def update_metadata(self, command: UpdateJourneyMetadata) -> Journey:
        current = self._repository.get(owner_id=command.owner_id, journey_id=command.journey_id)
        updated = replace(
            current,
            title=command.title,
            destination_label=command.destination_label,
            country_label=command.country_label,
            date_intent=command.date_intent,
            activity_intent=command.activity_intent,
            updated_at=_now(),
        )
        return self._repository.update_metadata(updated)

    def get_journey(self, *, owner_id: str, journey_id: str) -> Journey:
        return self._repository.get(owner_id=owner_id, journey_id=journey_id)

    def list_journeys(self, *, owner_id: str) -> tuple[Journey, ...]:
        return self._repository.list(owner_id=owner_id)


def _validate_metadata(
    owner_id: str, title: str, destination_label: str | None, country_label: str | None
) -> None:
    for label, value in (("owner", owner_id), ("title", title)):
        if not value or value != value.strip() or len(value) > 256:
            raise ValueError(f"Journey {label} is invalid")
    for label, value in (("destination", destination_label), ("country", country_label)):
        if value is not None and (not value or value != value.strip() or len(value) > 256):
            raise ValueError(f"Journey {label} is invalid")


def _validate_intents(
    date_intent: JourneyDateIntent, activity_intent: JourneyActivityIntent | None
) -> None:
    if not isinstance(date_intent, JourneyDateIntent):
        raise ValueError("Journey date intent is invalid")
    if activity_intent is not None and not isinstance(activity_intent, JourneyActivityIntent):
        raise ValueError("Journey activity intent is unsupported")


def _creation_semantics(journey: Journey) -> tuple[object, ...]:
    return (
        journey.title,
        journey.destination_label,
        journey.country_label,
        journey.date_intent,
        journey.activity_intent,
    )


def _creation_semantics_from_command(command: CreateJourney) -> tuple[object, ...]:
    return (
        command.title,
        command.destination_label,
        command.country_label,
        command.date_intent,
        command.activity_intent,
    )
