from copy import deepcopy
from datetime import datetime, timedelta

from dataclasses import dataclass

MAX_EVENT_INCREASE = 0.15     # max +15%
SHOULDER_DAY_DECAY = 0.35     # shoulder days get 35% of event intensity


@dataclass(frozen=True)
class Event:
    name: str
    month: int
    day: int
    intensity: float          # 0.0 – 1.0 (how strongly it drives hotel demand)
    shoulder_days: int = 0    # extra days before & after with elevated demand


# ──────────────────────────────────────────────
# Fixed-date annual events near St Pete Beach
# ──────────────────────────────────────────────
EVENTS = (
    # --- Spring ---
    Event("Tampa Bay Blues Festival",      4, 11, 0.80, shoulder_days=1),
    Event("Mainsail Art Festival",         4, 19, 0.70, shoulder_days=1),

    # --- Summer ---
    Event("St Pete Pride Festival",        6, 22, 0.65, shoulder_days=1),

    # --- Fall ---
    Event("Corey Area Craft Festival",     11, 15, 0.60),
    Event("Sanding Ovations",              11, 20, 0.95, shoulder_days=2),

    # --- Winter ---
    Event("Holiday Boat Parade",           12, 13, 0.70),
    Event("First Night St Petersburg",     12, 31, 0.50),   # NYE arts event (stacks with holiday)

    # --- January / February (peak season) ---
    Event("Outback Bowl / ReliaQuest Bowl",  1,  1,  0.55),  # stacks with New Year's Day
    Event("Gasparilla Pirate Festival",      1, 25, 0.85, shoulder_days=1),
)


# ──────────────────────────────────────────────
# Build lookup  (month, day) -> [(Event, effective_intensity), …]
# ──────────────────────────────────────────────
EVENT_LOOKUP: dict[tuple[int, int], list[tuple[Event, float]]] = {}


def _add_entry(month: int, day: int, event: Event, intensity: float):
    EVENT_LOOKUP.setdefault((month, day), []).append((event, intensity))


for e in EVENTS:
    # The event day itself — full intensity
    _add_entry(e.month, e.day, e, e.intensity)

    # Shoulder days — decayed intensity
    if e.shoulder_days > 0:
        anchor = datetime(2000, e.month, e.day)
        for offset in range(1, e.shoulder_days + 1):
            decay = SHOULDER_DAY_DECAY * (1 / offset)
            for delta in (-offset, offset):
                dt = anchor + timedelta(days=delta)
                effective = round(e.intensity * decay, 4)
                if effective > 0.01:
                    _add_entry(dt.month, dt.day, e, effective)


async def apply_events(results):

    output = deepcopy(results)

    for room_id, rows in output.items():

        if not isinstance(rows, list):
            continue

        for row in rows:

            if row.get("predicted") is None:
                continue

            dt = datetime.strptime(row["date"], "%Y-%m-%d").date()

            entries = EVENT_LOOKUP.get((dt.month, dt.day))

            if entries is None:
                continue

            # Sum effective intensities, cap at 1.0
            combined_intensity = min(1.0, sum(eff for _, eff in entries))

            row["predicted"] = round(
                float(row["predicted"]) * (1 + combined_intensity * MAX_EVENT_INCREASE),
                2,
            )

            row["event"] = ", ".join(e.name for e, _ in entries)
            row["event_intensity"] = round(combined_intensity, 4)

    return output