<script lang="ts">
  import { browser } from '$app/environment';
  import { onMount } from 'svelte';
  import { page } from '$app/stores';
  import { categories } from '$lib/data/stats';
  import { loadCalculators, type CalculatorDef } from '$lib/data/calculatorLoader';

  let allCalculators: CalculatorDef[] = [];
  let isLoaded = false;

  async function loadData() {
    if (isLoaded || !browser) return;
    const data = await loadCalculators();
    allCalculators = data;
    isLoaded = true;
  }

  let expandedCategory = '';
  let expandedUnits: Record<string, string> = {};
  let isDesktop = false;
  let navBreakpoint: MediaQueryList | null = null;
  let lastPath = '';
  let lastDesktop = isDesktop;
  let autoExpandedCategory = '';

  const categoryPathRegex = /^\/category\/([^/]+)(?:\/|$)/;

  function getCategorySlugFromPath(path: string) {
    const match = path.match(categoryPathRegex);
    return match?.[1] ?? '';
  }

  $: currentPath = $page.url.pathname;

  type UnitGroup = {
    label: string;
    conversions: UnitConversionLink[];
  };

  type UnitBucket = {
    label: string;
    conversions: UnitConversionLink[];
  };

  type UnitConversionLink = {
    name: string;
    slug: string;
    sortKey: string;
  };

  const sortConversionsForUnit = (conversions: UnitConversionLink[]) =>
    conversions.slice().sort((a, b) => a.name.localeCompare(b.name));

  const toPairKey = (unitA: string, unitB: string) =>
    [unitA.toLowerCase(), unitB.toLowerCase()].sort().join('::');

  const toDirectionKey = (fromUnit: string, toUnit: string) =>
    `${fromUnit.toLowerCase()}::${toUnit.toLowerCase()}`;

  function addConversion(
    buckets: Map<string, UnitBucket>,
    fromUnit: string,
    toUnit: string,
    slug: string
  ) {
    const bucketKey = fromUnit.toLowerCase();
    const directionKey = toDirectionKey(fromUnit, toUnit);
    const conversion: UnitConversionLink = {
      name: `${fromUnit} to ${toUnit}`,
      slug,
      sortKey: directionKey,
    };

    const existing = buckets.get(bucketKey);
    if (existing) {
      if (!existing.conversions.some(link => link.sortKey === directionKey)) {
        existing.conversions.push(conversion);
      }
      return;
    }

    buckets.set(bucketKey, {
      label: fromUnit,
      conversions: [conversion],
    });
  }

  $: categoryUnitGroups = Object.entries(categories).map(([key, meta]) => {
    const buckets = new Map<string, UnitBucket>();
    
    if (!isLoaded) {
      return { key, meta, units: [] };
    }

    const calcs = allCalculators.filter(c => c.category === key && !c.hidden);
    const canonicalByPair = new Map<string, CalculatorDef>();

    calcs.forEach(calc => {
      const pairKey = toPairKey(calc.labels.in1, calc.labels.in2);
      const existing = canonicalByPair.get(pairKey);
      if (!existing || calc.slug.localeCompare(existing.slug) < 0) {
        canonicalByPair.set(pairKey, calc);
      }
    });

    canonicalByPair.forEach(calc => {
      addConversion(buckets, calc.labels.in1, calc.labels.in2, calc.slug);
      addConversion(buckets, calc.labels.in2, calc.labels.in1, calc.slug);
    });

    const units = [...buckets.entries()]
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([, bucket]) => ({
        label: bucket.label,
        conversions: sortConversionsForUnit(bucket.conversions),
      }));

    return { key, meta, units };
  });

  function toggle(cat: string) {
    const wasOpen = expandedCategory === cat;
    expandedCategory = wasOpen ? '' : cat;
    if (wasOpen) {
      expandedUnits = { ...expandedUnits, [cat]: '' };
    }
  }

  function toggleUnit(category: string, unitLabel: string) {
    expandedUnits = {
      ...expandedUnits,
      [category]: expandedUnits[category] === unitLabel ? '' : unitLabel,
    };
  }

  onMount(() => {
    if (!browser) return;

    navBreakpoint = window.matchMedia('(max-width: 1024px)');
    const updateDesktop = (event?: MediaQueryListEvent) => {
      const matches = event?.matches ?? navBreakpoint!.matches;
      isDesktop = !matches;
    };

    updateDesktop();

    const handleNavChange = (event: MediaQueryListEvent) => updateDesktop(event);
    if ('addEventListener' in navBreakpoint) {
      navBreakpoint.addEventListener('change', handleNavChange);
    } else {
      (navBreakpoint as MediaQueryList & {
        addListener: (listener: (event: MediaQueryListEvent) => void) => void;
      }).addListener(handleNavChange);
    }

    return () => {
      if (!navBreakpoint) return;
      if ('removeEventListener' in navBreakpoint) {
        navBreakpoint.removeEventListener('change', handleNavChange);
      } else {
        (navBreakpoint as MediaQueryList & {
          removeListener: (listener: (event: MediaQueryListEvent) => void) => void;
        }).removeListener(handleNavChange);
      }
    };
  });

  $: if (browser && (currentPath !== lastPath || isDesktop !== lastDesktop)) {
    const slug = getCategorySlugFromPath(currentPath);
    if (isDesktop && slug) {
      expandedCategory = slug;
      autoExpandedCategory = slug;
    } else if (autoExpandedCategory && (!isDesktop || !slug)) {
      if (expandedCategory === autoExpandedCategory) {
        expandedCategory = '';
      }
      autoExpandedCategory = '';
    }
    lastPath = currentPath;
    lastDesktop = isDesktop;
  }

  export let open = false;

  $: if (browser && (isDesktop || open)) {
    loadData();
  }

  $: isSidebarHidden = !isDesktop && !open;

  function closeSidebar() {
    open = false;
  }

  function handleWindowKeydown(event: KeyboardEvent) {
    if (event.key === 'Escape' && open && !isDesktop) {
      closeSidebar();
    }
  }
</script>

<svelte:window on:keydown={handleWindowKeydown} />

<aside
  class="sidebar"
  class:open={open}
  id="site-navigation"
  aria-hidden={isSidebarHidden ? 'true' : undefined}
  inert={isSidebarHidden}
>
  <div class="sidebar-header">
    <h3>All Converters</h3>
    <button type="button" class="close-btn" on:click={closeSidebar} aria-label="Close sidebar">✕</button>
  </div>
  <nav aria-label="Calculator categories">
    {#each categoryUnitGroups as group}
      <div class="cat-section">
        <button
          type="button"
          class="cat-toggle"
          class:active={expandedCategory === group.key || currentPath.includes(`/category/${group.key}`)}
          on:click={() => toggle(group.key)}
          aria-expanded={expandedCategory === group.key}
          aria-controls={`cat-list-${group.key}`}
        >
          <span class="cat-icon">{group.meta.icon}</span>
          <span class="cat-label">{group.meta.label}</span>
          <span class="chevron" class:expanded={expandedCategory === group.key}>›</span>
        </button>
        {#if expandedCategory === group.key}
          <ul class="cat-list" id={`cat-list-${group.key}`}>
            {#each group.units as unit (unit.label)}
              <li class="unit-item">
                <button
                  type="button"
                  class="unit-toggle"
                  class:expanded={expandedUnits[group.key] === unit.label}
                  aria-expanded={expandedUnits[group.key] === unit.label}
                  on:click={() => toggleUnit(group.key, unit.label)}
                >
                  <span class="unit-label">{unit.label}</span>
                  <span class="chevron" class:expanded={expandedUnits[group.key] === unit.label}>›</span>
                </button>
                {#if expandedUnits[group.key] === unit.label}
                  <ul class="unit-list">
                    {#each unit.conversions as conversion}
                      <li>
                        <a
                          href="/{conversion.slug}"
                          class:current={currentPath === `/${conversion.slug}`}
                          aria-current={currentPath === `/${conversion.slug}` ? 'page' : undefined}
                        >
                          {conversion.name}
                        </a>
                      </li>
                    {/each}
                  </ul>
                {/if}
              </li>
            {/each}
            <li>
              <a
                href="/category/{group.key}"
                class="view-all"
                aria-current={currentPath === `/category/${group.key}` ? 'page' : undefined}
              >
                View all {group.meta.label} →
              </a>
            </li>
          </ul>
        {/if}
      </div>
    {/each}
  </nav>
</aside>

{#if open && !isDesktop}
  <button type="button" class="overlay" aria-label="Close sidebar" on:click={closeSidebar}></button>
{/if}

<style>
  .sidebar {
    flex: 0 0 280px;
    width: 280px;
    min-width: 280px;
    position: sticky;
    top: var(--header-h);
    height: calc(100vh - var(--header-h));
    overflow-y: auto;
    background: var(--sidebar-bg);
    border-right: 1px solid var(--border);
    padding: 0;
  }
  .sidebar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.25rem 1rem;
    border-bottom: 1px solid var(--border);
  }
  .sidebar-header h3 {
    margin: 0;
    font-size: 0.9rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--accent);
  }
  .close-btn {
    display: none;
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 1.2rem;
    cursor: pointer;
    padding: 0.25rem;
  }
  .close-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--accent-glow);
    border-radius: 8px;
  }

  nav {
    padding: 0.5rem 0;
  }

  .cat-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.6rem 1rem;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 0.88rem;
    color: var(--text);
    gap: 0.5rem;
    transition: background 0.15s;
    text-align: left;
  }
  .cat-toggle:hover {
    background: var(--hover-bg);
  }
  .cat-toggle:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--accent-glow);
  }
  .cat-toggle.active {
    color: var(--accent);
    font-weight: 600;
  }
  .cat-icon {
    font-size: 1rem;
    flex-shrink: 0;
    width: 1.4rem;
    text-align: center;
  }
  .cat-label {
    flex: 1;
  }
  .chevron {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-muted);
    transition: transform 0.2s;
  }
  .chevron.expanded {
    transform: rotate(90deg);
  }

  .cat-list {
    list-style: none;
    margin: 0;
    padding: 0 0 0.5rem;
  }
  .cat-list li a {
    display: block;
    padding: 0.35rem 1rem 0.35rem 2.8rem;
    font-size: 0.82rem;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.15s, background 0.15s;
    border-radius: 0;
  }
  .cat-list li a:hover {
    color: var(--accent);
    background: var(--hover-bg);
  }
  .cat-list li a:focus-visible {
    outline: none;
    color: var(--accent);
    background: var(--hover-bg);
  }
  .cat-list li a.current {
    color: var(--accent);
    font-weight: 600;
    background: var(--accent-glow);
  }
  .view-all {
    font-weight: 600 !important;
    color: var(--accent) !important;
  }

  .unit-item {
    margin: 0;
  }

  .unit-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.4rem 1rem 0.4rem 2.5rem;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 0.8rem;
    color: var(--text-muted);
    gap: 0.4rem;
    text-align: left;
    transition: color 0.15s, background 0.15s;
  }
  .unit-toggle:hover {
    background: var(--hover-bg);
  }
  .unit-toggle:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--accent-glow);
  }
  .unit-toggle.expanded {
    color: var(--accent);
    font-weight: 600;
  }
  .unit-label {
    flex: 1;
  }
  .unit-list {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .unit-list li a {
    display: block;
    padding: 0.25rem 1rem 0.25rem 3.4rem;
    font-size: 0.78rem;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.15s, background 0.15s;
    border-radius: 0;
  }
  .unit-list li a:hover {
    color: var(--accent);
    background: var(--hover-bg);
  }
  .unit-list li a:focus-visible {
    outline: none;
    color: var(--accent);
    background: var(--hover-bg);
  }

  .overlay {
    display: none;
    border: 0;
  }

  @media (max-width: 1024px) {
    .sidebar {
      position: fixed;
      top: 0;
      left: 0;
      z-index: 100;
      height: 100vh;
      transform: translateX(-100%);
      transition: transform 0.3s ease;
      will-change: transform;
      box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2);
    }
    .sidebar.open {
      transform: translateX(0);
    }
    .close-btn {
      display: block;
    }
    .overlay {
      display: block;
      position: fixed;
      inset: 0;
      z-index: 99;
      background: rgba(0, 0, 0, 0.5);
    }
  }
</style>
