<?php

require_once __DIR__ . '/chore_helpers.php';
require_once __DIR__ . '/meal_helpers.php';
require_once __DIR__ . '/grocery_helpers.php';
require_once __DIR__ . '/expense_helpers.php';
require_once __DIR__ . '/family_settings.php';

/**
 * @return array{0: string, 1: DateTimeZone}
 */
function familyHubCalendarContext(array $familySettings): array {
    $allowed = familyHubUsTimezoneIdentifiers();
    $tzId = normalizeTimezoneToUs((string) ($familySettings['timezone'] ?? ''), $allowed);
    try {
        $tz = new DateTimeZone($tzId);
    } catch (Exception $e) {
        $tz = new DateTimeZone('America/New_York');
        $tzId = 'America/New_York';
    }
    return [$tzId, $tz];
}

function familyHubTodayYmdInTz(DateTimeZone $tzLocal): string {
    return (new DateTimeImmutable('now', $tzLocal))->format('Y-m-d');
}

/**
 * @param array<int, array{dayOfMonth: int, title: string}> $billDayDefs
 * @return list<string> Y-m-d dates in range
 */
function calendarBillReminderDatesInRange(
    string $rangeStart,
    string $rangeEnd,
    array $billDayDefs,
    DateTimeZone $tzLocal
): array {
    if ($billDayDefs === [] || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $rangeStart) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $rangeEnd)) {
        return [];
    }
    try {
        $start = new DateTimeImmutable($rangeStart . ' 00:00:00', $tzLocal);
        $end = new DateTimeImmutable($rangeEnd . ' 23:59:59', $tzLocal);
    } catch (Exception $e) {
        return [];
    }
    if ($start > $end) {
        return [];
    }
    $out = [];
    $cur = $start->modify('first day of this month')->setTime(0, 0);
    $lastMonth = $end->modify('first day of this month')->setTime(0, 0);
    while ($cur <= $lastMonth) {
        $y = (int) $cur->format('Y');
        $m = (int) $cur->format('n');
        $daysInMonth = (int) $cur->format('t');
        foreach ($billDayDefs as $bd) {
            $dom = (int) $bd['dayOfMonth'];
            if ($dom < 1) {
                continue;
            }
            $d = min($dom, $daysInMonth);
            $ymd = sprintf('%04d-%02d-%02d', $y, $m, $d);
            try {
                $dt = new DateTimeImmutable($ymd . ' 00:00:00', $tzLocal);
            } catch (Exception $e) {
                continue;
            }
            if ($dt >= $start && $dt <= $end) {
                $out[] = $ymd;
            }
        }
        $cur = $cur->modify('first day of next month');
    }
    return $out;
}

/**
 * Calendar date for month/day in a given year (Feb 29 → Feb 28 on non-leap years).
 */
function hubCalendarBirthdayInYear(int $year, int $month, int $day, DateTimeZone $tzLocal): ?DateTimeImmutable {
    if ($month < 1 || $month > 12 || $day < 1 || $day > 31) {
        return null;
    }
    if (!checkdate($month, $day, $year)) {
        if ($month === 2 && $day === 29 && checkdate(2, 28, $year)) {
            try {
                return new DateTimeImmutable(sprintf('%04d-02-28', $year), $tzLocal);
            } catch (Exception $e) {
                return null;
            }
        }
        return null;
    }
    try {
        return new DateTimeImmutable(sprintf('%04d-%02d-%02d', $year, $month, $day), $tzLocal);
    } catch (Exception $e) {
        return null;
    }
}

/**
 * @return int negative if $a sorts before $b
 */
function hubCalendarEventSortCompare(array $a, array $b): int {
    $da = (string) ($a['date'] ?? '');
    $db = (string) ($b['date'] ?? '');
    if ($da !== $db) {
        return $da <=> $db;
    }
    $rank = ['chore' => 0, 'meal' => 1, 'grocery_due' => 2, 'expense' => 3, 'bill_day' => 4, 'birthday_reminder' => 5];
    $ta = $rank[(string) ($a['type'] ?? '')] ?? 99;
    $tb = $rank[(string) ($b['type'] ?? '')] ?? 99;
    if ($ta !== $tb) {
        return $ta <=> $tb;
    }
    return strcmp((string) ($a['title'] ?? ''), (string) ($b['title'] ?? ''));
}

/**
 * @param array<int, array<string, mixed>> $people
 * @param array<string, mixed> $familySettings
 * @return list<array{type: string, date: string, title: string, detail: string, hrefQuery: string}>
 */
function hubCalendarAgendaEvents(
    string $rangeStart,
    string $rangeEnd,
    array $people,
    array $familySettings
): array {
    [$tzId, $tzLocal] = familyHubCalendarContext($familySettings);
    unset($tzId);

    if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $rangeStart) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $rangeEnd)) {
        return [];
    }

    $events = [];

    $nameById = [];
    foreach ($people as $p) {
        if (!empty($p['id'])) {
            $nameById[(string) $p['id']] = (string) ($p['name'] ?? '');
        }
    }

    $sym = trim((string) ($familySettings['currency_symbol'] ?? ''));

    $rawChores = readJsonFile('chores.json');
    $chores = migrateAllChores(normalizeChoresList($rawChores), $people);
    foreach ($chores as $c) {
        if (($c['status'] ?? '') !== 'active') {
            continue;
        }
        $due = trim((string) ($c['due_date'] ?? ''));
        if ($due === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $due)) {
            continue;
        }
        $inForwardWindow = ($due >= $rangeStart && $due <= $rangeEnd);
        $overdue = ($due < $rangeStart);
        if (!$inForwardWindow && !$overdue) {
            continue;
        }
        $title = trim((string) ($c['title'] ?? ''));
        if ($title === '') {
            $title = 'Chore';
        }
        $assignees = [];
        foreach ($c['assignee_ids'] ?? [] as $aid) {
            $aid = (string) $aid;
            if ($aid !== '' && isset($nameById[$aid])) {
                $assignees[] = $nameById[$aid];
            }
        }
        $detail = $assignees !== [] ? implode(', ', $assignees) : '';
        if ($overdue) {
            $detail = trim('Overdue · ' . $detail, ' ·');
        }
        $cid = (string) ($c['id'] ?? '');
        $events[] = [
            'type' => 'chore',
            'date' => $due,
            'title' => $title,
            'detail' => $detail,
            'hrefQuery' => 'tab=chores' . ($cid !== '' ? '&edit=' . rawurlencode($cid) : ''),
        ];
    }

    $meals = migrateLegacyMealsList(normalizeMealsList(readJsonFile('meals.json')));
    $plan = loadMealPlan();
    $weekStart = (string) ($plan['weekStart'] ?? '');
    if ($weekStart !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $weekStart)) {
        foreach (mealSlotTypes() as $slot) {
            $slotLabel = ucfirst($slot);
            for ($i = 0; $i < 7; $i++) {
                $key = (string) $i;
                $mid = $plan['slots'][$key][$slot] ?? null;
                if ($mid === null || $mid === '') {
                    continue;
                }
                $mid = (string) $mid;
                $meal = findMealById($meals, $mid);
                $mTitle = $meal !== null ? trim((string) ($meal['title'] ?? '')) : '';
                if ($mTitle === '') {
                    $mTitle = 'Meal';
                }
                try {
                    $d = (new DateTimeImmutable($weekStart . ' 00:00:00', $tzLocal))->modify('+' . $i . ' days')->format('Y-m-d');
                } catch (Exception $e) {
                    continue;
                }
                if ($d < $rangeStart || $d > $rangeEnd) {
                    continue;
                }
                $events[] = [
                    'type' => 'meal',
                    'date' => $d,
                    'title' => $mTitle,
                    'detail' => $slotLabel,
                    'hrefQuery' => 'tab=meals',
                ];
            }
        }
    }

    $catalog = normalizeCatalogList(readJsonFile('grocery_catalog.json'));
    foreach ($catalog as $row) {
        $nd = isset($row['nextDueDate']) ? trim((string) $row['nextDueDate']) : '';
        if ($nd === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $nd)) {
            continue;
        }
        if ($nd < $rangeStart || $nd > $rangeEnd) {
            continue;
        }
        $n = trim((string) ($row['name'] ?? ''));
        if ($n === '') {
            $n = 'Grocery item';
        }
        $events[] = [
            'type' => 'grocery_due',
            'date' => $nd,
            'title' => $n,
            'detail' => 'Recurring catalog',
            'hrefQuery' => 'tab=groceries',
        ];
    }

    $expenses = normalizeExpensesList(readJsonFile('expenses.json'));
    foreach ($expenses as $ex) {
        $d = trim((string) ($ex['date'] ?? ''));
        if ($d === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $d)) {
            continue;
        }
        if ($d < $rangeStart || $d > $rangeEnd) {
            continue;
        }
        $t = trim((string) ($ex['title'] ?? ''));
        if ($t === '') {
            $t = 'Expense';
        }
        $val = isset($ex['value']) && is_numeric($ex['value']) ? round((float) $ex['value'], 2) : null;
        $aid = (string) ($ex['assignee_id'] ?? '');
        $an = $aid !== '' ? ($nameById[$aid] ?? '') : '';
        $detail = $val !== null ? number_format($val, 2, '.', '') . ($sym !== '' ? ' ' . $sym : '') : '';
        if ($an !== '') {
            $detail = trim($detail . ' · ' . $an, ' ·');
        }
        $events[] = [
            'type' => 'expense',
            'date' => $d,
            'title' => $t,
            'detail' => $detail,
            'hrefQuery' => 'tab=currency',
        ];
    }

    $billDefs = normalizeCalendarBillDaysRaw($familySettings['calendar_bill_days'] ?? []);
    foreach ($billDefs as $bd) {
        $title = trim((string) ($bd['title'] ?? ''));
        if ($title === '') {
            continue;
        }
        $single = [$bd];
        foreach (calendarBillReminderDatesInRange($rangeStart, $rangeEnd, $single, $tzLocal) as $ymd) {
            $events[] = [
                'type' => 'bill_day',
                'date' => $ymd,
                'title' => $title,
                'detail' => 'Bill reminder',
                'hrefQuery' => 'tab=currency',
            ];
        }
    }

    $startYear = (int) substr($rangeStart, 0, 4);
    $endYear = (int) substr($rangeEnd, 0, 4);
    foreach ($people as $p) {
        $nm = trim((string) ($p['name'] ?? ''));
        $bdRaw = trim((string) ($p['birthday'] ?? ''));
        if ($nm === '' || !preg_match('/^\d{4}-(\d{2})-(\d{2})$/', $bdRaw, $bm)) {
            continue;
        }
        $mm = (int) $bm[1];
        $dd = (int) $bm[2];
        for ($y = $startYear; $y <= $endYear; $y++) {
            $bday = hubCalendarBirthdayInYear($y, $mm, $dd, $tzLocal);
            if ($bday === null) {
                continue;
            }
            $bYmd = $bday->format('Y-m-d');
            if ($bYmd < $rangeStart || $bYmd > $rangeEnd) {
                continue;
            }
            $idealReminderYmd = $bday->modify('-7 days')->format('Y-m-d');
            $eventYmd = max($rangeStart, $idealReminderYmd);
            if ($eventYmd > $rangeEnd) {
                continue;
            }
            try {
                $evDt = new DateTimeImmutable($eventYmd . ' 00:00:00', $tzLocal);
            } catch (Exception $e) {
                continue;
            }
            $interval = $evDt->diff($bday);
            if ($interval->invert === 1) {
                continue;
            }
            $daysUntil = (int) $interval->days;
            $bdayLabel = $bday->format('F j');
            if ($daysUntil === 0) {
                $title = $nm . '\'s birthday today';
            } elseif ($daysUntil === 1) {
                $title = $nm . '\'s birthday tomorrow';
            } elseif ($daysUntil === 7) {
                $title = $nm . '\'s birthday in one week';
            } else {
                $title = $nm . '\'s birthday in ' . $daysUntil . ' days';
            }
            $events[] = [
                'type' => 'birthday_reminder',
                'date' => $eventYmd,
                'title' => $title,
                'detail' => 'Birthday on ' . $bdayLabel,
                'hrefQuery' => 'tab=settings',
            ];
        }
    }

    usort($events, 'hubCalendarEventSortCompare');
    return $events;
}

/**
 * Default two-week window from today in family timezone.
 *
 * @return array{0: string, 1: string} rangeStart, rangeEnd
 */
function hubCalendarDefaultAgendaRange(array $familySettings): array {
    [, $tzLocal] = familyHubCalendarContext($familySettings);
    $today = familyHubTodayYmdInTz($tzLocal);
    try {
        $end = (new DateTimeImmutable($today . ' 00:00:00', $tzLocal))->modify('+13 days')->format('Y-m-d');
    } catch (Exception $e) {
        $end = $today;
    }
    return [$today, $end];
}
