<script lang="ts">
  import type { HudSignalPanel, SignalTone } from "$lib/types/hud";

  export let panel: HudSignalPanel;
  export let panelIndex = 0;

  const viewportWidth = 100;
  const viewportHeight = 36;

  const toneColorMap: Record<SignalTone, string> = {
    cyan: "62 232 255",
    lime: "133 255 68",
    orange: "255 91 63",
    violet: "171 118 255",
    gold: "255 206 84",
    rose: "255 108 176"
  };

  interface SeriesRenderShape {
    id: string;
    tone: SignalTone;
    linePath: string;
  }

  function clamp(value: number, min: number, max: number): number {
    return Math.min(max, Math.max(min, value));
  }

  function formatMetric(value: number | null): string {
    return value === null ? "--" : value.toFixed(1);
  }

  function resolveBounds(seriesCollection: HudSignalPanel["series"]): { min: number; max: number } {
    const allPoints = seriesCollection.flatMap((series) => series.points);

    if (allPoints.length === 0) {
      return { min: 0, max: 1 };
    }

    let min = Math.min(...allPoints);
    let max = Math.max(...allPoints);

    if (Math.abs(max - min) < 0.001) {
      const padding = Math.max(Math.abs(max) * 0.05, 1);
      min -= padding;
      max += padding;
    } else {
      const padding = Math.max((max - min) * 0.08, 0.5);
      min -= padding;
      max += padding;
    }

    return { min, max };
  }

  function convertPoints(rawPoints: number[], bounds: { min: number; max: number }): Array<{ x: number; y: number }> {
    if (!rawPoints.length) {
      return [];
    }

    if (rawPoints.length === 1) {
      return [{ x: viewportWidth / 2, y: viewportHeight / 2 }];
    }

    const stepX = viewportWidth / (rawPoints.length - 1);
    const span = Math.max(bounds.max - bounds.min, 1);

    return rawPoints.map((point, index) => ({
      x: Math.round(index * stepX * 100) / 100,
      y:
        Math.round(
          clamp(viewportHeight - ((point - bounds.min) / span) * viewportHeight, 1, viewportHeight - 1) * 100
        ) / 100
    }));
  }

  function createLinePath(rawPoints: number[], bounds: { min: number; max: number }): string {
    const points = convertPoints(rawPoints, bounds);
    if (!points.length) {
      return "";
    }

    return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`).join(" ");
  }

  $: bounds = resolveBounds(panel.series);
  $: renderedSeries = panel.series.map(
    (series): SeriesRenderShape => ({
      id: series.id,
      tone: series.tone,
      linePath: createLinePath(series.points, bounds)
    })
  );
  $: latestValue = formatMetric(panel.latest);
  $: minValue = formatMetric(panel.min);
  $: maxValue = formatMetric(panel.max);
</script>

<article
  class="signal-panel side-{panel.side}"
  class:is-active={panel.active}
  aria-hidden={!panel.active}
  style="--panel-index: {panelIndex};"
>
  <header class="panel-head">
    <div class="head-text">
      <p class="panel-code">{panel.code}</p>
      <p class="panel-title">{panel.title}</p>
    </div>

    <div class="icon-layer" aria-hidden="true">
      {#each panel.icons as icon (icon.id)}
        <span class="icon-chip tone-{icon.tone}">{icon.label}</span>
      {/each}
    </div>
  </header>

  <div class="chart-stage">
    <svg viewBox="0 0 {viewportWidth} {viewportHeight}" preserveAspectRatio="none" role="img" aria-label={panel.title}>
      <g class="grid-line-group">
        {#each [6, 12, 18, 24, 30] as y}
          <line x1="0" y1={y} x2={viewportWidth} y2={y}></line>
        {/each}
      </g>

      {#each renderedSeries as series (series.id)}
        {#if series.linePath}
          <path d={series.linePath} class="series-line tone-{series.tone}" />
        {/if}
      {/each}
    </svg>

    <div class="scan-haze" aria-hidden="true"></div>
  </div>

  <footer class="panel-foot">
    <p class="foot-item">
      <span class="dot tone-cyan"></span>
      <span class="metric-label">Now</span>
      <span class="value">{latestValue}</span>
    </p>
    <p class="foot-item">
      <span class="dot tone-lime"></span>
      <span class="metric-label">Max</span>
      <span class="value">{maxValue}</span>
    </p>
    <p class="foot-item">
      <span class="dot tone-orange"></span>
      <span class="metric-label">Min</span>
      <span class="value">{minValue}</span>
    </p>
  </footer>
</article>

<style>
  .signal-panel {
    --offset-x: 12%;
    --enter-ms: 1800ms;
    --fade-ms: 1000ms;
    overflow: hidden;
    inline-size: min(100%, clamp(16.8rem, 23vw, 22rem));
    aspect-ratio: 1.44 / 1;
    min-block-size: 11.8rem;
    justify-self: start;
    border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.42);
    border-radius: 0.92rem;
    padding: 0.56rem 0.62rem 0.58rem;
    background:
      linear-gradient(160deg, rgb(var(--hud-surface-alt-rgb) / 0.76) 0%, rgb(var(--hud-surface-rgb) / 0.62) 48%, rgb(var(--hud-surface-deep-rgb) / 0.76) 100%),
      radial-gradient(circle at 12% 0, rgb(var(--hud-glow-rgb) / 0.1), transparent 40%);
    box-shadow:
      inset 0 0 0 1px rgb(var(--hud-border-strong-rgb) / 0.08),
      inset 0 -24px 32px rgb(0 0 0 / 0.48),
      0 0 14px rgb(var(--hud-glow-rgb) / 0.14);
    opacity: 1;
    transform: translateX(0) scale(1) rotate(0);
    transition:
      opacity var(--fade-ms) cubic-bezier(0.18, 0.88, 0.3, 1),
      transform var(--enter-ms) cubic-bezier(0.2, 0.9, 0.28, 1),
      border-color 460ms ease,
      filter 760ms ease;
    transition-delay: calc(var(--panel-index) * 140ms);
  }

  .signal-panel.side-left {
    --offset-x: -132%;
  }

  .signal-panel.side-right {
    --offset-x: 132%;
    justify-self: end;
  }

  .signal-panel:not(.is-active) {
    border-color: transparent;
    opacity: 0;
    transform: translateX(var(--offset-x)) scale(0.98) rotate(-0.6deg);
    filter: blur(1.3px);
    pointer-events: none;
    transition-delay: 0ms;
  }

  .panel-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.4rem;
    margin-block-end: 0.4rem;
  }

  .head-text {
    min-width: 0;
  }

  .panel-code {
    margin: 0;
    font-size: 0.63rem;
    color: rgb(var(--hud-text-dim-rgb) / 0.88);
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .panel-title {
    margin: 0.12rem 0 0;
    font-size: 0.75rem;
    color: rgb(var(--hud-text-main-rgb) / 0.96);
    letter-spacing: 0.05em;
  }

  .icon-layer {
    display: flex;
    flex-wrap: wrap;
    gap: 0.26rem;
    justify-content: flex-end;
    align-items: center;
  }

  .icon-chip {
    border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.44);
    border-radius: 999px;
    padding: 0.08rem 0.36rem;
    font-size: 0.58rem;
    letter-spacing: 0.08em;
    color: rgb(var(--hud-text-main-rgb) / 0.94);
    background: rgb(var(--hud-surface-rgb) / 0.66);
  }

  .icon-chip.tone-cyan {
    border-color: rgb(var(--hud-cyan-rgb) / 0.54);
  }

  .icon-chip.tone-lime {
    border-color: rgb(var(--hud-lime-rgb) / 0.56);
  }

  .icon-chip.tone-orange {
    border-color: rgb(var(--hud-orange-rgb) / 0.58);
  }

  .icon-chip.tone-violet {
    border-color: rgb(171 118 255 / 0.58);
  }

  .icon-chip.tone-gold {
    border-color: rgb(255 206 84 / 0.58);
  }

  .icon-chip.tone-rose {
    border-color: rgb(255 108 176 / 0.58);
  }

  .chart-stage {
    position: relative;
    block-size: clamp(6.4rem, 9vw, 8.2rem);
    border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.32);
    border-radius: 0.62rem;
    overflow: hidden;
    background:
      linear-gradient(180deg, rgb(var(--hud-surface-alt-rgb) / 0.68), rgb(var(--hud-surface-deep-rgb) / 0.78)),
      radial-gradient(circle at 50% 0, rgb(var(--hud-glow-rgb) / 0.09), transparent 45%);
  }

  svg {
    display: block;
    inline-size: 100%;
    block-size: 100%;
  }

  .grid-line-group line {
    stroke: rgb(var(--hud-border-strong-rgb) / 0.16);
    stroke-width: 0.45;
  }

  .series-line {
    fill: none;
    stroke-width: 1.3;
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 0 2px rgb(0 0 0 / 0.42));
  }

  .series-line.tone-cyan {
    stroke: rgb(var(--hud-cyan-rgb) / 0.95);
  }

  .series-line.tone-lime {
    stroke: rgb(var(--hud-lime-rgb) / 0.94);
  }

  .series-line.tone-orange {
    stroke: rgb(var(--hud-orange-rgb) / 0.94);
  }

  .series-line.tone-violet {
    stroke: rgb(171 118 255 / 0.94);
  }

  .series-line.tone-gold {
    stroke: rgb(255 206 84 / 0.94);
  }

  .series-line.tone-rose {
    stroke: rgb(255 108 176 / 0.94);
  }

  .scan-haze {
    position: absolute;
    inset: 0;
    background:
      repeating-linear-gradient(
        180deg,
        rgb(var(--hud-border-strong-rgb) / 0.04) 0,
        rgb(var(--hud-border-strong-rgb) / 0.04) 1px,
        transparent 1px,
        transparent 3px
      ),
      linear-gradient(180deg, transparent 0%, rgb(var(--hud-glow-rgb) / 0.06) 50%, transparent 100%);
    background-size: 100% 100%, 100% 100%;
    mix-blend-mode: screen;
    pointer-events: none;
  }

  .panel-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 0.8rem;
    margin: 0.4rem 0 0;
    padding: 0;
  }

  .foot-item {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: rgb(var(--hud-text-main-rgb) / 0.9);
    font-size: 0.62rem;
    letter-spacing: 0.04em;
  }

  .dot {
    inline-size: 0.34rem;
    block-size: 0.34rem;
    border-radius: 50%;
  }

  .dot.tone-cyan {
    background: rgb(var(--hud-cyan-rgb));
  }

  .dot.tone-lime {
    background: rgb(var(--hud-lime-rgb));
  }

  .dot.tone-orange {
    background: rgb(var(--hud-orange-rgb));
  }

  .metric-label {
    color: rgb(var(--hud-text-dim-rgb) / 0.82);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }

  .value {
    min-inline-size: 2.4rem;
  }

  @media (max-width: 1180px) {
    .signal-panel {
      inline-size: min(100%, clamp(14rem, 30vw, 17rem));
      aspect-ratio: 1.5 / 1;
      min-block-size: 10.1rem;
    }
  }

  @media (max-height: 900px) {
    .signal-panel {
      inline-size: min(100%, clamp(15rem, 22vw, 18.5rem));
      min-block-size: 10.6rem;
    }

    .chart-stage {
      block-size: clamp(5.7rem, 7.6vw, 6.9rem);
    }
  }

  @media (max-height: 760px) {
    .signal-panel {
      inline-size: min(100%, clamp(13.8rem, 20vw, 16.5rem));
      min-block-size: 9.8rem;
      padding: 0.46rem 0.5rem 0.5rem;
    }

    .panel-foot {
      margin-block-start: 0.28rem;
    }

    .chart-stage {
      block-size: clamp(5rem, 6.6vw, 6rem);
    }
  }

  @media (max-height: 680px) {
    .signal-panel {
      inline-size: min(100%, clamp(12.8rem, 18vw, 15rem));
      min-block-size: 8.7rem;
      padding: 0.4rem 0.46rem 0.44rem;
    }

    .panel-head {
      margin-block-end: 0.26rem;
    }

    .panel-foot {
      margin-block-start: 0.18rem;
      gap: 0.56rem;
    }

    .chart-stage {
      block-size: clamp(4.4rem, 5.6vw, 5.4rem);
    }
  }

  @media (max-width: 900px) {
    .signal-panel {
      inline-size: 100%;
      aspect-ratio: 1.7 / 1;
      min-block-size: 0;
    }
  }
</style>
