from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from models.calendars import CalendarEntry



@dataclass
class VisibleEntries:
    """
    Result of applying calendar visibility rules for a given period.
    """
    user_entries: list[CalendarEntry]
    confirmed_entries: list[CalendarEntry]
    admin_other_entries: list[CalendarEntry]
    merged_entries: list[CalendarEntry]  # sorted, deduped


async def get_visible_entries_for_period(
    sess: AsyncSession,
    calendar_id: int,
    period_start: datetime,
    period_end: datetime,
    user: Optional[object],
    session_id: Optional[str],
) -> VisibleEntries:
    """
    Visibility rules (same as your fixed month view):

    - Non-admin:
        - sees all *confirmed* entries in the period (any user)
        - sees all entries for current user/session in the period (any state)
    - Admin:
        - sees all confirmed + provisional + ordered entries in the period (all users)
        - sees pending only for current user/session
    """

    user_id = user.id if user else None
    is_admin = bool(user and getattr(user, "is_admin", False))

    # --- Entries for current user/session (any state, in period) ---
    user_entries: list[CalendarEntry] = []
    if user_id or session_id:
        conditions_user = [
            CalendarEntry.calendar_id == calendar_id,
            CalendarEntry.deleted_at.is_(None),
            CalendarEntry.start_at >= period_start,
            CalendarEntry.start_at < period_end,
        ]
        if user_id:
            conditions_user.append(CalendarEntry.user_id == user_id)
        elif session_id:
            conditions_user.append(CalendarEntry.session_id == session_id)

        result_user = await sess.execute(select(CalendarEntry).where(*conditions_user))
        user_entries = result_user.scalars().all()

    # --- Confirmed entries for everyone in period ---
    result_conf = await sess.execute(
        select(CalendarEntry).where(
            CalendarEntry.calendar_id == calendar_id,
            CalendarEntry.state == "confirmed",
            CalendarEntry.deleted_at.is_(None),
            CalendarEntry.start_at >= period_start,
            CalendarEntry.start_at < period_end,
        )
    )
    confirmed_entries = result_conf.scalars().all()

    # --- For admins: ordered + provisional for everyone in period ---
    admin_other_entries: list[CalendarEntry] = []
    if is_admin:
        result_admin = await sess.execute(
            select(CalendarEntry).where(
                CalendarEntry.calendar_id == calendar_id,
                CalendarEntry.state.in_(("ordered", "provisional")),
                CalendarEntry.deleted_at.is_(None),
                CalendarEntry.start_at >= period_start,
                CalendarEntry.start_at < period_end,
            )
        )
        admin_other_entries = result_admin.scalars().all()

    # --- Merge with de-duplication and keep chronological order ---
    entries_by_id: dict[int, CalendarEntry] = {}

    # Everyone's confirmed
    for e in confirmed_entries:
        entries_by_id[e.id] = e

    # Admin-only: everyone's ordered/provisional
    if is_admin:
        for e in admin_other_entries:
            entries_by_id[e.id] = e

    # Always include current user/session entries (includes their pending)
    for e in user_entries:
        entries_by_id[e.id] = e

    merged_entries = sorted(
        entries_by_id.values(),
        key=lambda e: e.start_at or period_start,
    )

    return VisibleEntries(
        user_entries=user_entries,
        confirmed_entries=confirmed_entries,
        admin_other_entries=admin_other_entries,
        merged_entries=merged_entries,
    )
