/**
 * Video playback controls — manages the <video> element, seek bar,
 * volume slider, play/pause, fullscreen, and video overlay.
 */
import { api } from './api';
import {
  library, currentIndex, suppressTick,
  setSuppressTick, setSeeking, seeking,
  clamp, fmtTime, cb, currentItem, computeResumeTime,
} from './store';

// ---- DOM refs ----
let player: HTMLVideoElement;
let seek: HTMLInputElement;
let seekFill: HTMLElement;
let volSlider: HTMLInputElement & { dragging?: boolean };
let volFill: HTMLElement;
let volTooltip: HTMLElement;
let volMuteBtn: HTMLElement;
let volIcon: HTMLElement;
let playPauseBtn: HTMLElement;
let ppIcon: HTMLElement;
let prevBtn: HTMLElement;
let nextBtn: HTMLElement;
let fsBtn: HTMLElement;
let pipBtn: HTMLElement;
let pipIcon: HTMLElement;
let timeNow: HTMLElement;
let timeDur: HTMLElement;
let speedBtn: HTMLElement;
let speedBtnText: HTMLElement;
let speedIcon: SVGElement;
let speedMenu: HTMLElement;
let videoOverlay: HTMLElement;
let overlayIcon: HTMLElement;
let overlayIconI: HTMLElement;
let seekFeedbackEl: HTMLElement;
let errorOverlay: HTMLElement;
let errorNextBtn: HTMLElement;

// ---- Mute state ----
let muted = false;
let lastVolume = 1.0;

// ---- Seek feedback state ----
let seekFeedbackTimer: ReturnType<typeof setTimeout> | null = null;
let seekAccum = 0;

// ---- Double-click state ----
let clickTimer: ReturnType<typeof setTimeout> | null = null;

export function getPlayer(): HTMLVideoElement { return player; }

export function initPlayer(): void {
  player = document.getElementById('player') as HTMLVideoElement;
  seek = document.getElementById('seek') as HTMLInputElement;
  seekFill = document.getElementById('seekFill')!;
  volSlider = document.getElementById('volSlider') as HTMLInputElement & { dragging?: boolean };
  volFill = document.getElementById('volFill')!;
  volTooltip = document.getElementById('volTooltip')!;
  volMuteBtn = document.getElementById('volMuteBtn')!;
  volIcon = document.getElementById('volIcon')!;
  playPauseBtn = document.getElementById('playPauseBtn')!;
  ppIcon = document.getElementById('ppIcon')!;
  prevBtn = document.getElementById('prevBtn')!;
  nextBtn = document.getElementById('nextBtn')!;
  fsBtn = document.getElementById('fsBtn')!;
  pipBtn = document.getElementById('pipBtn')!;
  pipIcon = document.getElementById('pipIcon')!;
  timeNow = document.getElementById('timeNow')!;
  timeDur = document.getElementById('timeDur')!;
  speedBtn = document.getElementById('speedBtn')!;
  speedBtn.setAttribute('aria-haspopup', 'true');
  speedBtn.setAttribute('aria-expanded', 'false');
  speedBtnText = document.getElementById('speedBtnText')!;
  speedIcon = document.getElementById('speedIcon') as unknown as SVGElement;
  speedMenu = document.getElementById('speedMenu')!;
  videoOverlay = document.getElementById('videoOverlay')!;
  overlayIcon = document.getElementById('overlayIcon')!;
  overlayIconI = document.getElementById('overlayIconI')!;
  seekFeedbackEl = document.getElementById('seekFeedback')!;
  errorOverlay = document.getElementById('errorOverlay')!;
  errorNextBtn = document.getElementById('errorNextBtn')!;

  // --- Play/Pause ---
  playPauseBtn.onclick = togglePlay;

  // --- Prev / Next ---
  prevBtn.onclick = () => nextPrev(-1);
  nextBtn.onclick = () => nextPrev(+1);

  // --- Fullscreen ---
  fsBtn.onclick = () => toggleFullscreen();

  // --- Picture-in-Picture ---
  if (pipBtn) {
    if (!document.pictureInPictureEnabled) {
      pipBtn.style.display = 'none';
    } else {
      pipBtn.onclick = async () => {
        try {
          if (document.pictureInPictureElement) await document.exitPictureInPicture();
          else await player.requestPictureInPicture();
        } catch (_) {}
      };
      player.addEventListener('enterpictureinpicture', () => {
        pipIcon.className = 'fa-solid fa-down-left-and-up-right-to-center';
        pipBtn.setAttribute('aria-label', 'Exit picture-in-picture');
      });
      player.addEventListener('leavepictureinpicture', () => {
        pipIcon.className = 'fa-solid fa-up-right-from-square';
        pipBtn.setAttribute('aria-label', 'Enter picture-in-picture');
      });
    }
  }

  // --- Mute button ---
  if (volMuteBtn) {
    volMuteBtn.onclick = (e) => { e.stopPropagation(); toggleMute(); };
  }

  // --- Video error state ---
  player.addEventListener('error', () => {
    if (errorOverlay) errorOverlay.style.display = 'flex';
  });
  if (errorNextBtn) {
    errorNextBtn.onclick = () => nextPrev(+1);
  }

  // --- Seek bar ---
  seek.addEventListener('input', () => {
    setSeeking(true);
    const d = player.duration || 0;
    if (d > 0) {
      const t = (Number(seek.value) / 1000) * d;
      timeNow.textContent = fmtTime(t);
    }
    updateSeekFill();
  });

  seek.addEventListener('change', () => {
    const d = player.duration || 0;
    if (d > 0) {
      const t = (Number(seek.value) / 1000) * d;
      try { player.currentTime = t; } catch (_) {}
    }
    setSeeking(false);
    updateSeekFill();
  });

  // --- Video events ---
  player.addEventListener('timeupdate', () => {
    if (!seeking) {
      const d = player.duration || 0, t = player.currentTime || 0;
      if (d > 0) seek.value = String(Math.round((t / d) * 1000));
      updateSeekFill();
      updateTimeReadout();
      // Update active row's mini progress bar in real time
      const activeRow = document.querySelector('.row.active');
      if (activeRow && d > 0) {
        const bar = activeRow.querySelector('.rowProgress') as HTMLElement;
        if (bar) bar.style.width = clamp((t / d) * 100, 0, 100) + '%';
      }
    }
    cb.updateInfoPanel?.();
  });

  player.addEventListener('loadedmetadata', async () => {
    const d = player.duration || 0;
    timeDur.textContent = d ? fmtTime(d) : '00:00';
    if (errorOverlay) errorOverlay.style.display = 'none';
    await cb.refreshCurrentVideoMeta?.();
  });

  player.addEventListener('ended', () => nextPrev(+1));
  player.addEventListener('play', () => { updatePlayPauseIcon(); updateVideoOverlay(); });
  player.addEventListener('pause', () => { updatePlayPauseIcon(); updateVideoOverlay(); });

  // --- Volume slider ---
  updateVolFill();

  volSlider.addEventListener('input', () => {
    const v = clamp(Number(volSlider.value || 1.0), 0, 1);
    if (muted && v > 0) { muted = false; }
    setSuppressTick(true); player.volume = v; setSuppressTick(false);
    if (library) (library as any).folder_volume = v;
    cb.updateInfoPanel?.();
    updateVolFill();
    updateVolumeIcon();
    // Update tooltip position
    if (volTooltip && volTooltip.classList.contains('show')) {
      volTooltip.textContent = Math.round(v * 100) + '%';
      const sliderWidth = volSlider.offsetWidth;
      const thumbRadius = 7;
      const trackRange = sliderWidth - thumbRadius * 2;
      const thumbCenter = thumbRadius + v * trackRange;
      volTooltip.style.left = (10 + 14 + 10 + 8 + thumbCenter) + 'px';
    }
  });

  const showVolTooltip = () => {
    volSlider.dragging = true;
    if (volTooltip) {
      const v = clamp(Number(volSlider.value || 1.0), 0, 1);
      volTooltip.textContent = Math.round(v * 100) + '%';
      const sliderWidth = volSlider.offsetWidth;
      const thumbRadius = 7;
      const trackRange = sliderWidth - thumbRadius * 2;
      const thumbCenter = thumbRadius + v * trackRange;
      volTooltip.style.left = (10 + 14 + 10 + 8 + thumbCenter) + 'px';
      volTooltip.classList.add('show');
    }
  };
  volSlider.addEventListener('mousedown', showVolTooltip);
  volSlider.addEventListener('touchstart', showVolTooltip);

  volSlider.addEventListener('change', async () => {
    volSlider.dragging = false;
    if (volTooltip) volTooltip.classList.remove('show');
    updateVolFill();
    if (!library) return;
    try {
      const v = clamp(Number(volSlider.value || 1.0), 0, 1);
      await api.setFolderVolume(v);
      (library as any).folder_volume = v;
      cb.updateInfoPanel?.();
    } catch (_) {}
  });

  const hideVolTooltip = () => {
    volSlider.dragging = false;
    if (volTooltip) volTooltip.classList.remove('show');
  };
  window.addEventListener('mouseup', hideVolTooltip);
  window.addEventListener('touchend', hideVolTooltip);

  // --- Speed menu ---
  speedBtn.addEventListener('click', (e) => {
    e.preventDefault(); e.stopPropagation();
    if (speedMenu.classList.contains('show')) closeSpeedMenu();
    else {
      openSpeedMenu();
      const first = speedMenu.querySelector('[role="menuitem"]') as HTMLElement | null;
      if (first) first.focus();
    }
  });
  speedBtn.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') { closeSpeedMenu(); speedBtn.focus(); }
  });
  window.addEventListener('click', () => { closeSpeedMenu(); });
  speedMenu.addEventListener('click', (e) => e.stopPropagation());

  // --- Video overlay (single-click = play/pause, double-click = fullscreen) ---
  if (videoOverlay) {
    const videoWrap = videoOverlay.parentElement!;
    videoWrap.addEventListener('mouseenter', () => {
      if (!player.paused && overlayIcon) {
        overlayIconI.className = 'fa-solid fa-pause';
        overlayIcon.classList.add('pause');
        overlayIcon.classList.add('show');
      }
    });
    videoWrap.addEventListener('mouseleave', () => {
      if (!player.paused && overlayIcon) {
        overlayIcon.classList.remove('show');
      }
    });
    videoOverlay.style.pointerEvents = 'auto';
    videoOverlay.style.cursor = 'pointer';
    videoOverlay.addEventListener('click', () => {
      if (clickTimer) {
        clearTimeout(clickTimer);
        clickTimer = null;
        toggleFullscreen();
        return;
      }
      clickTimer = setTimeout(() => {
        clickTimer = null;
        if (player.paused) player.play();
        else player.pause();
        overlayIcon.classList.add('pulse');
        setTimeout(() => overlayIcon.classList.remove('pulse'), 400);
      }, 250);
    });
  }

  setTimeout(updateVideoOverlay, 100);
}

// ---- Exported functions ----

export function togglePlay(): void {
  try {
    if (player.paused || player.ended) player.play();
    else player.pause();
  } catch (_) {}
  updatePlayPauseIcon();
}

export function nextPrev(delta: number): void {
  if (!library || !library.items || library.items.length === 0) return;
  const newIdx = Math.max(0, Math.min(currentIndex + delta, library.items.length - 1));
  const it = library.items[newIdx];
  const auto = !!library.folder_autoplay;
  cb.loadIndex?.(newIdx, computeResumeTime(it), !auto, auto);
}

export function updatePlayPauseIcon(): void {
  if (!ppIcon) return;
  ppIcon.className = (player.paused || player.ended) ? 'fa-solid fa-play' : 'fa-solid fa-pause';
  playPauseBtn.setAttribute('aria-label', (player.paused || player.ended) ? 'Play' : 'Pause');
}

export function updateTimeReadout(): void {
  const t = player.currentTime || 0, d = player.duration || 0;
  timeNow.textContent = fmtTime(t);
  timeDur.textContent = d ? fmtTime(d) : '00:00';
}

export function updateSeekFill(): void {
  if (seekFill) {
    const pct = (Number(seek.value) / 1000) * 100;
    seekFill.style.width = pct + '%';
  }
}

export function updateVolFill(): void {
  if (volFill && volSlider) {
    const pct = clamp(Number(volSlider.value || 1.0), 0, 1) * 100;
    volFill.style.width = pct + '%';
  }
}

export function updateVideoOverlay(): void {
  if (!overlayIcon || !overlayIconI) return;
  if (player.paused) {
    overlayIconI.className = 'fa-solid fa-play';
    overlayIcon.classList.remove('pause');
    overlayIcon.classList.add('show');
  } else {
    overlayIcon.classList.remove('show');
  }
}

export function setVolume(vol: number): void {
  const v = clamp(vol, 0, 1);
  player.volume = v;
  volSlider.value = String(v);
  updateVolFill();
}

export function setPlaybackRate(rate: number): void {
  const r = clamp(rate, 0.25, 3);
  player.playbackRate = r;
  speedBtnText.textContent = `${r.toFixed(2)}x`;
  updateSpeedIcon(r);
}

export function getVideoTime(): number { return player?.currentTime || 0; }
export function getVideoDuration(): number | null {
  return player && Number.isFinite(player.duration) ? player.duration : null;
}
export function isPlaying(): boolean { return player ? !player.paused && !player.ended : false; }
export function isVolDragging(): boolean { return !!volSlider?.dragging; }

// ---- Mute ----

export function toggleMute(): void {
  if (muted) {
    muted = false;
    const v = lastVolume > 0 ? lastVolume : 1.0;
    player.volume = v;
    volSlider.value = String(v);
  } else {
    lastVolume = player.volume || 1.0;
    muted = true;
    player.volume = 0;
    volSlider.value = '0';
  }
  updateVolFill();
  updateVolumeIcon();
  if (library) (library as any).folder_volume = player.volume;
  cb.updateInfoPanel?.();
}

export function updateVolumeIcon(): void {
  if (!volIcon || !volMuteBtn) return;
  const v = player.volume;
  if (muted || v === 0) {
    volIcon.className = 'fa-solid fa-volume-xmark';
    volMuteBtn.setAttribute('aria-label', 'Unmute');
    volMuteBtn.closest('.miniCtl')?.classList.add('muted');
  } else if (v < 0.5) {
    volIcon.className = 'fa-solid fa-volume-low';
    volMuteBtn.setAttribute('aria-label', 'Mute');
    volMuteBtn.closest('.miniCtl')?.classList.remove('muted');
  } else {
    volIcon.className = 'fa-solid fa-volume-high';
    volMuteBtn.setAttribute('aria-label', 'Mute');
    volMuteBtn.closest('.miniCtl')?.classList.remove('muted');
  }
}

// ---- Fullscreen ----

export async function toggleFullscreen(): Promise<void> {
  try {
    if (document.fullscreenElement) await document.exitFullscreen();
    else await player.requestFullscreen();
  } catch (_) {}
}

// ---- Seek feedback ----

export function showSeekFeedback(delta: number): void {
  seekAccum += delta;
  const sign = seekAccum >= 0 ? '+' : '\u2212';
  seekFeedbackEl.textContent = `${sign}${Math.abs(seekAccum)}s`;
  seekFeedbackEl.classList.add('show');
  if (seekFeedbackTimer) clearTimeout(seekFeedbackTimer);
  seekFeedbackTimer = setTimeout(() => {
    seekFeedbackEl.classList.remove('show');
    seekAccum = 0;
  }, 600);
}

// ---- Speed cycle ----

export function cycleSpeed(delta: number): void {
  const current = player.playbackRate;
  let idx = SPEEDS.findIndex(s => Math.abs(s - current) < 0.01);
  if (idx === -1) idx = SPEEDS.indexOf(1.0);
  idx = clamp(idx + delta, 0, SPEEDS.length - 1);
  const r = SPEEDS[idx];
  player.playbackRate = r;
  if (library) (library as any).folder_rate = r;
  speedBtnText.textContent = `${r.toFixed(2)}x`;
  updateSpeedIcon(r);
  buildSpeedMenu(r);
  cb.updateInfoPanel?.();
  cb.notify?.(`Speed: ${r}x`);
  setSuppressTick(true);
  if (library) { api.setFolderRate(r).catch(() => {}).finally(() => setSuppressTick(false)); }
  else { setSuppressTick(false); }
}

/** Load a video by index and handle the onloadedmetadata callback. */
export async function loadVideoSrc(
  idx: number,
  timecode: number = 0,
  pauseAfterLoad: boolean = true,
  autoplayOnLoad: boolean = false,
  onReady?: () => Promise<void>,
): Promise<void> {
  if (!library || !library.items || library.items.length === 0) return;
  idx = Math.max(0, Math.min(idx, library.items.length - 1));

  const keepVol = clamp(Number(library.folder_volume ?? player.volume ?? 1.0), 0, 1);
  const keepRate = clamp(Number(library.folder_rate ?? player.playbackRate ?? 1.0), 0.25, 3);

  setSuppressTick(true);
  player.src = `http://tutdock.localhost/video/${idx}`;
  player.load();

  player.onloadedmetadata = async () => {
    try { player.volume = keepVol; volSlider.value = String(keepVol); updateVolFill(); } catch (_) {}
    try { player.playbackRate = keepRate; } catch (_) {}
    speedBtnText.textContent = `${keepRate.toFixed(2)}x`;
    updateSpeedIcon(keepRate);
    cb.buildSpeedMenu?.(keepRate);

    try { const t = Number(timecode || 0.0); if (t > 0) player.currentTime = t; } catch (_) {}

    if (onReady) await onReady();

    if (autoplayOnLoad) {
      try { await player.play(); } catch (_) {}
    } else if (pauseAfterLoad) {
      player.pause();
    }

    setSuppressTick(false);
    updateTimeReadout();
    updatePlayPauseIcon();
    cb.updateInfoPanel?.();
    await cb.refreshCurrentVideoMeta?.();
  };
}

// ---- Speed menu ----

const SPEEDS = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0];

export function closeSpeedMenu(): void {
  speedMenu?.classList.remove('show');
  speedBtn?.setAttribute('aria-expanded', 'false');
}
export function openSpeedMenu(): void {
  speedMenu?.classList.add('show');
  speedBtn?.setAttribute('aria-expanded', 'true');
}

export function buildSpeedMenu(active: number): void {
  if (!speedMenu) return;
  speedMenu.innerHTML = '';
  for (const s of SPEEDS) {
    const row = document.createElement('div');
    row.className = 'speedItem' + (Math.abs(s - active) < 0.0001 ? ' active' : '');
    row.setAttribute('role', 'menuitem');
    row.tabIndex = -1;
    row.addEventListener('keydown', (e) => {
      if (e.key === 'ArrowDown') {
        e.preventDefault(); e.stopPropagation();
        const next = row.nextElementSibling as HTMLElement | null;
        if (next) next.focus();
      } else if (e.key === 'ArrowUp') {
        e.preventDefault(); e.stopPropagation();
        const prev = row.previousElementSibling as HTMLElement | null;
        if (prev) prev.focus();
      } else if (e.key === 'Escape' || e.key === 'Tab') {
        e.preventDefault(); e.stopPropagation();
        closeSpeedMenu();
        speedBtn.focus();
      } else if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault(); e.stopPropagation();
        row.click();
      }
    });

    const left = document.createElement('div');
    left.style.display = 'flex'; left.style.alignItems = 'center'; left.style.gap = '10px';
    const dot = document.createElement('div'); dot.className = 'dot';
    const txt = document.createElement('div');
    txt.textContent = `${s.toFixed(2)}x`;

    left.appendChild(dot); left.appendChild(txt);
    row.appendChild(left);

    row.onclick = () => {
      closeSpeedMenu();
      const r = clamp(Number(s), 0.25, 3);
      player.playbackRate = r;
      if (library) (library as any).folder_rate = r;
      speedBtnText.textContent = `${r.toFixed(2)}x`;
      updateSpeedIcon(r);
      buildSpeedMenu(r);
      cb.updateInfoPanel?.();
      setSuppressTick(true);
      if (library) { api.setFolderRate(r).catch(() => {}).finally(() => setSuppressTick(false)); }
      else { setSuppressTick(false); }
    };
    speedMenu.appendChild(row);
  }
}

export function updateSpeedIcon(rate: number): void {
  if (!speedIcon) return;
  let needleAngle = 0;
  let needleColor = 'rgba(255,255,255,.85)';

  if (rate <= 0.5) {
    needleAngle = -150;
    needleColor = 'rgba(100,180,255,.9)';
  } else if (rate < 1.0) {
    const t = (rate - 0.5) / 0.5;
    needleAngle = -150 + t * 150;
    needleColor = `rgba(${Math.round(100 + t * 155)},${Math.round(180 + t * 75)},${Math.round(255)},0.9)`;
  } else if (rate <= 1.0) {
    needleAngle = 0;
    needleColor = 'rgba(255,255,255,.85)';
  } else if (rate < 2.0) {
    const t = (rate - 1.0) / 1.0;
    needleAngle = t * 150;
    needleColor = `rgba(255,${Math.round(255 - t * 115)},${Math.round(255 - t * 155)},0.9)`;
  } else {
    needleAngle = 150;
    needleColor = 'rgba(255,140,100,.9)';
  }

  const needleGroup = speedIcon.querySelector('.speed-needle') as SVGElement | null;
  const needleLine = speedIcon.querySelector('.speed-needle line') as SVGElement | null;
  if (needleGroup && needleLine) {
    needleGroup.style.transform = `rotate(${needleAngle}deg)`;
    needleLine.style.stroke = needleColor;
  } else {
    speedIcon.innerHTML = `
      <path d="M12 22C6.5 22 2 17.5 2 12S6.5 2 12 2s10 4.5 10 10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity=".5"/>
      <path d="M12 22c5.5 0 10-4.5 10-10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity=".3"/>
      <g class="speed-needle" style="transform-origin:12px 13px; transform:rotate(${needleAngle}deg); transition:transform 0.8s cubic-bezier(.4,0,.2,1);">
        <line x1="12" y1="13" x2="12" y2="5" stroke="${needleColor}" stroke-width="2.5" stroke-linecap="round" style="transition:stroke 0.8s ease;"/>
      </g>
      <circle cx="12" cy="13" r="2" fill="currentColor" opacity=".7"/>
    `;
  }
}
