<script lang="ts">
  /**
   * Load and inspect a profile: bundled samples, a file picker for GPX/YAML,
   * and a small editor that previews the YAML as you type (§5.5, §5.6, FR-6.7).
   *
   * Parsing and preview both happen in Rust — the editor sends text and gets a
   * `ProfileView` back, so what you see here is exactly what the ride engine
   * will do with it.
   */
  import { open } from '@tauri-apps/plugin-dialog';
  import { app } from '../lib/app.svelte';
  import { api } from '../lib/bridge';
  import { axisValue } from '../lib/format';
  import type { ProfileView } from '../lib/types';

  let yaml = $state('');
  let preview = $state<ProfileView | null>(null);
  let error = $state<string | null>(null);
  let debounce: ReturnType<typeof setTimeout> | null = null;

  const loaded = $derived(app.ride?.profile ?? null);

  function close() {
    app.showProfiles = false;
  }

  async function refreshPreview(text: string) {
    if (!text.trim()) {
      preview = null;
      error = null;
      return;
    }
    try {
      preview = await api.previewYaml(text);
      error = null;
    } catch (e) {
      preview = null;
      error = String(e);
    }
  }

  function onEdit(text: string) {
    yaml = text;
    if (debounce) clearTimeout(debounce);
    debounce = setTimeout(() => refreshPreview(text), 220);
  }

  async function pickFile() {
    const path = await open({
      multiple: false,
      filters: [
        { name: 'Route or profile', extensions: ['gpx', 'yaml', 'yml'] },
        { name: 'All files', extensions: ['*'] },
      ],
    });
    if (typeof path === 'string') {
      const view = await app.run(() => api.loadProfilePath(path));
      if (view) {
        yaml = view.yaml;
        preview = view;
        error = null;
      }
    }
  }

  async function loadSample(name: string, text: string, isGpx: boolean) {
    const view = await app.run(() => api.loadProfileText(name, text, isGpx));
    if (view) {
      yaml = view.yaml;
      preview = view;
      error = null;
    }
  }

  async function loadEdited() {
    if (!yaml.trim()) return;
    const view = await app.run(() => api.loadProfileText('Edited profile', yaml, false));
    if (view) preview = view;
  }

  function sparkline(view: ProfileView): string {
    const pairs = view.elevation ?? view.series;
    if (!pairs || pairs.length < 2) return '';
    const step = Math.max(1, Math.floor(pairs.length / 220));
    const pts: [number, number][] = [];
    for (let i = 0; i < pairs.length; i += step) pts.push(pairs[i]);
    const xs = pts.map((p) => p[0]);
    const ys = pts.map((p) => p[1]);
    const x0 = Math.min(...xs);
    const x1 = Math.max(...xs);
    const y0 = Math.min(...ys);
    const y1 = Math.max(...ys);
    const sx = (v: number) => ((v - x0) / (x1 - x0 || 1)) * 100;
    const sy = (v: number) => 30 - ((v - y0) / (y1 - y0 || 1)) * 28;
    return `M ${pts.map((p) => `${sx(p[0]).toFixed(2)} ${sy(p[1]).toFixed(2)}`).join(' L ')}`;
  }
</script>

<div
  class="scrim"
  role="button"
  tabindex="-1"
  onclick={close}
  onkeydown={(e) => e.key === 'Escape' && close()}
></div>

<aside class="drawer">
  <header>
    <h2>Profiles &amp; routes</h2>
    <button class="btn ghost" onclick={close}>Close <span class="kbd">Esc</span></button>
  </header>

  <div class="body">
    <section class="pick">
      <div class="row-head">
        <span class="label">Load</span>
        <button class="btn" onclick={pickFile}>Open GPX or YAML…</button>
      </div>

      <ul class="samples">
        {#each app.samples as s (s.name)}
          <li>
            <button class="sample" onclick={() => loadSample(s.name, s.text, s.isGpx)}>
              <span class="sample-name">
                {s.name}
                {#if s.isGpx}<span class="tag">GPX</span>{/if}
              </span>
              <span class="sample-sub">{s.summary}</span>
            </button>
          </li>
        {/each}
      </ul>

      {#if loaded}
        <div class="active">
          <span class="label">Loaded</span>
          <strong>{loaded.name}</strong>
          <span class="sample-sub">
            {loaded.totalMetres ? `${(loaded.totalMetres / 1000).toFixed(1)} km` : ''}
            {loaded.totalSeconds ? `${Math.round(loaded.totalSeconds / 60)} min` : ''}
            {loaded.totalAscentM != null ? `· ${loaded.totalAscentM.toFixed(0)} m up` : ''}
            {loaded.looping ? '· loops' : ''}
          </span>
          <button class="btn ghost danger" onclick={() => app.run(() => api.clearProfile())}>
            Unload
          </button>
        </div>
      {/if}
    </section>

    <section class="edit">
      <div class="row-head">
        <span class="label">Editor — YAML profile</span>
        <button class="btn" disabled={!preview} onclick={loadEdited}>Load into ride</button>
      </div>

      <textarea
        spellcheck="false"
        placeholder={'name: My profile\nblocks:\n  - type: constant\n    channel: gradient\n    value: 4.0\n    extent: { seconds: 600 }'}
        value={yaml}
        oninput={(e) => onEdit(e.currentTarget.value)}
      ></textarea>

      {#if error}
        <p class="err">{error}</p>
      {:else if preview}
        <div class="preview">
          <svg viewBox="0 0 100 32" preserveAspectRatio="none">
            <path d={sparkline(preview)} />
          </svg>
          <div class="blocks">
            {#each preview.blocks as b (b.index)}
              <span class="block">
                <em>{b.kind}</em>
                {b.label}
                <span class="quiet">
                  {axisValue(b.unit, b.endX - b.startX)}
                </span>
              </span>
            {/each}
          </div>
        </div>
      {/if}
    </section>
  </div>
</aside>

<style>
  .scrim {
    position: fixed;
    inset: 0;
    background: rgba(2, 4, 7, 0.72);
    backdrop-filter: blur(2px);
    z-index: 20;
  }

  .drawer {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(56rem, 94vw);
    z-index: 21;
    display: grid;
    grid-template-rows: auto 1fr;
    background: var(--bg);
    border-left: 1px solid var(--hairline);
  }

  header {
    display: flex;
    align-items: center;
    gap: var(--gap);
    padding: 1.1rem var(--edge) 0.9rem;
    border-bottom: 1px solid var(--hairline);
  }

  h2 {
    margin: 0;
    font-size: 1.3rem;
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  header .btn {
    margin-left: auto;
  }

  .body {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--edge);
    padding: 1.1rem var(--edge) 1.4rem;
    overflow: hidden;
    min-height: 0;
  }

  section {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    min-height: 0;
  }

  .row-head {
    display: flex;
    align-items: center;
    gap: var(--gap);
  }

  .row-head .btn {
    margin-left: auto;
  }

  .samples {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    min-height: 0;
  }

  .sample {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    width: 100%;
    text-align: left;
    padding: 0.7rem 0.6rem;
    border-radius: 0.45rem;
    transition: background 120ms ease;
  }

  .sample:hover {
    background: var(--bg-lift);
  }

  .sample-name {
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .tag {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    padding: 0.15em 0.45em;
    border-radius: 0.25rem;
    background: rgba(69, 208, 255, 0.14);
    color: var(--route);
  }

  .sample-sub {
    font-size: 0.82rem;
    color: var(--ink-dim);
  }

  .active {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.8rem 0.6rem;
    border-top: 1px solid var(--hairline);
  }

  .active .btn {
    align-self: flex-start;
    margin-top: 0.35rem;
  }

  textarea {
    flex: 1;
    min-height: 12rem;
    resize: none;
    padding: 0.8rem 0.9rem;
    border-radius: 0.5rem;
    border: 1px solid var(--hairline);
    background: #080b10;
    color: var(--ink);
    font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
    font-size: 0.84rem;
    line-height: 1.6;
    tab-size: 2;
  }

  textarea:focus {
    outline: none;
    border-color: #23394a;
  }

  .err {
    margin: 0;
    color: var(--bad);
    font-size: 0.85rem;
    font-family: ui-monospace, monospace;
  }

  .preview {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .preview svg {
    width: 100%;
    height: 4.5rem;
  }

  .preview path {
    fill: none;
    stroke: var(--route);
    stroke-width: 0.8;
    vector-effect: non-scaling-stroke;
  }

  .blocks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    max-height: 6rem;
    overflow-y: auto;
  }

  .block {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.28em 0.6em;
    border-radius: 0.35rem;
    background: var(--bg-lift);
    font-size: 0.78rem;
    color: var(--ink-soft);
  }

  .block em {
    font-style: normal;
    font-weight: 700;
    color: var(--ink-dim);
    text-transform: uppercase;
    font-size: 0.68rem;
    letter-spacing: 0.08em;
  }

  .quiet {
    color: var(--ink-faint);
  }

  /* One column below 900 px, from the measured size class rather than a
     `max-width` of its own — see ui/src/lib/viewport.ts. */
  :global([data-size='compact']) .body,
  :global([data-size='medium']) .body {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }
</style>
