<!-- @format -->
<script lang="ts">
  export let onVisibleMonthChange: (month: {
    year: number;
    month: number;
    label: string;
  }) => void = () => {};
  export let onSelectedDateChange: (payload: {
    year: number;
    month: number;
    day: number;
    key: string;
  }) => void = () => {};
  export let onDateActivate: (payload: {
    year: number;
    month: number;
    day: number;
    key: string;
  }) => void = () => {};

  const today = new Date();
  let currentYear = today.getFullYear();
  let currentMonth = today.getMonth();
  let selectedDateKey = getDateKey(
    today.getFullYear(),
    today.getMonth(),
    today.getDate(),
  );

  const weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

  type CalendarCell = {
    day: number;
    month: number;
    year: number;
    inMonth: boolean;
    isToday: boolean;
    isSelected: boolean;
  };

  function getDateKey(year: number, month: number, day: number): string {
    const mm = String(month + 1).padStart(2, "0");
    const dd = String(day).padStart(2, "0");
    return `${year}-${mm}-${dd}`;
  }

  function setViewDate(year: number, month: number) {
    const next = new Date(year, month, 1);
    currentYear = next.getFullYear();
    currentMonth = next.getMonth();
  }

  function changeMonth(offset: number) {
    setViewDate(currentYear, currentMonth + offset);
  }

  function selectCell(cell: CalendarCell) {
    if (!cell.inMonth) {
      setViewDate(cell.year, cell.month);
    }
    const key = getDateKey(cell.year, cell.month, cell.day);
    selectedDateKey = key;
    onDateActivate({
      year: cell.year,
      month: cell.month,
      day: cell.day,
      key,
    });
  }

  function getCalendarCells(year: number, month: number): CalendarCell[] {
    const firstDay = new Date(year, month, 1);
    const lastDay = new Date(year, month + 1, 0);
    const prevMonthLastDay = new Date(year, month, 0).getDate();
    const startOffset = (firstDay.getDay() + 6) % 7;
    const daysInMonth = lastDay.getDate();

    const nextCells: CalendarCell[] = [];

    for (let i = 0; i < startOffset; i += 1) {
      const day = prevMonthLastDay - startOffset + i + 1;
      const prevMonthDate = new Date(year, month - 1, day);
      const key = getDateKey(
        prevMonthDate.getFullYear(),
        prevMonthDate.getMonth(),
        day,
      );
      nextCells.push({
        day,
        month: prevMonthDate.getMonth(),
        year: prevMonthDate.getFullYear(),
        inMonth: false,
        isToday:
          key ===
          getDateKey(today.getFullYear(), today.getMonth(), today.getDate()),
        isSelected: key === selectedDateKey,
      });
    }

    for (let day = 1; day <= daysInMonth; day += 1) {
      const key = getDateKey(year, month, day);
      nextCells.push({
        day,
        month,
        year,
        inMonth: true,
        isToday:
          key ===
          getDateKey(today.getFullYear(), today.getMonth(), today.getDate()),
        isSelected: key === selectedDateKey,
      });
    }

    const trailing = (7 - (nextCells.length % 7)) % 7;
    for (let day = 1; day <= trailing; day += 1) {
      const nextMonthDate = new Date(year, month + 1, day);
      const key = getDateKey(
        nextMonthDate.getFullYear(),
        nextMonthDate.getMonth(),
        day,
      );
      nextCells.push({
        day,
        month: nextMonthDate.getMonth(),
        year: nextMonthDate.getFullYear(),
        inMonth: false,
        isToday:
          key ===
          getDateKey(today.getFullYear(), today.getMonth(), today.getDate()),
        isSelected: key === selectedDateKey,
      });
    }

    return nextCells;
  }

  $: monthLabel = new Date(currentYear, currentMonth, 1).toLocaleString(
    undefined,
    { month: "long" },
  );
  $: cells = getCalendarCells(currentYear, currentMonth);
  $: onVisibleMonthChange({
    year: currentYear,
    month: currentMonth,
    label: monthLabel,
  });
  $: {
    const parts = selectedDateKey.split("-");
    const [year, month, day] = parts.map((value) => Number(value));
    if (
      parts.length === 3 &&
      !Number.isNaN(year) &&
      !Number.isNaN(month) &&
      !Number.isNaN(day)
    ) {
      onSelectedDateChange({
        year,
        month: month - 1,
        day,
        key: selectedDateKey,
      });
    }
  }
</script>

<section class="calendar-widget" aria-label="Monthly calendar">
  <header class="calendar-header">
    <button
      type="button"
      class="nav-icon"
      aria-label="Previous month"
      on:click={() => changeMonth(-1)}
    >
      <span class="material-symbols-outlined">chevron_left</span>
    </button>

    <div class="month-title">
      <h3>{monthLabel}</h3>
      <span>{currentYear}</span>
    </div>

    <button
      type="button"
      class="nav-icon"
      aria-label="Next month"
      on:click={() => changeMonth(1)}
    >
      <span class="material-symbols-outlined">chevron_right</span>
    </button>
  </header>

  <div class="calendar-weekdays">
    {#each weekdays as weekday}
      <span>{weekday}</span>
    {/each}
  </div>

  <div class="calendar-grid">
    {#each cells as cell}
      <button
        type="button"
        class="calendar-cell"
        class:is-muted={!cell.inMonth}
        class:is-today={cell.isToday}
        class:is-selected={cell.isSelected}
        aria-label={`Day ${cell.day}`}
        on:click={() => selectCell(cell)}
      >
        <span class="day-number">{cell.day}</span>
      </button>
    {/each}
  </div>
</section>

<style>
  .calendar-widget {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    background: var(--surface-1);
    padding: 10px;
  }

  .calendar-header {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr) 28px;
    align-items: center;
    gap: 8px;
  }

  .nav-icon {
    width: 28px;
    height: 28px;
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    display: grid;
    place-items: center;
    color: var(--text-muted);
    cursor: pointer;
  }

  .nav-icon .material-symbols-outlined {
    font-size: 1rem;
  }

  .nav-icon:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
  }

  .month-title {
    text-align: center;
  }

  .month-title h3 {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--text-primary);
  }

  .month-title span {
    font-size: 0.74rem;
    color: var(--text-muted);
  }

  .calendar-weekdays,
  .calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
  }

  .calendar-weekdays span {
    text-align: center;
    font-size: 0.7rem;
    color: var(--text-dim);
    font-weight: 500;
  }

  .calendar-cell {
    height: 36px;
    border-radius: 7px;
    border: 1px solid transparent;
    font-size: 0.76rem;
    color: var(--text-muted);
    cursor: pointer;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .day-number {
    line-height: 1;
  }

  .calendar-cell:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
    border-color: var(--border-soft);
  }

  .calendar-cell.is-muted {
    color: var(--text-dim);
  }

  .calendar-cell.is-today {
    color: var(--text-primary);
    background: var(--surface-3);
    border-color: var(--border-strong);
  }

  .calendar-cell.is-selected {
    border-color: var(--zinc-300);
    box-shadow: inset 0 0 0 1px var(--zinc-300);
  }
</style>
