<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'

import { buildMonitorEcgPath } from './monitorEcg'

type BeatRangeMs = {
  min: number
  max: number
}

let patternIdCounter = 0

const props = withDefaults(defineProps<{
  mode: 'ready' | 'processing'
  width?: number
  height?: number
  ariaLabel?: string
  beatEnabled?: boolean
  beatRangeMs?: BeatRangeMs
  random?: () => number
}>(), {
  width: 120,
  height: 30,
  ariaLabel: 'System monitor ECG',
  beatEnabled: true,
  beatRangeMs: undefined,
  random: undefined,
})

const randomFn = computed(() => props.random ?? Math.random)

const totalWidth = computed(() => props.width * 2)
const waveformPath = computed(() => buildMonitorEcgPath({
  segmentWidth: props.width,
  height: props.height,
  beatsPerSegment: props.mode === 'processing' ? 3 : 2,
  segments: 2,
  samplesPerBeat: 96,
}))

const styleVars = computed(() => ({
  '--monitor-ecg-width': `${props.width}px`,
  '--monitor-ecg-height': `${props.height}px`,
  '--monitor-ecg-duration': props.mode === 'processing' ? '1.6s' : '3s',
  '--monitor-ecg-stroke-opacity': props.mode === 'processing' ? '1' : '0.72',
  '--monitor-ecg-glow-opacity': props.mode === 'processing' ? '0.9' : '0.45',
  '--monitor-ecg-grid-opacity': props.mode === 'processing' ? '0.26' : '0.16',
}))

const patternId = `monitor-ecg-grid-${patternIdCounter += 1}`

const beatPulse = ref(0)
const beatLift = ref(0)

const beatStyle = computed(() => ({
  transform: `translateY(${beatLift.value}px) scaleY(${1 + beatPulse.value})`,
}))

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

function resolveBeatRange(mode: 'ready' | 'processing', override?: BeatRangeMs): BeatRangeMs {
  if (override) {
    return {
      min: clamp(override.min, 1, 60000),
      max: clamp(override.max, clamp(override.min, 1, 60000), 60000),
    }
  }

  if (mode === 'processing') {
    return { min: 420, max: 980 }
  }

  return { min: 780, max: 1600 }
}

let beatTimer: ReturnType<typeof setTimeout> | null = null
let beatPhaseTimer: ReturnType<typeof setTimeout> | null = null

function clearBeatTimers() {
  if (beatTimer) {
    clearTimeout(beatTimer)
    beatTimer = null
  }

  if (beatPhaseTimer) {
    clearTimeout(beatPhaseTimer)
    beatPhaseTimer = null
  }
}

function scheduleNextBeat() {
  if (!props.beatEnabled) return

  const range = resolveBeatRange(props.mode, props.beatRangeMs)
  const mix = randomFn.value()
  const delay = range.min + (range.max - range.min) * clamp(mix, 0, 1)

  beatTimer = setTimeout(() => {
    const strengthBase = props.mode === 'processing' ? 0.12 : 0.07
    const strengthJitter = props.mode === 'processing' ? 0.06 : 0.04
    const strength = strengthBase + strengthJitter * clamp(randomFn.value(), 0, 1)

    beatPulse.value = strength
    beatLift.value = -(1.4 + 1.6 * clamp(randomFn.value(), 0, 1))

    beatPhaseTimer = setTimeout(() => {
      beatPulse.value = Math.min(strength * 0.25, 0.05)
      beatLift.value = 0.6

      beatPhaseTimer = setTimeout(() => {
        beatPulse.value = 0
        beatLift.value = 0
      }, 110)
    }, 120)

    scheduleNextBeat()
  }, delay)
}

watch(() => props.mode, () => {
  clearBeatTimers()
  scheduleNextBeat()
})

watch(() => props.beatEnabled, (enabled) => {
  clearBeatTimers()
  if (enabled) scheduleNextBeat()
})

onMounted(() => {
  scheduleNextBeat()
})

onUnmounted(() => {
  clearBeatTimers()
})
</script>

<template>
  <div
    class="monitor-ecg-strip"
    :class="`mode-${mode}`"
    :style="styleVars"
    data-testid="monitor-ecg-strip"
  >
    <svg
      class="monitor-ecg-svg"
      :viewBox="`0 0 ${totalWidth} ${height}`"
      preserveAspectRatio="none"
      role="img"
      :aria-label="ariaLabel"
      data-testid="monitor-ecg-svg"
    >
      <defs>
        <pattern
          :id="patternId"
          patternUnits="userSpaceOnUse"
          width="10"
          height="10"
        >
          <path d="M 10 0 L 0 0 0 10" class="monitor-ecg-grid-line" />
        </pattern>
      </defs>
      <rect class="monitor-ecg-grid" :width="totalWidth" :height="height" :fill="`url(#${patternId})`" />
      <g class="monitor-ecg-track" :style="beatStyle">
        <path class="monitor-ecg-glow" :d="waveformPath" />
        <path class="monitor-ecg-line" :d="waveformPath" data-testid="monitor-ecg-line" />
      </g>
    </svg>
  </div>
</template>

<style scoped>
.monitor-ecg-strip {
  width: var(--monitor-ecg-width);
  height: var(--monitor-ecg-height);
  overflow: hidden;
  margin-left: 8px;
  border-radius: 4px;
  background:
    linear-gradient(180deg, rgba(0, 245, 212, 0.06), rgba(0, 245, 212, 0.015)),
    rgba(0, 245, 212, 0.03);
  border: 1px solid rgba(0, 245, 212, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
}

.monitor-ecg-svg {
  width: 100%;
  height: 100%;
  display: block;
}

.monitor-ecg-grid {
  opacity: 1;
}

.monitor-ecg-grid-line {
  fill: none;
  stroke: rgba(0, 245, 212, var(--monitor-ecg-grid-opacity));
  stroke-width: 0.4;
}

.monitor-ecg-track {
  transform-origin: center;
  transform-box: fill-box;
  animation: monitor-ecg-scroll var(--monitor-ecg-duration) linear infinite;
  will-change: transform;
}

.monitor-ecg-glow,
.monitor-ecg-line {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.monitor-ecg-glow {
  stroke: rgba(0, 245, 212, var(--monitor-ecg-glow-opacity));
  stroke-width: 2.8;
  filter: blur(1.8px);
}

.monitor-ecg-line {
  stroke: rgba(0, 245, 212, var(--monitor-ecg-stroke-opacity));
  stroke-width: 1.35;
  filter: drop-shadow(0 0 4px rgba(0, 245, 212, 0.55));
}

.mode-processing {
  border-color: rgba(0, 245, 212, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 0 12px rgba(0, 245, 212, 0.12);
}

@keyframes monitor-ecg-scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .monitor-ecg-track {
    animation-duration: calc(var(--monitor-ecg-duration) * 2.2);
  }
}
</style>
