<script lang="ts">
  import { onMount } from 'svelte';
  import { page } from '$app/stores';
  import { goto } from '$app/navigation';
  import PageHeader from '$lib/components/layout/PageHeader.svelte';
  import LoadingState from '$lib/components/common/LoadingState.svelte';
  import FilterBar from '$lib/components/common/FilterBar.svelte';
  import MultiSelect from '$lib/components/common/MultiSelect.svelte';
  import Pagination from '$lib/components/common/Pagination.svelte';
  import { actualsService } from '$lib/services/actualsService';
  import { projectService, type Project } from '$lib/services/projectService';
  import { teamMemberService, type TeamMember } from '$lib/services/teamMemberService';
  import { ChevronLeft, ChevronRight } from 'lucide-svelte';
  import type { ActualGridItem } from '$lib/types/actuals';

  function getCurrentMonth(): string {
    const now = new Date();
    return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
  }

  function formatMonth(period: string): string {
    const [year, month] = period.split('-').map(Number);
    const date = new Date(year, (month ?? 1) - 1);
    return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
  }

  function formatVarianceValue(percentage: number | null, display: string | null): string {
    if (display) return display;
    if (percentage === null) return '-';
    const sign = percentage >= 0 ? '+' : '';
    return `${sign}${percentage.toFixed(1)}%`;
  }

  function getVarianceColor(indicator: string | null): string {
    switch (indicator) {
      case 'green':
        return 'badge-success';
      case 'yellow':
        return 'badge-warning';
      case 'red':
        return 'badge-error';
      default:
        return 'badge-ghost';
    }
  }

  let actuals = $state<ActualGridItem[]>([]);
  let projects = $state<Project[]>([]);
  let teamMembers = $state<TeamMember[]>([]);
  let loading = $state(true);
  let errorMessage = $state<string | null>(null);

  let currentPeriod = $state(getCurrentMonth());
  let selectedProjectIds = $state<string[]>([]);
  let selectedMemberIds = $state<string[]>([]);
  let includeInactive = $state(false);
  let searchQuery = $state('');

  let currentPage = $state(1);
  let totalPages = $state(1);
  let totalItems = $state(0);
  let perPage = $state(25);

  let showModal = $state(false);
  let selectedCell = $state<ActualGridItem | null>(null);
  let formHours = $state(0);
  let formNotes = $state('');
  let formLoading = $state(false);
  let formError = $state<string | null>(null);

  // Derive visible members from actuals data (respects filters applied to API)
  let visibleMembers = $derived(
    (() => {
      const map = new Map<string, { id: string; name: string; is_active: boolean }>();
      actuals.forEach(item => {
        if (item.team_member && item.team_member_id) {
          map.set(item.team_member_id, item.team_member);
        }
      });
      // Sort by name
      const list = Array.from(map.values());
      list.sort((a, b) => a.name.localeCompare(b.name));
      return list;
    })()
  );

  let projectOptions = $derived(
    projects
      .map(project => ({
        id: project.id,
        label: `${project.code} - ${project.title}`,
        is_active: (project as Project & { is_active?: boolean }).is_active ?? true
      }))
      .filter(option => includeInactive || option.is_active)
      .sort((a, b) => a.label.localeCompare(b.label))
  );

  let memberOptions = $derived(
    teamMembers
      .map(member => ({ id: member.id, label: member.name, is_active: member.active }))
      .filter(option => includeInactive || option.is_active)
      .sort((a, b) => a.label.localeCompare(b.label))
  );

  let visibleProjects = $derived(
    (() => {
      const map = new Map(actuals.map(item => [item.project_id, item.project]));
      const list = Array.from(map.values());
      list.sort((a, b) => a.code.localeCompare(b.code));
      return list;
    })()
  );

  onMount(async () => {
    const url = $page.url;
    currentPeriod = url.searchParams.get('month') || getCurrentMonth();
    const parsedPage = Number(url.searchParams.get('page'));
    currentPage = Number.isFinite(parsedPage) && parsedPage > 0 ? parsedPage : 1;
    const parsedPerPage = Number(url.searchParams.get('per_page'));
    perPage = Number.isFinite(parsedPerPage) && parsedPerPage > 0 ? parsedPerPage : perPage;
    includeInactive = url.searchParams.get('include_inactive') === 'true';
    searchQuery = url.searchParams.get('search') || '';
    selectedProjectIds = url.searchParams.getAll('project_ids[]');
    selectedMemberIds = url.searchParams.getAll('team_member_ids[]');

    await Promise.all([loadProjects(), loadTeamMembers()]);
    await loadData();
  });

  async function loadProjects() {
    try {
      const collection = await projectService.getAll();
      projects = collection.slice().sort((a, b) => (a.code ?? '').localeCompare(b.code ?? ''));
    } catch (error) {
      console.error('Unable to load projects', error);
    }
  }

  async function loadTeamMembers() {
    try {
      const collection = await teamMemberService.getAll();
      teamMembers = collection.slice().sort((a, b) => a.name.localeCompare(b.name));
    } catch (error) {
      console.error('Unable to load team members', error);
    }
  }

  async function loadData() {
    loading = true;
    errorMessage = null;
    const params = new URLSearchParams();
    params.set('month', currentPeriod);
    params.set('page', String(currentPage));
    params.set('per_page', String(perPage));
    selectedProjectIds.forEach(id => {
      params.append('project_ids[]', id);
    });
    selectedMemberIds.forEach(id => {
      params.append('team_member_ids[]', id);
    });
    if (includeInactive) params.set('include_inactive', 'true');
    if (searchQuery) params.set('search', searchQuery);
    const queryString = params.toString();
    goto(`${$page.url.pathname}${queryString ? `?${queryString}` : ''}`, { replaceState: true });

    try {
      const response = await actualsService.getAll(currentPeriod, {
        project_ids: selectedProjectIds,
        team_member_ids: selectedMemberIds,
        include_inactive: includeInactive,
        search: searchQuery,
        page: currentPage,
        per_page: perPage
      });

      actuals = response.data;
      currentPage = response.meta.current_page;
      totalPages = response.meta.last_page;
      totalItems = response.meta.total;
      perPage = response.meta.per_page;
    } catch (error) {
      const apiError = error as { message?: string };
      errorMessage = apiError.message || 'Failed to load actuals';
      actuals = [];
      totalPages = 1;
      totalItems = 0;
    } finally {
      loading = false;
    }
  }

  async function applyFilters() {
    currentPage = 1;
    await loadData();
  }

  async function clearFilters() {
    selectedProjectIds = [];
    selectedMemberIds = [];
    includeInactive = false;
    searchQuery = '';
    currentPage = 1;
    await loadData();
  }

  async function handlePageChange(page: number) {
    if (page === currentPage) return;
    currentPage = page;
    await loadData();
  }

  async function changeMonth(delta: number) {
    const [year, month] = currentPeriod.split('-').map(Number);
    const reference = new Date(year, month - 1 + delta);
    currentPeriod = `${reference.getFullYear()}-${String(reference.getMonth() + 1).padStart(2, '0')}`;
    currentPage = 1;
    await loadData();
  }

  function getCell(projectId: string, memberId: string | null): ActualGridItem {
    const found = actuals.find(cell => cell.project_id === projectId && (cell.team_member_id ?? null) === memberId);
    if (found) return found;

    const project = projects.find(item => item.id === projectId);
    const member = memberId ? teamMembers.find(item => item.id === memberId) ?? null : null;
    const projectPayload = {
      id: project?.id ?? projectId,
      code: project?.code ?? 'Unknown',
      title: project?.title ?? 'Unknown project',
      status: project?.status,
      is_active: (project as Project & { is_active?: boolean }).is_active ?? true
    };
    const memberPayload = member
      ? { id: member.id, name: member.name, is_active: member.active }
      : null;

    return {
      id: '',
      project_id: projectId,
      project: projectPayload,
      team_member_id: memberId,
      team_member: memberPayload,
      month: currentPeriod,
      allocated_hours: '0',
      actual_hours: '0',
      variance_percentage: null,
      variance_display: null,
      variance_indicator: 'gray',
      notes: null,
      is_readonly: !projectPayload.is_active || (memberPayload ? !memberPayload.is_active : false)
    };
  }

  function openModal(cell: ActualGridItem) {
    selectedCell = cell;
    formHours = 0;
    formNotes = cell.notes ?? '';
    formError = null;
    showModal = true;
  }

  function closeModal() {
    showModal = false;
    selectedCell = null;
    formError = null;
  }

  async function handleSubmit() {
    if (!selectedCell) return;
    const hoursToAdd = Number(formHours);
    if (!Number.isFinite(hoursToAdd) || hoursToAdd <= 0) {
      formError = 'Enter a positive number of hours to log.';
      return;
    }

    formLoading = true;
    formError = null;

    try {
      await actualsService.create({
        project_id: selectedCell.project_id,
        team_member_id: selectedCell.team_member_id,
        month: currentPeriod,
        hours: hoursToAdd,
        notes: formNotes.trim() || undefined
      });

      showModal = false;
      await loadData();
    } catch (error) {
      const apiError = error as { message?: string; data?: { errors?: Record<string, string[]> } };
      if (apiError.data?.errors) {
        formError = Object.entries(apiError.data.errors)
          .map(([field, messages]) => `${field}: ${messages.join(', ')}`)
          .join('; ');
      } else {
        formError = apiError.message || 'Failed to log hours';
      }
    } finally {
      formLoading = false;
    }
  }

  async function handleDelete() {
    if (!selectedCell?.id) return;
    formLoading = true;
    formError = null;

    try {
      await actualsService.delete(selectedCell.id);
      showModal = false;
      await loadData();
    } catch (error) {
      const apiError = error as { message?: string };
      formError = apiError.message || 'Failed to remove actuals entry';
    } finally {
      formLoading = false;
    }
  }
</script>

<svelte:head>
  <title>Actuals Tracking | Headroom</title>
</svelte:head>

<PageHeader title="Actuals Tracking" description="Log and view actual hours vs allocations">
  <div class="flex items-center gap-2">
    <button class="btn btn-ghost btn-sm" onclick={() => changeMonth(-1)} aria-label="Previous month">
      <ChevronLeft size={16} />
    </button>
    <span class="text-lg font-medium">{formatMonth(currentPeriod)}</span>
    <button class="btn btn-ghost btn-sm" onclick={() => changeMonth(1)} aria-label="Next month">
      <ChevronRight size={16} />
    </button>
  </div>
</PageHeader>

<section class="space-y-3 mb-6">
  <FilterBar
    searchValue={searchQuery}
    searchPlaceholder="Search projects or members"
    onSearchChange={value => (searchQuery = value)}
    onClear={clearFilters}
  />

  <div class="flex flex-wrap gap-3 items-center">
    <div class="w-[220px]">
      <MultiSelect
        options={projectOptions}
        bind:selected={selectedProjectIds}
        placeholder="Filter projects"
        labelKey="label"
        valueKey="id"
        compactTrigger
      />
    </div>

    <div class="w-[220px]">
      <MultiSelect
        options={memberOptions}
        bind:selected={selectedMemberIds}
        placeholder="Filter team members"
        labelKey="label"
        valueKey="id"
        compactTrigger
      />
    </div>

    <label class="flex items-center gap-2 text-sm">
      <input
        type="checkbox"
        class="checkbox checkbox-sm"
        checked={includeInactive}
        onchange={event => (includeInactive = event.currentTarget.checked)}
      />
      Include inactive
    </label>

    <button
      type="button"
      class="btn btn-primary btn-sm ml-auto"
      onclick={applyFilters}
      disabled={loading}
    >
      Apply
    </button>
  </div>

  {#if selectedProjectIds.length > 0 || selectedMemberIds.length > 0}
    <div class="flex flex-wrap items-center gap-2 rounded-lg bg-base-200/60 px-3 py-2">
      <span class="text-xs font-semibold uppercase tracking-wide text-base-content/50 mr-1">Active filters</span>

      {#each selectedProjectIds as id (id)}
        {@const option = projectOptions.find(o => o.id === id)}
        {#if option}
          <span class="badge badge-sm badge-primary gap-1">
            {option.label}
            <button
              type="button"
              class="hover:text-error"
              onclick={() => { selectedProjectIds = selectedProjectIds.filter(v => v !== id); }}
            >×</button>
          </span>
        {/if}
      {/each}

      {#each selectedMemberIds as id (id)}
        {@const option = memberOptions.find(o => o.id === id)}
        {#if option}
          <span class="badge badge-sm badge-secondary gap-1">
            {option.label}
            <button
              type="button"
              class="hover:text-error"
              onclick={() => { selectedMemberIds = selectedMemberIds.filter(v => v !== id); }}
            >×</button>
          </span>
        {/if}
      {/each}

      <button
        type="button"
        class="btn btn-ghost btn-xs ml-auto"
        onclick={clearFilters}
      >
        Clear all
      </button>
    </div>
  {/if}
</section>

{#if loading}
  <LoadingState />
{:else}
  {#if errorMessage}
    <div class="alert alert-error mb-4">
      <span>{errorMessage}</span>
    </div>
  {/if}

  <div class="rounded-xl border border-base-200 bg-base-100">
    {#if !actuals.length}
      <div class="p-6 text-sm text-center text-base-content/60">
        No actuals recorded for {formatMonth(currentPeriod)}.
      </div>
    {:else}
      <div class="overflow-x-auto">
        <table class="table table-xs w-full">
          <thead>
            <tr>
              <th class="sticky left-0 bg-base-200 z-10 min-w-[200px]">Project</th>
              {#each visibleMembers as member (member.id)}
                <th class="text-center min-w-[130px]">{member.name}</th>
              {/each}
              <th class="text-center bg-base-200 font-medium">Untracked</th>
            </tr>
          </thead>
          <tbody>
            {#each visibleProjects as project (project.id)}
              {@const rowMembers = visibleMembers.map(member => getCell(project.id, member.id))}
              {@const rowUntracked = getCell(project.id, null)}
              <tr class="hover">
                <td class="sticky left-0 bg-base-100 z-10 font-medium">
                  {project.code} - {project.title}
                </td>
                {#each rowMembers as cell, index (visibleMembers[index]?.id ?? index)}
                  <td
                    class={`text-center cursor-pointer transition-colors ${cell.is_readonly ? 'opacity-50 bg-base-200/40 cursor-not-allowed' : 'hover:bg-base-200/60'}`}
                    onclick={() => !cell.is_readonly && openModal(cell)}
                  >
                    <div class="text-[11px] text-base-content/60">{cell.allocated_hours}h alloc</div>
                    <div class="font-semibold">{cell.actual_hours}h actual</div>
                    <div class={`badge badge-xs ${getVarianceColor(cell.variance_indicator)}`}>
                      {formatVarianceValue(cell.variance_percentage, cell.variance_display)}
                    </div>
                  </td>
                {/each}
                <td
                  class={`text-center cursor-pointer transition-colors bg-base-200/30 ${rowUntracked.is_readonly ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200/60'}`}
                  onclick={() => !rowUntracked.is_readonly && openModal(rowUntracked)}
                >
                  <div class="text-[11px] text-base-content/60">{rowUntracked.allocated_hours}h alloc</div>
                  <div class="font-semibold">{rowUntracked.actual_hours}h actual</div>
                  <div class={`badge badge-xs ${getVarianceColor(rowUntracked.variance_indicator)}`}>
                    {formatVarianceValue(rowUntracked.variance_percentage, rowUntracked.variance_display)}
                  </div>
                </td>
              </tr>
            {/each}
          </tbody>
        </table>
      </div>
    {/if}
  </div>

  <div class="mt-4">
    <Pagination
      currentPage={currentPage}
      totalPages={totalPages}
      totalItems={totalItems}
      pageSize={perPage}
      onPageChange={handlePageChange}
    />
  </div>
{/if}

{#if showModal && selectedCell}
  <div class="modal modal-open">
    <div class="modal-box max-w-md">
      <div class="flex justify-between items-center mb-4">
        <h3 class="font-bold text-lg">Log Actual Hours</h3>
        <button class="btn btn-ghost btn-sm btn-circle" onclick={closeModal}>
          ✕
        </button>
      </div>

      {#if formError}
        <div class="alert alert-error mb-4">
          <span>{formError}</span>
        </div>
      {/if}

      <form onsubmit={event => { event.preventDefault(); handleSubmit(); }}>
        <div class="form-control mb-3">
          <label class="label" for="actuals-project">
            <span class="label-text font-medium">Project</span>
          </label>
          <input
            id="actuals-project"
            class="input input-bordered"
            type="text"
            value={`${selectedCell.project.code} - ${selectedCell.project.title}`}
            disabled
          />
        </div>

        <div class="form-control mb-3">
          <label class="label" for="actuals-member">
            <span class="label-text font-medium">Team member</span>
          </label>
          <input
            class="input input-bordered"
            type="text"
            id="actuals-member"
            value={selectedCell.team_member?.name ?? 'Untracked'}
            disabled
          />
        </div>

        <div class="form-control mb-4">
          <label class="label" for="actuals-month">
            <span class="label-text font-medium">Month</span>
          </label>
          <input
            id="actuals-month"
            class="input input-bordered"
            type="text"
            value={formatMonth(currentPeriod)}
            disabled
          />
        </div>

        <div class="form-control mb-2">
          <label class="label" for="actuals-hours">
            <span class="label-text font-medium">Hours to add</span>
          </label>
          <input
            class="input input-bordered"
            type="number"
            min="0"
            step="0.5"
            id="actuals-hours"
            bind:value={formHours}
            required
            disabled={formLoading}
          />
        </div>

        <p class="text-sm text-base-content/60 mb-3">
          Current total: {selectedCell.actual_hours}h
        </p>

        <div class="form-control mb-4">
          <label class="label" for="actuals-notes">
            <span class="label-text font-medium">Notes</span>
          </label>
          <textarea
            class="textarea textarea-bordered"
            rows="3"
            placeholder="Optional context"
            id="actuals-notes"
            bind:value={formNotes}
            disabled={formLoading}
          ></textarea>
        </div>

        <div class="modal-action">
          {#if selectedCell.id}
            <button type="button" class="btn btn-error" onclick={handleDelete} disabled={formLoading}>
              Delete
            </button>
          {/if}
          <button type="button" class="btn btn-ghost" onclick={closeModal} disabled={formLoading}>
            Cancel
          </button>
          <button type="submit" class="btn btn-primary" disabled={formLoading}>
            {#if formLoading}
              <span class="loading loading-spinner loading-sm mr-2"></span>
            {/if}
            Add Hours
          </button>
        </div>
      </form>
    </div>
    <div
      class="modal-backdrop"
      role="button"
      tabindex="0"
      onclick={closeModal}
      onkeydown={event => (['Enter', ' ', 'Escape'].includes(event.key) && closeModal())}
    ></div>
  </div>
{/if}
