#!/usr/bin/env node
import { Command } from 'commander';
import * as fs from 'fs';
import * as path from 'path';
import { loadConfig, loadToken, saveToken, clearToken, CONFIG_DIR, CACHE_DIR, PRICING_PATH, getOrCreateDeviceId, getOsPlatform, getAgentVersion } from './config';
import { EventQueue } from './queue';
import { Uploader } from './uploader';
import { JsonlWatcher } from './watcher';
import { PricingTable } from './parsers/types';
import { installService, uninstallService } from './service';
import { renderStatusline } from './statusline';
import { setup as runSetup, unsetup as runUnsetup } from './setup';
import { reportDaily, reportWeekly, reportMonthly, reportSessions } from './reports';

const program = new Command();
program.name('ffctk').description('FF AI Coding tools usage tracker').version('0.1.0');

program
  .command('login')
  .description('Save personal access token to local credentials store')
  .argument('<token>', 'ffai_xxxxxxxx personal access token')
  .action((token: string) => {
    if (!token.startsWith('ffai_')) {
      console.error('Token must start with "ffai_"');
      process.exit(1);
    }
    saveToken(token);
    console.log(`✅ Token saved to ${CONFIG_DIR}/credentials (mode 0600)`);
    console.log('Run `ffctk start` to begin uploading usage events.');
  });

program
  .command('logout')
  .description('Remove local credentials')
  .action(() => {
    clearToken();
    console.log('✅ Credentials removed');
  });

program
  .command('status')
  .description('Show agent status, queue depth, and config')
  .action(() => {
    const token = loadToken();
    const q = new EventQueue();
    const pending = q.pending();
    q.close();
    console.log(`Token:          ${token ? token.slice(0, 12) + '...' : '(none — run `ffctk login`)'}`);
    console.log(`Device ID:      ${getOrCreateDeviceId()}`);
    console.log(`Platform:       ${getOsPlatform()}`);
    console.log(`Agent version:  ${getAgentVersion()}`);
    console.log(`Config dir:     ${CONFIG_DIR}`);
    console.log(`Cache dir:      ${CACHE_DIR}`);
    console.log(`Queue pending:  ${pending} event(s)`);
    if (fs.existsSync(PRICING_PATH)) {
      try {
        const p = JSON.parse(fs.readFileSync(PRICING_PATH, 'utf8'));
        console.log(`Pricing:        v${p.version} (${p.models?.length ?? 0} models)`);
      } catch {}
    }
  });

program
  .command('start')
  .description('Start the daemon (watch JSONL + upload)')
  .action(async () => {
    const token = loadToken();
    if (!token) {
      console.error('No credentials. Run `ffctk login <token>` first.');
      process.exit(1);
    }
    console.log('🔒 Privacy: ffctk uploads only token counts and metadata. It does NOT read message content / prompts / tool results.');
    const config = loadConfig();
    const queue = new EventQueue();
    const uploader = new Uploader(token, config, queue);
    const watcher = new JsonlWatcher(config.batchIntervalMs);

    let pricing: PricingTable | null = null;
    if (fs.existsSync(PRICING_PATH)) {
      try {
        pricing = JSON.parse(fs.readFileSync(PRICING_PATH, 'utf8'));
      } catch {}
    }
    pricing = (await uploader.refreshPricing()) ?? pricing;
    watcher.setPricing(pricing);

    watcher.onFlush((events) => {
      const inserted = queue.enqueueBatch(events);
      console.log(`[ffctk] queued ${inserted} new events (deduped ${events.length - inserted})`);
    });
    watcher.start();

    let paused = false;
    const tick = async () => {
      if (paused) return;
      try {
        const result = await uploader.flushOnce();
        if (result) console.log(`[ffctk] uploaded: accepted=${result.accepted} deduped=${result.deduped} dlq=${result.dlq}`);
      } catch (err: any) {
        if (err.message === 'AUTH_FAILED' || err.message === 'DEVICE_BLOCKED') {
          paused = true;
          console.error('[ffctk] pausing uploads. Restart to retry.');
        }
      }
    };
    const flushTimer = setInterval(tick, config.batchIntervalMs);

    // Daily pricing refresh
    const pricingTimer = setInterval(async () => {
      const fresh = await uploader.refreshPricing();
      if (fresh) watcher.setPricing(fresh);
    }, 24 * 60 * 60 * 1000);

    console.log('✅ ffctk started. Watching ~/.claude/projects and ~/.codex/sessions for JSONL changes.');
    console.log('   Logs: ' + (process.platform === 'win32' ? CACHE_DIR + '\\ffctk.log' : CACHE_DIR + '/ffctk.log'));
    console.log('   Stop: Ctrl+C');

    const shutdown = () => {
      console.log('\n[ffctk] shutting down…');
      clearInterval(flushTimer);
      clearInterval(pricingTimer);
      watcher.stop();
      queue.close();
      process.exit(0);
    };
    process.on('SIGINT', shutdown);
    process.on('SIGTERM', shutdown);
  });

program
  .command('install-service')
  .description('Register ffctk as auto-start service (systemd user / launchd / Windows scheduled task)')
  .option('--bin <path>', 'Path to ffctk binary (defaults to current executable)')
  .action((opts: { bin?: string }) => {
    const binPath = opts.bin ?? process.execPath;
    installService(binPath);
  });

program
  .command('uninstall-service')
  .description('Remove auto-start service registration')
  .action(() => {
    uninstallService();
  });

program
  .command('statusline')
  .description('Read Claude Code statusLine JSON from stdin, render multi-line status (zero network, < 100ms)')
  .action(() => {
    try {
      process.stdout.write(renderStatusline());
    } catch (err: any) {
      process.stdout.write(`ffctk: ${err.message}\n`);
    }
  });

program
  .command('setup')
  .description('Write statusLine config to ~/.claude/settings.json so Claude Code shows ffctk live status')
  .action(() => runSetup());

program
  .command('unsetup')
  .description('Restore Claude Code statusLine config (revert `ffctk setup`)')
  .action(() => runUnsetup());

program
  .command('daily')
  .description('Show daily token usage from local JSONL (last 30 days)')
  .action(() => reportDaily());

program
  .command('weekly')
  .description('Show weekly token usage (last 12 weeks)')
  .action(() => reportWeekly());

program
  .command('monthly')
  .description('Show monthly token usage (last 12 months)')
  .action(() => reportMonthly());

program
  .command('sessions')
  .description('Show recent sessions sorted by last activity')
  .option('--limit <n>', 'Number of sessions', '20')
  .action((opts: { limit: string }) => reportSessions(parseInt(opts.limit, 10)));

program
  .command('flush')
  .description('Force a one-time upload of queued events and exit')
  .action(async () => {
    const token = loadToken();
    if (!token) { console.error('No credentials.'); process.exit(1); }
    const config = loadConfig();
    const queue = new EventQueue();
    const uploader = new Uploader(token, config, queue);
    for (let i = 0; i < 5; i++) {
      const r = await uploader.flushOnce();
      if (r) console.log(`flush: accepted=${r.accepted} deduped=${r.deduped} dlq=${r.dlq}`);
      if (!r || queue.pending() === 0) break;
    }
    queue.close();
  });

program.parseAsync(process.argv).catch((err) => {
  console.error('[ffctk]', err.message);
  process.exit(1);
});
