<?php
// ============================================================
//  Calendar helpers — shared between admin & member pages
// ============================================================
//
//  Provides:
//    - sa_public_holidays($year) → date string ⇒ holiday label
//    - calendar_easter_sunday($year) → timestamp
//    - calendar_render($year, $month, $events, $opts) → HTML
//
//  Public holidays observe the Sunday roll-over rule (Public
//  Holidays Act 1994): if a holiday falls on a Sunday, the
//  following Monday is also a public holiday.
//
// ============================================================

date_default_timezone_set('Africa/Johannesburg');

/**
 * Compute Easter Sunday for a given year using the
 * Anonymous Gregorian algorithm.
 *
 * @return int Unix timestamp at midnight on Easter Sunday.
 */
function calendar_easter_sunday(int $year): int {
    $a = $year % 19;
    $b = intdiv($year, 100);
    $c = $year % 100;
    $d = intdiv($b, 4);
    $e = $b % 4;
    $f = intdiv($b + 8, 25);
    $g = intdiv($b - $f + 1, 3);
    $h = (19 * $a + $b - $d - $g + 15) % 30;
    $i = intdiv($c, 4);
    $k = $c % 4;
    $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
    $m = intdiv($a + 11 * $h + 22 * $l, 451);
    $month = intdiv($h + $l - 7 * $m + 114, 31);
    $day   = (($h + $l - 7 * $m + 114) % 31) + 1;
    return mktime(0, 0, 0, $month, $day, $year);
}

/**
 * South African public holidays for a given year.
 * Returns a map of YYYY-MM-DD → label, including Sunday
 * roll-over Mondays (per the Public Holidays Act 1994).
 */
function sa_public_holidays(int $year): array {
    $easter = calendar_easter_sunday($year);

    // Fixed-date holidays
    $holidays = [
        sprintf('%04d-01-01', $year) => "New Year's Day",
        sprintf('%04d-03-21', $year) => 'Human Rights Day',
        sprintf('%04d-04-27', $year) => 'Freedom Day',
        sprintf('%04d-05-01', $year) => "Workers' Day",
        sprintf('%04d-06-16', $year) => 'Youth Day',
        sprintf('%04d-08-09', $year) => "National Women's Day",
        sprintf('%04d-09-24', $year) => 'Heritage Day',
        sprintf('%04d-12-16', $year) => 'Day of Reconciliation',
        sprintf('%04d-12-25', $year) => 'Christmas Day',
        sprintf('%04d-12-26', $year) => 'Day of Goodwill',
    ];

    // Easter-based
    $good_friday = date('Y-m-d', strtotime('-2 days', $easter));
    $family_day  = date('Y-m-d', strtotime('+1 day',  $easter));
    $holidays[$good_friday] = 'Good Friday';
    $holidays[$family_day]  = 'Family Day';

    // Sunday roll-over: any holiday on a Sunday makes Monday a holiday
    $rollovers = [];
    foreach ($holidays as $date => $label) {
        if ((int)date('w', strtotime($date)) === 0) {
            $monday = date('Y-m-d', strtotime('+1 day', strtotime($date)));
            $rollovers[$monday] = $label . ' (observed)';
        }
    }

    ksort($holidays);
    return $holidays + $rollovers;  // + preserves earlier keys
}

/**
 * Render a month-view calendar.
 *
 * @param int   $year  e.g. 2026
 * @param int   $month 1–12
 * @param array $events Map of YYYY-MM-DD → array of event arrays.
 *                     Each event: ['label' => string, 'url' => string,
 *                                  'class' => string (optional)]
 * @param array $opts  Optional:
 *                     - 'base_url'  → URL prefix for prev/next/today links
 *                                     (default: current path with ?y=&m=)
 *                     - 'show_holidays' → bool (default true)
 *                     - 'show_weekends' → bool (default true; tints sat+sun)
 *                     - 'first_day'   → 1=Mon, 0=Sun (default 1)
 *
 * @return string HTML
 */
function calendar_render(int $year, int $month, array $events = [], array $opts = []): string {
    $first_day      = (int)($opts['first_day'] ?? 1); // 1 = Monday
    $show_holidays  = (bool)($opts['show_holidays'] ?? true);
    $show_weekends  = (bool)($opts['show_weekends'] ?? true);
    $base_url       = $opts['base_url'] ?? ($_SERVER['PHP_SELF'] ?? '?');

    // Sanity bounds
    if ($month < 1 || $month > 12) $month = (int)date('n');
    if ($year  < 1970 || $year > 2100) $year = (int)date('Y');

    $first_ts   = mktime(0, 0, 0, $month, 1, $year);
    $days_in    = (int)date('t', $first_ts);
    $first_dow  = (int)date('w', $first_ts);  // 0 = Sun, 6 = Sat
    // Convert to grid offset: leading empty cells before day 1
    $offset = ($first_dow - $first_day + 7) % 7;

    // Prev / next month
    $prev_y = $month === 1 ? $year - 1 : $year;
    $prev_m = $month === 1 ? 12        : $month - 1;
    $next_y = $month === 12 ? $year + 1 : $year;
    $next_m = $month === 12 ? 1         : $month + 1;
    $today_y = (int)date('Y');
    $today_m = (int)date('n');

    // Public holidays (only need them if we're displaying)
    $holidays = $show_holidays ? sa_public_holidays($year) : [];

    // Day-of-week labels in user's chosen first-day order
    $day_labels_full = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    $headers = [];
    for ($i = 0; $i < 7; $i++) {
        $idx = ($first_day + $i) % 7;
        $headers[] = ['label' => $day_labels_full[$idx], 'is_weekend' => ($idx === 0 || $idx === 6)];
    }

    $today = date('Y-m-d');
    $month_label = date('F Y', $first_ts);

    ob_start();
    ?>
    <div class="cal-wrap">
        <div class="cal-header">
            <h2 class="cal-title"><?= htmlspecialchars($month_label) ?></h2>
            <div class="cal-nav">
                <a class="cal-nav-btn" href="<?= htmlspecialchars($base_url . (str_contains($base_url, '?') ? '&' : '?') . "y={$prev_y}&m={$prev_m}") ?>"
                   title="Previous month">‹</a>
                <a class="cal-nav-btn" href="<?= htmlspecialchars($base_url . (str_contains($base_url, '?') ? '&' : '?') . "y={$today_y}&m={$today_m}") ?>"
                   title="Jump to today">Today</a>
                <a class="cal-nav-btn" href="<?= htmlspecialchars($base_url . (str_contains($base_url, '?') ? '&' : '?') . "y={$next_y}&m={$next_m}") ?>"
                   title="Next month">›</a>
            </div>
        </div>

        <table class="cal-grid">
            <thead>
                <tr>
                    <?php foreach ($headers as $h): ?>
                        <th class="<?= $h['is_weekend'] ? 'cal-th-weekend' : '' ?>">
                            <?= htmlspecialchars($h['label']) ?>
                        </th>
                    <?php endforeach; ?>
                </tr>
            </thead>
            <tbody>
            <?php
            $cell_idx = 0;
            $day = 1;
            // Up to 6 weeks
            for ($week = 0; $week < 6 && $day <= $days_in; $week++):
                echo '<tr>';
                for ($col = 0; $col < 7; $col++):
                    if ($cell_idx < $offset || $day > $days_in) {
                        // Out-of-month padding cell
                        echo '<td class="cal-cell cal-out"></td>';
                    } else {
                        $date_str   = sprintf('%04d-%02d-%02d', $year, $month, $day);
                        $dow        = (int)date('w', strtotime($date_str));
                        $is_weekend = $show_weekends && ($dow === 0 || $dow === 6);
                        $is_today   = ($date_str === $today);
                        $holiday    = $holidays[$date_str] ?? null;

                        $classes = ['cal-cell'];
                        if ($is_today)   $classes[] = 'cal-today';
                        if ($is_weekend) $classes[] = 'cal-weekend';
                        if ($holiday)    $classes[] = 'cal-holiday';

                        echo '<td class="' . implode(' ', $classes) . '">';
                        echo '<div class="cal-day-num">' . $day . '</div>';

                        if ($holiday) {
                            echo '<div class="cal-holiday-label" title="' . htmlspecialchars($holiday) . '">'
                                 . htmlspecialchars($holiday)
                                 . '</div>';
                        }

                        if (!empty($events[$date_str])) {
                            echo '<ul class="cal-events">';
                            foreach ($events[$date_str] as $ev) {
                                $ev_class = htmlspecialchars($ev['class'] ?? '');
                                $ev_label = htmlspecialchars($ev['label'] ?? '');
                                if (!empty($ev['url'])) {
                                    echo '<li class="cal-event ' . $ev_class . '">'
                                       . '<a href="' . htmlspecialchars($ev['url']) . '" title="' . $ev_label . '">'
                                       . $ev_label
                                       . '</a></li>';
                                } else {
                                    echo '<li class="cal-event ' . $ev_class . '" title="' . $ev_label . '">'
                                       . $ev_label . '</li>';
                                }
                            }
                            echo '</ul>';
                        }

                        echo '</td>';
                        $day++;
                    }
                    $cell_idx++;
                endfor;
                echo '</tr>';
            endfor;
            ?>
            </tbody>
        </table>
    </div>
    <?php
    return ob_get_clean();
}

/**
 * Standard CSS for the calendar. Include once per page.
 */
function calendar_css(): string {
    return <<<'CSS'
.cal-wrap{margin:0;}
.cal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.85rem;flex-wrap:wrap;gap:.75rem;}
.cal-title{margin:0;font-size:1.4rem;}
.cal-nav{display:flex;gap:.4rem;}
.cal-nav-btn{
    display:inline-flex;align-items:center;justify-content:center;
    min-width:34px;height:34px;padding:0 .85rem;
    border:1px solid var(--line, #e5e7eb);background:#fff;border-radius:6px;
    color:var(--ink, #1f2937);text-decoration:none;font-size:.92rem;font-weight:500;
    transition:.15s;
}
.cal-nav-btn:hover{background:#f5f3ee;border-color:#d4d0c8;}

.cal-grid{
    width:100%;border-collapse:collapse;table-layout:fixed;
    background:#fff;border:1px solid var(--line, #e5e7eb);border-radius:8px;overflow:hidden;
}
.cal-grid th{
    padding:.55rem 0;font-size:.72rem;font-weight:700;text-transform:uppercase;
    letter-spacing:.05em;color:var(--ink-muted, #64748b);
    background:#fafaf8;border-bottom:1px solid var(--line, #e5e7eb);
}
.cal-th-weekend{color:#9ca3af;}

.cal-cell{
    height:110px;vertical-align:top;padding:.45rem .5rem;
    border:1px solid var(--line, #e5e7eb);position:relative;
    font-size:.78rem;line-height:1.25;background:#fff;
}
.cal-cell.cal-out{background:#fafafa;}
.cal-cell.cal-weekend{background:#f9f7f1;}
.cal-cell.cal-holiday{background:#fff7ed;}
.cal-cell.cal-today{box-shadow:inset 0 0 0 2px #6366f1;}
.cal-cell.cal-holiday.cal-weekend{background:#fef3e7;}

.cal-day-num{font-weight:600;font-size:.85rem;color:var(--ink, #1f2937);margin-bottom:.2rem;}
.cal-cell.cal-today .cal-day-num{color:#6366f1;}

.cal-holiday-label{
    font-size:.65rem;color:#9a3412;background:#fed7aa;
    padding:.05em .35em;border-radius:3px;font-weight:600;
    margin-bottom:.25rem;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

.cal-events{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.15rem;}
.cal-event{
    font-size:.7rem;background:#dbeafe;color:#1e40af;
    padding:.1em .4em;border-radius:3px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.cal-event a{color:inherit;text-decoration:none;display:block;}
.cal-event a:hover{text-decoration:underline;}

.cal-event.task-open       {background:#dbeafe;color:#1e40af;}
.cal-event.task-in_progress{background:#fef3c7;color:#92400e;}
.cal-event.task-done       {background:#dcfce7;color:#166534;text-decoration:line-through;}
.cal-event.task-cancelled  {background:#e5e7eb;color:#6b7280;text-decoration:line-through;}
.cal-event.task-overdue    {background:#fee2e2;color:#991b1b;font-weight:600;}

@media (max-width: 720px) {
    .cal-cell{height:80px;font-size:.7rem;padding:.3rem;}
    .cal-day-num{font-size:.78rem;}
    .cal-holiday-label{font-size:.58rem;}
    .cal-event{font-size:.62rem;}
}
CSS;
}