from copy import deepcopy
from datetime import datetime, timedelta

from dataclasses import dataclass

MAX_HOLIDAY_INCREASE = 0.20   # max +20%
SHOULDER_DAY_DECAY   = 0.40   # shoulder days get 40% of the holiday intensity


@dataclass(frozen=True)
class Holiday:
    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 that still see elevated demand


# ──────────────────────────────────────────────
# Fixed-date US holidays relevant to St Pete Beach tourism
# ──────────────────────────────────────────────
HOLIDAYS = (
    # --- Major fixed-date federal holidays ---
    Holiday("New Year's Day",       1,  1,  1.00, shoulder_days=1),
    Holiday("Juneteenth",           6,  19, 0.40),
    Holiday("Independence Day",     7,  4,  1.00, shoulder_days=2),
    Holiday("Veterans Day",         11, 11, 0.35),
    Holiday("Christmas Eve",        12, 24, 0.85),
    Holiday("Christmas Day",        12, 25, 0.90, shoulder_days=1),
    Holiday("New Year's Eve",       12, 31, 1.00),

    # --- Cultural / commercial holidays (fixed date) ---
    Holiday("Valentine's Day",      2,  14, 0.55, shoulder_days=1),
    Holiday("St. Patrick's Day",    3,  17, 0.45),
    Holiday("Cinco de Mayo",        5,  5,  0.35),
    Holiday("Halloween",            10, 31, 0.40),

    # --- Regional (Pinellas County / Tampa Bay) ---
    Holiday("Epiphany – Tarpon Springs", 1, 6, 0.30),
)

# ──────────────────────────────────────────────
# Build lookup  (month, day) -> [Holiday, …]
# Includes shoulder-day entries with decayed intensity
# ──────────────────────────────────────────────
HOLIDAY_LOOKUP: dict[tuple[int, int], list[tuple[Holiday, float]]] = {}


def _add_entry(month: int, day: int, holiday: Holiday, intensity: float):
    HOLIDAY_LOOKUP.setdefault((month, day), []).append((holiday, intensity))


for h in HOLIDAYS:
    # The holiday itself — full intensity
    _add_entry(h.month, h.day, h, h.intensity)

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


async def apply_holidays(results: dict):

    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 = HOLIDAY_LOOKUP.get((dt.month, dt.day))

            if entries is None:
                continue

            predicted = float(row["predicted"])

            # Sum effective intensities, cap at 1.0 so total increase never exceeds MAX_HOLIDAY_INCREASE
            combined_intensity = min(1.0, sum(eff for _, eff in entries))
            predicted *= (1 + combined_intensity * MAX_HOLIDAY_INCREASE)

            row["predicted"] = round(predicted, 2)
            row["holiday"] = ", ".join(h.name for h, _ in entries)
            row["holiday_intensity"] = round(combined_intensity, 4)

    return output