import { Command } from 'commander';
import http from 'node:http';

export interface CacheCommandDeps {
  log: (...args: string[]) => void;
  mcplocalUrl?: string;
}

interface NamespaceStats {
  name: string;
  entries: number;
  size: number;
  oldestMs: number;
  newestMs: number;
}

interface CacheStats {
  rootDir: string;
  totalSize: number;
  totalEntries: number;
  namespaces: NamespaceStats[];
}

interface ClearResult {
  removed: number;
  freedBytes: number;
}

function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB'];
  const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
  const val = bytes / Math.pow(1024, i);
  return `${val < 10 ? val.toFixed(1) : Math.round(val)} ${units[i]}`;
}

function formatAge(ms: number): string {
  if (ms === 0) return '-';
  const age = Date.now() - ms;
  const days = Math.floor(age / (24 * 60 * 60 * 1000));
  if (days > 0) return `${days}d ago`;
  const hours = Math.floor(age / (60 * 60 * 1000));
  if (hours > 0) return `${hours}h ago`;
  const mins = Math.floor(age / (60 * 1000));
  return `${mins}m ago`;
}

function fetchJson<T>(url: string, method = 'GET'): Promise<T> {
  return new Promise((resolve, reject) => {
    const req = http.request(url, { method, timeout: 5000 }, (res) => {
      let data = '';
      res.on('data', (chunk: Buffer) => { data += chunk.toString(); });
      res.on('end', () => {
        try {
          resolve(JSON.parse(data) as T);
        } catch {
          reject(new Error(`Invalid response from mcplocal: ${data.slice(0, 200)}`));
        }
      });
    });
    req.on('error', () => reject(new Error('Cannot connect to mcplocal. Is it running?')));
    req.on('timeout', () => { req.destroy(); reject(new Error('mcplocal request timed out')); });
    req.end();
  });
}

export function createCacheCommand(deps: CacheCommandDeps): Command {
  const cache = new Command('cache')
    .description('Manage ProxyModel pipeline cache');

  const mcplocalUrl = deps.mcplocalUrl ?? 'http://localhost:3200';

  cache
    .command('stats')
    .description('Show cache statistics')
    .action(async () => {
      const stats = await fetchJson<CacheStats>(`${mcplocalUrl}/cache/stats`);

      if (stats.totalEntries === 0) {
        deps.log('Cache is empty.');
        return;
      }

      deps.log(`Cache: ${formatBytes(stats.totalSize)} total, ${stats.totalEntries} entries`);
      deps.log(`Path:  ${stats.rootDir}`);
      deps.log('');

      // Table header
      const pad = (s: string, w: number) => s.padEnd(w);
      deps.log(
        `${pad('NAMESPACE', 40)}  ${pad('ENTRIES', 8)}  ${pad('SIZE', 10)}  ${pad('OLDEST', 12)}  NEWEST`,
      );
      deps.log(
        `${pad('-'.repeat(40), 40)}  ${pad('-'.repeat(8), 8)}  ${pad('-'.repeat(10), 10)}  ${pad('-'.repeat(12), 12)}  ${'-'.repeat(12)}`,
      );

      for (const ns of stats.namespaces) {
        deps.log(
          `${pad(ns.name, 40)}  ${pad(String(ns.entries), 8)}  ${pad(formatBytes(ns.size), 10)}  ${pad(formatAge(ns.oldestMs), 12)}  ${formatAge(ns.newestMs)}`,
        );
      }
    });

  cache
    .command('clear')
    .description('Clear cache entries')
    .argument('[namespace]', 'Clear only this namespace')
    .option('--older-than <days>', 'Clear entries older than N days')
    .option('-y, --yes', 'Skip confirmation')
    .action(async (namespace: string | undefined, opts: { olderThan?: string; yes?: boolean }) => {
      // Show what will be cleared first
      const stats = await fetchJson<CacheStats>(`${mcplocalUrl}/cache/stats`);
      if (stats.totalEntries === 0) {
        deps.log('Cache is already empty.');
        return;
      }

      const target = namespace
        ? stats.namespaces.find((ns) => ns.name === namespace)
        : null;
      if (namespace && !target) {
        deps.log(`Namespace '${namespace}' not found.`);
        deps.log(`Available: ${stats.namespaces.map((ns) => ns.name).join(', ')}`);
        return;
      }

      const olderThan = opts.olderThan ? `?olderThan=${opts.olderThan}` : '';
      const url = namespace
        ? `${mcplocalUrl}/cache/${encodeURIComponent(namespace)}${olderThan}`
        : `${mcplocalUrl}/cache${olderThan}`;

      const result = await fetchJson<ClearResult>(url, 'DELETE');
      deps.log(`Cleared ${result.removed} entries, freed ${formatBytes(result.freedBytes)}`);
    });

  return cache;
}
