from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any


class SeatType(str, Enum):
    GENERAL_FIRST = "general_first"
    GENERAL_ONLY = "general_only"
    SPECIAL_FIRST = "special_first"
    SPECIAL_ONLY = "special_only"


@dataclass
class Passenger:
    type: str
    count: int = 1

    @staticmethod
    def adult(count=1):
        return Passenger("adult", count)

    @staticmethod
    def child(count=1):
        return Passenger("child", count)

    @staticmethod
    def senior(count=1):
        return Passenger("senior", count)

    @staticmethod
    def toddler(count=1):
        return Passenger("toddler", count)

    def __repr__(self):
        names = {"adult": "어른", "child": "어린이", "senior": "경로", "toddler": "유아"}
        return f"{names.get(self.type, self.type)} {self.count}명"


@dataclass
class Train:
    train_name: str
    train_number: str
    dep_station: str
    arr_station: str
    dep_date: str
    dep_time: str
    arr_date: str
    arr_time: str
    general_available: bool
    special_available: bool
    _raw: Any = field(repr=False, default=None)

    def __str__(self):
        dep = f"{self.dep_time[:2]}:{self.dep_time[2:4]}"
        arr = f"{self.arr_time[:2]}:{self.arr_time[2:4]}"
        month = self.dep_date[4:6]
        day = self.dep_date[6:8]
        special = "가능" if self.special_available else "매진"
        general = "가능" if self.general_available else "매진"
        return (
            f"[{self.train_name} {self.train_number}] "
            f"{month}월 {day}일, {self.dep_station}~{self.arr_station}"
            f"({dep}~{arr}) "
            f"특실 {special}, 일반실 {general}"
        )


@dataclass
class Reservation:
    id: str
    total_cost: str
    seat_count: str
    payment_date: str
    payment_time: str
    paid: bool
    train_name: str
    train_number: str
    dep_station: str
    arr_station: str
    dep_date: str
    dep_time: str
    arr_time: str
    _raw: Any = field(repr=False, default=None)

    def __str__(self):
        dep = f"{self.dep_time[:2]}:{self.dep_time[2:4]}"
        arr = f"{self.arr_time[:2]}:{self.arr_time[2:4]}"
        month = self.dep_date[4:6]
        day = self.dep_date[6:8]
        s = (
            f"[{self.train_name}] "
            f"{month}월 {day}일, {self.dep_station}~{self.arr_station}"
            f"({dep}~{arr}) "
            f"{self.total_cost}원({self.seat_count}석)"
        )
        if not self.paid:
            pm = self.payment_date[4:6]
            pd = self.payment_date[6:8]
            pt = f"{self.payment_time[:2]}:{self.payment_time[2:4]}"
            s += f", 구입기한 {pm}월 {pd}일 {pt}"
        return s


class TrainService(ABC):
    """Abstract base for train reservation services."""

    @abstractmethod
    def search(
        self, dep, arr, date=None, time=None, *, time_limit=None, passengers=None
    ) -> list[Train]:
        ...

    @abstractmethod
    def reserve(
        self, train, *, passengers=None, seat_type=SeatType.GENERAL_FIRST
    ) -> Reservation:
        ...

    @abstractmethod
    def reservations(self) -> list[Reservation]:
        ...

    @abstractmethod
    def cancel(self, reservation) -> bool:
        ...
