"""Create and load the hotel DB with fake data, using SQLModel."""

import random

from faker import Faker
from sqlalchemy import CheckConstraint
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine

# echo=True prints every SQL statement that SQLModel sends to the database.
engine = create_engine("sqlite:///hotels.db", echo=True)

fake = Faker()

# Seeding makes the "random" data the same every run, which makes bugs repeatable.
Faker.seed(374)
random.seed(374)


class Hotel(SQLModel, table=True):
    # Field() cannot say CHECK, so the constraint goes in __table_args__.
    __table_args__ = (CheckConstraint("year BETWEEN 1800 AND 2026"),)

    # None until the object is committed; then the database assigns the id.
    hotel_id: int | None = Field(default=None, primary_key=True)
    name: str
    street: str
    city: str
    country: str
    year: int

    # Not a column: a list of the HotelPhone objects whose hotel_id is this one.
    phones: list["HotelPhone"] = Relationship(back_populates="hotel")


class HotelPhone(SQLModel, table=True):
    # Without this line, the table would be named "hotelphone".
    __tablename__ = "hotel_phone"

    hotel_id: int = Field(foreign_key="hotel.hotel_id", primary_key=True)
    sort: int = Field(primary_key=True)
    phone: str
    label: str | None = None

    hotel: Hotel = Relationship(back_populates="phones")


def make_hotels(count: int) -> list[Hotel]:
    """Return new Hotel objects, each with 1 to 4 phones, not yet in the database."""
    hotels = []
    for _ in range(count):
        hotel = Hotel(
            name=fake.last_name() + " " + random.choice(["Hotel", "Inn", "Suites"]),
            street=fake.street_address(),
            city=fake.city(),
            country=fake.country(),
            year=fake.random_int(1900, 2025),
        )
        # No hotel_id yet, so let the relationship fill it in at commit time.
        for sort in range(1, random.randint(1, 4) + 1):
            label = random.choice([None, "Front Desk", "Reservations", "Security"])
            hotel.phones.append(HotelPhone(sort=sort, phone=fake.phone_number(), label=label))
        hotels.append(hotel)
    return hotels


if __name__ == "__main__":
    SQLModel.metadata.drop_all(engine)
    SQLModel.metadata.create_all(engine)

    hotels = make_hotels(20)
    with Session(engine) as session:
        print("before:", hotels[0].hotel_id)
        session.add_all(hotels)
        session.commit()
        print("after:", hotels[0].hotel_id)
        print(hotels[0].phones)
