#!/usr/bin/env bun
/**
 * Trigger the music library index on the officer-music sidecar and follow its progress live,
 * ending with a summary report. Run from the platform repo:  bun scripts/reindex-music.ts
 *
 * It reads the sidecar's port from DATA_PATH/music/.server (written by the sidecar on startup) and
 * consumes its /reindex/stream SSE endpoint — the same stream the app subscribes to.
 */
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data');
const PORT_FILE = join(DATA_PATH, 'music', '.server');

type Progress = {
  running: boolean;
  foldersScanned: number;
  albumsBuilt: number;
  albumsSkipped: number;
  tracksIndexed: number;
  coversSaved: number;
  currentPath: string;
};
type Report = {
  albums: number;
  built: number;
  skipped: number;
  foldersScanned: number;
  tracksIndexed: number;
  coversSaved: number;
  discographies: number;
  elapsedSec: number;
  error: string | null;
};

function readPort(): number {
  try {
    const p = parseInt(readFileSync(PORT_FILE, 'utf8').trim(), 10);
    if (Number.isInteger(p) && p > 0) return p;
  } catch {
    /* fall through */
  }
  console.error(`✗ Could not read the sidecar port from ${PORT_FILE}.`);
  console.error('  Is officer-music running?  pm2 restart officer-music');
  process.exit(1);
}

const isTTY = Boolean(process.stdout.isTTY);
const cols = () => (process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 100);

function printProgress(p: Progress): void {
  const line =
    `♪ indexing… folders ${p.foldersScanned} · tracks ${p.tracksIndexed} · ` +
    `built ${p.albumsBuilt} · skipped ${p.albumsSkipped} · covers ${p.coversSaved}` +
    (p.currentPath ? ` · ${p.currentPath}` : '');
  if (isTTY) {
    const clipped = line.length > cols() - 1 ? line.slice(0, cols() - 2) + '…' : line;
    process.stdout.write('\r\x1b[2K' + clipped);
  } else {
    process.stdout.write(line + '\n');
  }
}

function printReport(r: Report): void {
  if (isTTY) process.stdout.write('\r\x1b[2K');
  const l = (k: string, v: string | number) => console.log(`  ${k.padEnd(9)} ${v}`);
  console.log('\n─── Music index complete ───');
  l('Albums:', `${r.albums}  (${r.built} built, ${r.skipped} unchanged)`);
  l('Tracks:', `${r.tracksIndexed} indexed`);
  l('Covers:', `${r.coversSaved} compressed`);
  l('Discogs:', `${r.discographies} artist${r.discographies === 1 ? '' : 's'}`);
  l('Folders:', `${r.foldersScanned} scanned`);
  l('Elapsed:', `${r.elapsedSec}s`);
  if (r.error) l('Error:', r.error);
  console.log('────────────────────────────\n');
}

async function main(): Promise<void> {
  const port = readPort();
  const url = `http://127.0.0.1:${port}/reindex/stream`;
  console.log(`Triggering music index via ${url}\n`);

  let res: Response;
  try {
    res = await fetch(url);
  } catch (err) {
    console.error(`✗ Could not reach the sidecar at 127.0.0.1:${port}: ${String(err)}`);
    process.exit(1);
  }
  if (!res.ok || !res.body) {
    console.error(`✗ Sidecar returned ${res.status}`);
    process.exit(1);
  }

  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buf = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buf += decoder.decode(value, { stream: true });
    let sep: number;
    while ((sep = buf.indexOf('\n\n')) >= 0) {
      const frame = buf.slice(0, sep);
      buf = buf.slice(sep + 2);
      let event = 'message';
      let data = '';
      for (const line of frame.split('\n')) {
        if (line.startsWith('event:')) event = line.slice(6).trim();
        else if (line.startsWith('data:')) data += line.slice(5).trim();
      }
      if (!data) continue;
      if (event === 'progress') printProgress(JSON.parse(data) as Progress);
      else if (event === 'done') printReport(JSON.parse(data) as Report);
    }
  }
}

main();
