#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const https = require('https');

const CRED_PATH = process.env.CRED_FILE || path.join(__dirname, '..', '.credentials', 'services.env');

function loadCreds() {
  const raw = fs.readFileSync(CRED_PATH, 'utf8');
  const env = {};
  for (const line of raw.split('\n')) {
    const m = line.match(/^(\w+)=(.+)$/);
    if (m) env[m[1]] = m[2];
  }
  return env;
}

function api(endpoint) {
  const { AUDIOBOOKSHELF_URL, AUDIOBOOKSHELF_TOKEN } = loadCreds();
  const url = `${AUDIOBOOKSHELF_URL}${endpoint}`;
  return new Promise((resolve, reject) => {
    const req = https.get(url, { headers: { 'Authorization': `Bearer ${AUDIOBOOKSHELF_TOKEN}` } }, (res) => {
      let data = '';
      res.on('data', c => data += c);
      res.on('end', () => {
        try { resolve(JSON.parse(data)); }
        catch { reject(new Error(`Invalid JSON from ${endpoint}`)); }
      });
    });
    req.on('error', reject);
    req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout')); });
  });
}

function formatDuration(secs) {
  const h = Math.floor(secs / 3600);
  const m = Math.floor((secs % 3600) / 60);
  if (h > 0) return `${h}h ${m}m`;
  return `${m}m`;
}

function formatProgress(progress) {
  return `${Math.round(progress * 100)}%`;
}

async function cmdCurrent() {
  const data = await api('/api/me/items-in-progress');
  const items = data.libraryItems || [];
  if (items.length === 0) {
    console.log('Nothing in progress.');
    return;
  }
  for (const item of items) {
    const meta = item.media?.metadata || {};
    const title = meta.title || 'Unknown';
    const author = meta.authorName || 'Unknown';
    const narrator = meta.narratorName || '-';
    const series = meta.seriesName || '-';
    const duration = formatDuration(item.media?.duration || 0);
    
    // Get progress from user's mediaProgress
    const me = await api('/api/me');
    const prog = (me.mediaProgress || []).find(p => p.libraryItemId === item.id);
    const progress = prog ? formatProgress(prog.progress) : '?';
    const currentTime = prog ? formatDuration(prog.currentTime) : '?';
    const remaining = prog ? formatDuration((item.media?.duration || 0) - (prog.currentTime || 0)) : '?';
    
    console.log(`${title}\t${author}\t${series}\t${progress}\t${currentTime}/${duration}\tremaining:${remaining}\tnarrator:${narrator}`);
  }
}

async function cmdLibraries() {
  const data = await api('/api/libraries');
  for (const lib of (data.libraries || [])) {
    console.log(`${lib.id}\t${lib.name}\t${lib.mediaType}`);
  }
}

async function cmdRecent(args) {
  const limit = args[0] || '5';
  const me = await api('/api/me');
  const progress = (me.mediaProgress || [])
    .filter(p => !p.isFinished)
    .sort((a, b) => (b.lastUpdate || 0) - (a.lastUpdate || 0))
    .slice(0, parseInt(limit, 10));
  
  if (progress.length === 0) {
    console.log('No recent listening activity.');
    return;
  }
  
  for (const p of progress) {
    try {
      const item = await api(`/api/items/${p.libraryItemId}?expanded=1`);
      const meta = item.media?.metadata || {};
      const title = meta.title || 'Unknown';
      const author = meta.authorName || 'Unknown';
      const series = meta.seriesName || '-';
      const pct = formatProgress(p.progress);
      const lastUpdate = new Date(p.lastUpdate).toISOString().slice(0, 10);
      console.log(`${title}\t${author}\t${series}\t${pct}\tlast:${lastUpdate}`);
    } catch {
      // Item might have been removed
    }
  }
}

async function cmdFinished(args) {
  const limit = args[0] || '10';
  const me = await api('/api/me');
  const finished = (me.mediaProgress || [])
    .filter(p => p.isFinished)
    .sort((a, b) => (b.finishedAt || 0) - (a.finishedAt || 0))
    .slice(0, parseInt(limit, 10));
  
  if (finished.length === 0) {
    console.log('No finished books.');
    return;
  }
  
  for (const p of finished) {
    try {
      const item = await api(`/api/items/${p.libraryItemId}?expanded=1`);
      const meta = item.media?.metadata || {};
      const title = meta.title || 'Unknown';
      const author = meta.authorName || 'Unknown';
      const finishedDate = p.finishedAt ? new Date(p.finishedAt).toISOString().slice(0, 10) : '?';
      console.log(`${title}\t${author}\tfinished:${finishedDate}`);
    } catch {
      // Item might have been removed
    }
  }
}

async function cmdStats() {
  const me = await api('/api/me');
  const progress = me.mediaProgress || [];
  const inProgress = progress.filter(p => !p.isFinished && !p.hideFromContinueListening).length;
  const finished = progress.filter(p => p.isFinished).length;
  const totalListened = progress.reduce((sum, p) => sum + (p.currentTime || 0), 0);
  
  console.log(`in-progress:${inProgress}\tfinished:${finished}\ttotal-listened:${formatDuration(totalListened)}`);
}

const LIBRARY_ID = '197852a2-2602-4cb9-985c-70f9df5867a8';

async function cmdLibrary(args) {
  const full = args.includes('--full');
  const limitArg = args.find(a => a !== '--full');
  const endpoint = `/api/libraries/${LIBRARY_ID}/items?limit=0&sort=media.metadata.title`;
  const data = await api(endpoint);
  const items = data.results || [];
  const limit = limitArg ? parseInt(limitArg, 10) : items.length;

  console.log('TITLE\tAUTHOR\tSERIES\tNARRATOR\tGENRES\tDURATION\tYEAR\tDESCRIPTION');
  for (const item of items.slice(0, limit)) {
    const meta = item.media?.metadata || {};
    const title = meta.title || 'Unknown';
    const author = meta.authorName || '-';
    const series = meta.seriesName || '-';
    const narrator = meta.narratorName || '-';
    const genres = (meta.genres || []).join(',') || '-';
    const duration = formatDuration(item.media?.duration || 0);
    const year = meta.publishedYear || '-';
    const desc = meta.description || '';
    const descOut = full ? desc.replace(/[\t\n\r]/g, ' ') : (desc.length > 100 ? desc.slice(0, 100).replace(/[\t\n\r]/g, ' ') + '...' : desc.replace(/[\t\n\r]/g, ' ')) || '-';
    console.log(`${title}\t${author}\t${series}\t${narrator}\t${genres}\t${duration}\t${year}\t${descOut}`);
  }
}

async function cmdGenres() {
  const data = await api(`/api/libraries/${LIBRARY_ID}/items?limit=0`);
  const items = data.results || [];
  const counts = {};
  for (const item of items) {
    for (const g of (item.media?.metadata?.genres || [])) {
      counts[g] = (counts[g] || 0) + 1;
    }
  }
  const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
  for (const [genre, count] of sorted) {
    console.log(`${genre}\t${count}`);
  }
}

// --- Main ---

const [cmd, ...args] = process.argv.slice(2);

const run = async () => {
  switch (cmd) {
    case 'current': await cmdCurrent(); break;
    case 'recent': await cmdRecent(args); break;
    case 'finished': await cmdFinished(args); break;
    case 'stats': await cmdStats(); break;
    case 'libraries': await cmdLibraries(); break;
    case 'library': await cmdLibrary(args); break;
    case 'genres': await cmdGenres(); break;
    default:
      console.log(`Usage: audiobooks <command>

Commands:
  current              Currently listening / in-progress books
  recent [limit]       Recently active books (default: 5)
  finished [limit]     Finished books (default: 10)
  stats                Listening stats overview
  libraries            List libraries
  library [limit]      All books with metadata (--full for full descriptions)
  genres               List genres with counts`);
  }
};

run().catch(err => { console.error(`Error: ${err.message}`); process.exit(1); });
