// Reusable, reorderable, checkable video file list with async durations.

import { h, clear, basename, fmtDur } from "./dom";
import { probeDurations, isVideo } from "./api";

export interface FileItem {
  path: string;
  checked: boolean;
  duration: number; // -1 = pending
}

export class FileList {
  items: FileItem[] = [];
  active: number | null = null;
  private listEl: HTMLElement;
  private totalEl: HTMLElement;
  private onChange: () => void;

  constructor(listEl: HTMLElement, totalEl: HTMLElement, onChange: () => void) {
    this.listEl = listEl;
    this.totalEl = totalEl;
    this.onChange = onChange;
  }

  /** Filter to videos, dedupe, append, then probe durations in the background. */
  async add(paths: string[]) {
    const existing = new Set(this.items.map((i) => i.path));
    const fresh: FileItem[] = [];
    for (const p of paths) {
      if (existing.has(p)) continue;
      if (!(await isVideo(p))) continue;
      const item: FileItem = { path: p, checked: true, duration: -1 };
      this.items.push(item);
      fresh.push(item);
    }
    if (!fresh.length) return;
    if (this.active === null) this.active = 0;
    this.render();
    this.onChange();

    const durs = await probeDurations(fresh.map((f) => f.path));
    const byPath = new Map(durs.map((d) => [d.path, d.duration]));
    for (const f of fresh) f.duration = byPath.get(f.path) ?? 0;
    this.render();
    this.onChange();
  }

  checkedPaths(): string[] {
    return this.items.filter((i) => i.checked).map((i) => i.path);
  }

  orderedPaths(): string[] {
    return this.items.map((i) => i.path);
  }

  clearAll() { this.items = []; this.active = null; this.render(); this.onChange(); }
  selectAll(v: boolean) { this.items.forEach((i) => (i.checked = v)); this.render(); this.onChange(); }

  moveActive(dir: -1 | 1) {
    if (this.active === null) return;
    const j = this.active + dir;
    if (j < 0 || j >= this.items.length) return;
    [this.items[this.active], this.items[j]] = [this.items[j], this.items[this.active]];
    this.active = j;
    this.render();
    this.onChange();
  }

  removeActive() {
    if (this.active === null) return;
    this.items.splice(this.active, 1);
    this.active = this.items.length ? Math.min(this.active, this.items.length - 1) : null;
    this.render();
    this.onChange();
  }

  private total(): { secs: number; pending: number } {
    let secs = 0, pending = 0;
    for (const i of this.items) {
      if (i.duration >= 0) secs += i.duration;
      else pending++;
    }
    return { secs, pending };
  }

  render() {
    clear(this.listEl);
    this.items.forEach((it, idx) => {
      const cb = h("input", { type: "checkbox", class: "checkbox", checked: it.checked }) as HTMLInputElement;
      cb.addEventListener("change", () => { it.checked = cb.checked; this.onChange(); });

      const row = h("div", { class: `list-row ${this.active === idx ? "active" : ""}` },
        h("div", { class: "idx" }, String(idx + 1)),
        cb,
        h("div", { class: "name", title: it.path }, basename(it.path)),
        h("div", { class: "dur" }, it.duration >= 0 ? fmtDur(it.duration) : "…"),
        h("button", { class: "x", title: "Remove", onclick: (e: Event) => { e.stopPropagation(); this.active = idx; this.removeActive(); } }, "✕"),
      );
      row.addEventListener("click", () => { this.active = idx; this.render(); });
      this.listEl.append(row);
    });

    const { secs, pending } = this.total();
    const n = this.items.length;
    this.totalEl.textContent = !n ? "No files"
      : pending ? `${n} file${n > 1 ? "s" : ""} · ${fmtDur(secs)} (scanning ${pending}…)`
      : `${n} file${n > 1 ? "s" : ""} · total ${fmtDur(secs)}`;
  }
}
