<script lang="ts">
  import { createEventDispatcher, onMount } from 'svelte';
  import type { TeamMember } from '$lib/services/teamMemberService';
  import type { Holiday } from '$lib/types/capacity';
  import { normalizeToken, type NormalizedToken } from '$lib/utils/expertModeTokens';
  import { batchUpdateAvailability } from '$lib/api/capacity';
  import { getIndividualCapacity } from '$lib/api/capacity';

  export let month: string;
  export let teamMembers: TeamMember[];
  export let holidays: Holiday[];

  const dispatch = createEventDispatcher<{
    dirty: { count: number };
    valid: { allValid: boolean };
    saved: void;
  }>();

  interface CellState {
    memberId: string;
    date: string;
    originalValue: number | null;
    currentValue: NormalizedToken;
  }

  let cells: Map<string, CellState> = new Map();
  let loading = true;
  let saving = false;
  let error: string | null = null;
  let focusedCell: string | null = null;

  $: daysInMonth = getDaysInMonth(month);
  $: holidayDates = new Set(holidays.map((h) => h.date));
  $: dirtyCells = Array.from(cells.values()).filter(
    (c) => c.currentValue.valid && c.currentValue.numericValue !== c.originalValue
  );
  $: invalidCells = Array.from(cells.values()).filter((c) => !c.currentValue.valid);
  $: canSubmit = dirtyCells.length > 0 && invalidCells.length === 0 && !saving;

  $: totalCapacity = calculateTotalCapacity();
  $: totalRevenue = calculateTotalRevenue();

  $: dispatch('dirty', { count: dirtyCells.length });
  $: dispatch('valid', { allValid: invalidCells.length === 0 });

  function getDaysInMonth(monthStr: string): string[] {
    const [year, month] = monthStr.split('-').map(Number);
    const date = new Date(year, month - 1, 1);
    const days: string[] = [];
    while (date.getMonth() === month - 1) {
      const dayStr = date.toISOString().split('T')[0];
      days.push(dayStr);
      date.setDate(date.getDate() + 1);
    }
    return days;
  }

  function isWeekend(dateStr: string): boolean {
    const date = new Date(dateStr + 'T00:00:00');
    const day = date.getDay();
    return day === 0 || day === 6;
  }

  function getCellKey(memberId: string, date: string): string {
    return `${memberId}:${date}`;
  }

  function calculateTotalCapacity(): number {
    return Array.from(cells.values())
      .filter((c) => c.currentValue.valid)
      .reduce((sum, c) => sum + (c.currentValue.numericValue ?? 0), 0);
  }

  function calculateTotalRevenue(): number {
    return teamMembers.reduce((total, member) => {
      const memberCells = Array.from(cells.values()).filter((c) => c.memberId === member.id);
      const memberCapacity = memberCells
        .filter((c) => c.currentValue.valid)
        .reduce((sum, c) => sum + (c.currentValue.numericValue ?? 0), 0);
      const hourlyRate = parseFloat(member.hourly_rate) || 0;
      return total + memberCapacity * 8 * hourlyRate;
    }, 0);
  }

  async function loadExistingData() {
    loading = true;
    cells = new Map();

    for (const member of teamMembers) {
      try {
        const capacity = await getIndividualCapacity(month, member.id);
        for (const detail of capacity.details) {
          const key = getCellKey(member.id, detail.date);
          const numericValue = detail.availability;
          const wknd = isWeekend(detail.date);
          const hol = holidayDates.has(detail.date);

          let token: string;
          if (numericValue === 0) {
            if (hol) {
              token = 'H';
            } else if (wknd) {
              token = 'O';
            } else {
              token = '0';
            }
          } else if (numericValue === 0.5) {
            token = '0.5';
          } else {
            token = '1';
          }

          cells.set(key, {
            memberId: member.id,
            date: detail.date,
            originalValue: numericValue,
            currentValue: { rawToken: token, numericValue, valid: true }
          });
        }
      } catch {
        for (const date of daysInMonth) {
          const key = getCellKey(member.id, date);
          const wknd = isWeekend(date);
          const hol = holidayDates.has(date);
          let defaultValue: NormalizedToken;
          if (hol) {
            defaultValue = { rawToken: 'H', numericValue: 0, valid: true };
          } else if (wknd) {
            defaultValue = { rawToken: 'O', numericValue: 0, valid: true };
          } else {
            defaultValue = { rawToken: '1', numericValue: 1, valid: true };
          }
          cells.set(key, {
            memberId: member.id,
            date,
            originalValue: defaultValue.numericValue,
            currentValue: defaultValue
          });
        }
      }
      for (const date of daysInMonth) {
        const key = getCellKey(member.id, date);
        if (cells.has(key)) {
          continue;
        }
        const wknd = isWeekend(date);
        const hol = holidayDates.has(date);
        const defaultValue: NormalizedToken = hol
          ? { rawToken: 'H', numericValue: 0, valid: true }
          : wknd
            ? { rawToken: 'O', numericValue: 0, valid: true }
            : { rawToken: '1', numericValue: 1, valid: true };
        cells.set(key, {
          memberId: member.id,
          date,
          originalValue: defaultValue.numericValue,
          currentValue: defaultValue
        });
      }
    }

    loading = false;
  }

  function handleCellInput(memberId: string, date: string, rawValue: string) {
    const key = getCellKey(memberId, date);
    const cell = cells.get(key);
    if (!cell) return;

    const normalized = normalizeToken(rawValue, isWeekend(date), holidayDates.has(date));
    cell.currentValue = normalized;
    cells = cells;
  }

  async function handleSubmit() {
    if (!canSubmit) return;

    saving = true;
    error = null;

    try {
      const updates = dirtyCells.map((cell) => ({
        team_member_id: cell.memberId,
        date: cell.date,
        availability: cell.currentValue.numericValue as 0 | 0.5 | 1
      }));

      await batchUpdateAvailability(month, updates);

      // Update original values to current values
      for (const cell of dirtyCells) {
        cell.originalValue = cell.currentValue.numericValue;
      }
      cells = cells;
      dispatch('saved');
    } catch (e) {
      error = e instanceof Error ? e.message : 'Failed to save changes';
    } finally {
      saving = false;
    }
  }

  onMount(() => {
    loadExistingData();
  });
</script>

<div class="space-y-4">
  {#if loading}
    <div class="flex items-center gap-2 text-sm text-base-content/60 p-4">
      <span class="loading loading-spinner loading-sm"></span>
      Loading expert mode data...
    </div>
  {:else}
    <div class="flex items-center justify-between text-sm">
      <div class="flex gap-6">
        <span><strong>Capacity:</strong> {totalCapacity.toFixed(1)} person-days</span>
        <span><strong>Revenue:</strong> ${totalRevenue.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
      </div>
      <button
        class="btn btn-sm btn-primary"
        disabled={!canSubmit}
        on:click={handleSubmit}
      >
        {#if saving}
          <span class="loading loading-spinner loading-xs"></span>
          Saving...
        {:else}
          Submit ({dirtyCells.length})
        {/if}
      </button>
    </div>

    {#if error}
      <div class="alert alert-error text-sm">{error}</div>
    {/if}

    <div class="overflow-x-auto border border-base-200 rounded-lg">
      <table class="table table-xs">
        <thead>
          <tr>
            <th class="sticky left-0 bg-base-100 z-10 min-w-[150px]">Team Member</th>
            {#each daysInMonth as date}
              {@const day = parseInt(date.split('-')[2])}
              {@const isWknd = isWeekend(date)}
              {@const isHol = holidayDates.has(date)}
              <th
                class="text-center min-w-[40px] {isWknd ? 'bg-base-300 border-b-2 border-base-400' : ''} {isHol ? 'bg-warning/40 border-b-2 border-warning font-bold' : ''}"
                title={isHol ? holidays.find((h) => h.date === date)?.name : ''}
              >
                {day}{isHol ? ' H' : ''}
              </th>
            {/each}
          </tr>
        </thead>
        <tbody>
          {#each teamMembers as member}
            <tr>
              <td class="sticky left-0 bg-base-100 z-10 font-medium">{member.name}</td>
              {#each daysInMonth as date}
                {@const key = getCellKey(member.id, date)}
                {@const cell = cells.get(key)}
                {@const isWknd = isWeekend(date)}
                {@const isHol = holidayDates.has(date)}
                {@const isFocused = focusedCell === key}
                {@const isInvalid = cell && !cell.currentValue.valid}
                {@const isDirty = cell && cell.currentValue.numericValue !== cell.originalValue}
                <td
                  class="p-0 {isWknd ? 'bg-base-300' : ''} {isHol ? 'bg-warning/40' : ''}"
                >
                  <input
                    type="text"
                    class="input input-xs w-full text-center p-0 h-6 min-h-0 border-0 {isInvalid ? 'border-2 border-error' : ''} {isDirty && !isInvalid ? 'bg-success/20' : ''} {isFocused ? 'ring-2 ring-primary' : ''}"
                    value={cell?.currentValue.rawToken ?? ''}
                    on:input={(e) => handleCellInput(member.id, date, e.currentTarget.value)}
                    on:focus={() => (focusedCell = key)}
                    on:blur={() => (focusedCell = null)}
                    aria-label="{member.name} {date}"
                  />
                </td>
              {/each}
            </tr>
          {/each}
        </tbody>
      </table>
    </div>
  {/if}
</div>
