from datetime import date, time

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models.customer import Customer
from app.models.reservation import Reservation, ReservationSource, ReservationStatus, ReservationStatusHistory
from app.models.room import Room
from app.models.table import Table, TableCombination
from app.models.venue import Venue
from app.services.assignment import recalculate_day_assignments
from app.services.booking_settings_service import ensure_booking_settings

DEFAULT_VENUE_NAME = "BACO' LOUNGE BAR"
DEFAULT_ROOM_NAME = "Sala Interna"
DEFAULT_ROOM_WIDTH = 1400
DEFAULT_ROOM_HEIGHT = 980
DEFAULT_COUNTER_NAME = "Banco"
DEFAULT_COUNTER_WIDTH = 180
DEFAULT_COUNTER_HEIGHT = 280
DEFAULT_COUNTER_MARGIN_RIGHT = 90
DEFAULT_COUNTER_MARGIN_TOP = 220
DEFAULT_ENTRANCE_NAME = "Entrata"
DEFAULT_ENTRANCE_WIDTH = 260
DEFAULT_ENTRANCE_HEIGHT = 180
DEFAULT_ENTRANCE_MARGIN_BOTTOM = 60

DEMO_TABLES = [
    {"name": "1", "x": 890, "y": 780, "width": 120, "height": 90, "min_seats": 6, "max_seats": 8, "join_group": None},
    {"name": "2", "x": 340, "y": 560, "width": 96, "height": 64, "min_seats": 2, "max_seats": 6, "join_group": None},
    {"name": "3", "x": 70, "y": 540, "width": 110, "height": 110, "min_seats": 5, "max_seats": 8, "join_group": None},
    {"name": "4", "x": 170, "y": 430, "width": 78, "height": 58, "min_seats": 1, "max_seats": 3, "join_group": None},
    {"name": "5", "x": 50, "y": 85, "width": 74, "height": 160, "min_seats": 6, "max_seats": 8, "join_group": None},
    {"name": "6", "x": 225, "y": 80, "width": 82, "height": 56, "min_seats": 2, "max_seats": 4, "join_group": "north_small"},
    {"name": "7", "x": 430, "y": 80, "width": 82, "height": 56, "min_seats": 2, "max_seats": 4, "join_group": "north_small"},
    {"name": "8", "x": 635, "y": 75, "width": 98, "height": 56, "min_seats": 4, "max_seats": 6, "join_group": "north_large"},
    {"name": "9", "x": 890, "y": 75, "width": 132, "height": 58, "min_seats": 6, "max_seats": 8, "join_group": "north_large"},
    {"name": "10", "x": 1200, "y": 85, "width": 112, "height": 112, "min_seats": 5, "max_seats": 8, "join_group": None},
    {"name": "11", "x": 890, "y": 455, "width": 74, "height": 145, "min_seats": 6, "max_seats": 8, "join_group": "east_vertical"},
    {"name": "12", "x": 890, "y": 235, "width": 74, "height": 120, "min_seats": 4, "max_seats": 6, "join_group": "east_vertical"},
    {"name": "13", "x": 618, "y": 230, "width": 100, "height": 58, "min_seats": 4, "max_seats": 6, "join_group": "center_row"},
    {"name": "14", "x": 430, "y": 228, "width": 82, "height": 56, "min_seats": 2, "max_seats": 4, "join_group": "center_row"},
    {"name": "15", "x": 225, "y": 228, "width": 82, "height": 56, "min_seats": 2, "max_seats": 4, "join_group": "center_row"},
]

DEMO_COMBINATIONS = [
    {"name": "6 + 7", "table_names": ["6", "7"], "min_seats": 4, "max_seats": 8},
    {"name": "7 + 8", "table_names": ["7", "8"], "min_seats": 6, "max_seats": 10},
    {"name": "8 + 9", "table_names": ["8", "9"], "min_seats": 10, "max_seats": 14},
    {"name": "15 + 14", "table_names": ["15", "14"], "min_seats": 4, "max_seats": 8},
    {"name": "14 + 13", "table_names": ["14", "13"], "min_seats": 6, "max_seats": 10},
    {"name": "8 + 13", "table_names": ["8", "13"], "min_seats": 8, "max_seats": 12},
    {"name": "12 + 11", "table_names": ["12", "11"], "min_seats": 10, "max_seats": 14},
]

def _normalize_text(value: str | None) -> str:
    return (value or "").strip().lower()


def _is_ritual_venue(venue: Venue) -> bool:
    slug = _normalize_text(venue.portal_tenant_slug)
    name = _normalize_text(venue.name)
    return "ritual" in slug or "ritual" in name


def _is_baco_venue(venue: Venue) -> bool:
    slug = _normalize_text(venue.portal_tenant_slug)
    name = _normalize_text(venue.name)
    return "baco" in slug or "baco" in name


def _clear_room_layout(room_id: int, db: Session) -> None:
    combinations = list(db.scalars(select(TableCombination).where(TableCombination.room_id == room_id)))
    for combination in combinations:
        db.delete(combination)

    tables = list(db.scalars(select(Table).where(Table.room_id == room_id)))
    for table in tables:
        db.delete(table)
    db.flush()


def apply_default_counter_layout(room: Room, *, force: bool = False) -> bool:
    updated = False

    width = room.counter_width if room.counter_width is not None and not force else min(DEFAULT_COUNTER_WIDTH, room.width)
    height = room.counter_height if room.counter_height is not None and not force else min(DEFAULT_COUNTER_HEIGHT, room.height)
    width = max(80, width)
    height = max(80, height)
    default_x = max(room.width - width - DEFAULT_COUNTER_MARGIN_RIGHT, 0)
    default_y = min(DEFAULT_COUNTER_MARGIN_TOP, max(room.height - height, 0))

    if force or not room.counter_name:
        room.counter_name = DEFAULT_COUNTER_NAME
        updated = True
    if force or room.counter_width is None:
        room.counter_width = width
        updated = True
    if force or room.counter_height is None:
        room.counter_height = height
        updated = True
    if force or room.counter_x is None:
        room.counter_x = default_x
        updated = True
    if force or room.counter_y is None:
        room.counter_y = default_y
        updated = True
    if force or room.counter_visible is None:
        room.counter_visible = True
        updated = True

    return updated


def apply_default_entrance_layout(room: Room, *, force: bool = False) -> bool:
    updated = False

    width = room.entrance_width if room.entrance_width is not None and not force else min(DEFAULT_ENTRANCE_WIDTH, room.width)
    height = room.entrance_height if room.entrance_height is not None and not force else min(DEFAULT_ENTRANCE_HEIGHT, room.height)
    width = max(80, width)
    height = max(80, height)
    default_x = max((room.width - width) // 2, 0)
    default_y = max(room.height - height - DEFAULT_ENTRANCE_MARGIN_BOTTOM, 0)

    if force or not room.entrance_name:
        room.entrance_name = DEFAULT_ENTRANCE_NAME
        updated = True
    if force or room.entrance_width is None:
        room.entrance_width = width
        updated = True
    if force or room.entrance_height is None:
        room.entrance_height = height
        updated = True
    if force or room.entrance_x is None:
        room.entrance_x = default_x
        updated = True
    if force or room.entrance_y is None:
        room.entrance_y = default_y
        updated = True
    if force or room.entrance_visible is None:
        room.entrance_visible = True
        updated = True

    return updated


def reset_room_layout(room: Room, db: Session) -> None:
    _clear_room_layout(room.id, db)
    apply_default_counter_layout(room, force=True)
    apply_default_entrance_layout(room, force=True)
    room.background_image_data_url = None
    db.flush()


def _ensure_demo_layout_for_room(room_id: int, db: Session) -> tuple[list[Table], list[TableCombination]]:
    existing_tables = list(db.scalars(select(Table).where(Table.room_id == room_id).order_by(Table.name)))
    if not existing_tables:
        for table_data in DEMO_TABLES:
            db.add(Table(room_id=room_id, is_active=True, **table_data))
        db.flush()
        existing_tables = list(db.scalars(select(Table).where(Table.room_id == room_id).order_by(Table.name)))

    existing_combinations = list(
        db.scalars(select(TableCombination).where(TableCombination.room_id == room_id).order_by(TableCombination.name))
    )
    if not existing_combinations:
        table_ids_by_name = {table.name: table.id for table in existing_tables}
        for combination_data in DEMO_COMBINATIONS:
            db.add(
                TableCombination(
                    room_id=room_id,
                    name=combination_data["name"],
                    table_ids=[table_ids_by_name[name] for name in combination_data["table_names"]],
                    min_seats=combination_data["min_seats"],
                    max_seats=combination_data["max_seats"],
                    is_active=True,
                )
            )
        db.flush()
        existing_combinations = list(
            db.scalars(select(TableCombination).where(TableCombination.room_id == room_id).order_by(TableCombination.name))
        )

    return existing_tables, existing_combinations


def enforce_layout_policy_for_venue(venue: Venue, room: Room, db: Session) -> tuple[list[Table], list[TableCombination]]:
    existing_tables = list(db.scalars(select(Table).where(Table.room_id == room.id).order_by(Table.name)))
    existing_combinations = list(
        db.scalars(select(TableCombination).where(TableCombination.room_id == room.id).order_by(TableCombination.name))
    )

    if venue.layout_policy_applied:
        return existing_tables, existing_combinations

    if _is_ritual_venue(venue):
        existing_tables, existing_combinations = _ensure_demo_layout_for_room(room.id, db)
    elif _is_baco_venue(venue):
        if existing_tables or existing_combinations:
            _clear_room_layout(room.id, db)
            existing_tables = []
            existing_combinations = []

    venue.layout_policy_applied = True
    db.flush()
    return existing_tables, existing_combinations


def ensure_room_for_venue(
    venue_id: int,
    db: Session,
    *,
    room_name: str = DEFAULT_ROOM_NAME,
) -> Room:
    room = db.scalar(select(Room).where(Room.venue_id == venue_id).order_by(Room.id))
    if room is not None:
        apply_default_counter_layout(room)
        apply_default_entrance_layout(room)
        db.flush()
        return room

    room = Room(
        venue_id=venue_id,
        name=room_name,
        width=DEFAULT_ROOM_WIDTH,
        height=DEFAULT_ROOM_HEIGHT,
        counter_visible=True,
    )
    db.add(room)
    db.flush()
    apply_default_counter_layout(room, force=True)
    apply_default_entrance_layout(room, force=True)
    db.flush()
    return room


def ensure_venue_setup(
    db: Session,
    *,
    venue_name: str,
    portal_tenant_id: str | None = None,
    portal_tenant_slug: str | None = None,
) -> tuple[Venue, Room, list[Table], list[TableCombination]]:
    venue = None
    if portal_tenant_id:
        venue = db.scalar(select(Venue).where(Venue.portal_tenant_id == portal_tenant_id))

    if venue is None:
        venue = db.scalar(select(Venue).where(Venue.name == venue_name).order_by(Venue.id))
        if venue is not None and portal_tenant_id and venue.portal_tenant_id not in (None, portal_tenant_id):
            venue = None

    legacy_venue = db.scalar(select(Venue).where(Venue.name == "VIP CLUB"))
    if venue is None and venue_name == DEFAULT_VENUE_NAME and legacy_venue is not None:
        legacy_venue.name = DEFAULT_VENUE_NAME
        venue = legacy_venue

    if venue is None:
        venue = Venue(name=venue_name, portal_tenant_id=portal_tenant_id, portal_tenant_slug=portal_tenant_slug)
        db.add(venue)
        db.flush()
    else:
        updates_applied = False
        if venue.name != venue_name:
            venue.name = venue_name
            updates_applied = True
        if portal_tenant_id and venue.portal_tenant_id != portal_tenant_id:
            venue.portal_tenant_id = portal_tenant_id
            updates_applied = True
        if portal_tenant_slug and venue.portal_tenant_slug != portal_tenant_slug:
            venue.portal_tenant_slug = portal_tenant_slug
            updates_applied = True
        if updates_applied:
            db.flush()

    room = ensure_room_for_venue(venue.id, db)

    existing_tables, existing_combinations = enforce_layout_policy_for_venue(venue, room, db)

    ensure_booking_settings(venue.id, db)

    db.commit()
    db.refresh(venue)
    db.refresh(room)
    return venue, room, existing_tables, existing_combinations


def ensure_default_venue_setup(db: Session) -> tuple[Venue, Room, list[Table], list[TableCombination]]:
    return ensure_venue_setup(db, venue_name=DEFAULT_VENUE_NAME)


def seed_demo_data(db: Session) -> dict:
    venue, room, tables, combinations = ensure_default_venue_setup(db)

    if not tables or not combinations:
        tables, combinations = _ensure_demo_layout_for_room(room.id, db)
        db.commit()

    existing_reservations = db.query(Reservation).filter(Reservation.venue_id == venue.id).count()
    if existing_reservations > 0:
        return {
            "message": "Dati demo già presenti",
            "venue_id": venue.id,
            "room_id": room.id,
            "customers": db.query(Customer).count(),
            "tables": len(tables),
            "combinations": len(combinations),
            "reservations": existing_reservations,
        }

    customers = [
        Customer(name="Giulia Rossi", phone="+39 333 100 1001", email="giulia.rossi@example.com", notes="Ama il tavolo vicino alla finestra"),
        Customer(name="Marco Bianchi", phone="+39 333 100 1002", email="marco.bianchi@example.com"),
        Customer(name="Elena Conti", phone="+39 333 100 1003"),
        Customer(name="Luca Moretti", phone="+39 333 100 1004"),
        Customer(name="Sofia Gallo", phone="+39 333 100 1005"),
        Customer(name="Davide Ferri", phone="+39 333 100 1006"),
        Customer(name="Marta Serra", phone="+39 333 100 1007"),
        Customer(name="Andrea Villa", phone="+39 333 100 1008"),
    ]
    db.add_all(customers)
    db.flush()

    today = date.today()
    demo_reservations = [
        Reservation(venue_id=venue.id, customer_id=customers[0].id, reservation_date=today, start_time=time(19, 0), duration_minutes=120, guests=2, status=ReservationStatus.confirmed, source=ReservationSource.manual, notes="Anniversario"),
        Reservation(venue_id=venue.id, customer_id=customers[1].id, reservation_date=today, start_time=time(19, 15), duration_minutes=90, guests=4, status=ReservationStatus.confirmed, source=ReservationSource.web),
        Reservation(venue_id=venue.id, customer_id=customers[2].id, reservation_date=today, start_time=time(19, 30), duration_minutes=120, guests=6, status=ReservationStatus.pending, source=ReservationSource.whatsapp),
        Reservation(venue_id=venue.id, customer_id=customers[3].id, reservation_date=today, start_time=time(20, 0), duration_minutes=120, guests=2, status=ReservationStatus.confirmed, source=ReservationSource.manual),
        Reservation(venue_id=venue.id, customer_id=customers[4].id, reservation_date=today, start_time=time(20, 0), duration_minutes=100, guests=8, status=ReservationStatus.confirmed, source=ReservationSource.web),
        Reservation(venue_id=venue.id, customer_id=customers[5].id, reservation_date=today, start_time=time(20, 15), duration_minutes=90, guests=3, status=ReservationStatus.pending, source=ReservationSource.whatsapp),
        Reservation(venue_id=venue.id, customer_id=customers[6].id, reservation_date=today, start_time=time(20, 30), duration_minutes=90, guests=5, status=ReservationStatus.confirmed, source=ReservationSource.manual),
        Reservation(venue_id=venue.id, customer_id=customers[7].id, reservation_date=today, start_time=time(21, 0), duration_minutes=120, guests=10, status=ReservationStatus.confirmed, source=ReservationSource.web),
        Reservation(venue_id=venue.id, customer_id=customers[1].id, reservation_date=today, start_time=time(21, 15), duration_minutes=60, guests=2, status=ReservationStatus.pending, source=ReservationSource.manual),
        Reservation(venue_id=venue.id, customer_id=customers[4].id, reservation_date=today, start_time=time(22, 0), duration_minutes=90, guests=4, status=ReservationStatus.confirmed, source=ReservationSource.web),
    ]
    db.add_all(demo_reservations)
    db.flush()

    db.add_all(
        ReservationStatusHistory(reservation_id=reservation.id, old_status=None, new_status=reservation.status)
        for reservation in demo_reservations
    )
    db.commit()

    recalculate_day_assignments(today, venue.id, db)

    return {
        "message": "Prenotazioni demo create sul locale BACO' LOUNGE BAR",
        "venue_id": venue.id,
        "room_id": room.id,
        "customers": len(customers),
        "tables": len(tables),
        "combinations": len(combinations),
        "reservations": len(demo_reservations),
    }
