import { writeFileSync } from 'node:fs';

import type { Command } from 'commander';

import type { LogCategory, LogLevel, LogTier } from './agent-logs.js';

interface FilterOptions {
  agent?: string;
  level?: string;
  category?: string;
  tier?: string;
  limit?: string;
  db?: string;
}

function parseLimit(raw: string | undefined, defaultVal = 50): number {
  if (!raw) return defaultVal;
  const n = parseInt(raw, 10);
  return Number.isFinite(n) && n > 0 ? n : defaultVal;
}

function buildQuery(opts: FilterOptions) {
  return {
    ...(opts.agent ? { sessionId: opts.agent } : {}),
    ...(opts.level ? { level: opts.level as LogLevel } : {}),
    ...(opts.category ? { category: opts.category as LogCategory } : {}),
    ...(opts.tier ? { tier: opts.tier as LogTier } : {}),
    limit: parseLimit(opts.limit),
  };
}

async function openDb(connectionString: string) {
  const { createDb } = await import('@mosaicstack/db');
  return createDb(connectionString);
}

function resolveConnectionString(opts: FilterOptions): string | undefined {
  return opts.db ?? process.env['DATABASE_URL'];
}

/**
 * Register log subcommands on an existing Commander program.
 * This avoids cross-package Commander version mismatches by using the
 * caller's Command instance directly.
 */
export function registerLogCommand(parent: Command): void {
  const log = parent.command('log').description('Query and manage agent logs');

  // ─── tail ───────────────────────────────────────────────────────────────

  log
    .command('tail')
    .description('Tail recent agent logs')
    .option('--agent <id>', 'Filter by agent/session ID')
    .option('--level <level>', 'Filter by log level (debug|info|warn|error)')
    .option('--category <cat>', 'Filter by category (decision|tool_use|learning|error|general)')
    .option('--tier <tier>', 'Filter by tier (hot|warm|cold)')
    .option('--limit <n>', 'Number of logs to return (default 50)', '50')
    .option('--db <connection-string>', 'Database connection string (or set DATABASE_URL)')
    .action(async (opts: FilterOptions) => {
      const connStr = resolveConnectionString(opts);
      if (!connStr) {
        console.error('Database connection required: use --db or set DATABASE_URL');
        process.exit(1);
      }

      const handle = await openDb(connStr);
      try {
        const { createLogService } = await import('./log-service.js');
        const svc = createLogService(handle.db);
        const query = buildQuery(opts);

        const logs = await svc.logs.query(query);
        if (logs.length === 0) {
          console.log('No logs found.');
          return;
        }
        for (const entry of logs) {
          const ts = new Date(entry.createdAt).toISOString();
          console.log(`[${ts}] [${entry.level}] [${entry.category}] ${entry.content}`);
        }
      } finally {
        await handle.close();
      }
    });

  // ─── search ─────────────────────────────────────────────────────────────

  log
    .command('search <query>')
    .description('Full-text search over agent logs')
    .option('--agent <id>', 'Filter by agent/session ID')
    .option('--level <level>', 'Filter by log level (debug|info|warn|error)')
    .option('--category <cat>', 'Filter by category (decision|tool_use|learning|error|general)')
    .option('--tier <tier>', 'Filter by tier (hot|warm|cold)')
    .option('--limit <n>', 'Number of logs to return (default 50)', '50')
    .option('--db <connection-string>', 'Database connection string (or set DATABASE_URL)')
    .action(async (query: string, opts: FilterOptions) => {
      const connStr = resolveConnectionString(opts);
      if (!connStr) {
        console.error('Database connection required: use --db or set DATABASE_URL');
        process.exit(1);
      }

      const handle = await openDb(connStr);
      try {
        const { createLogService } = await import('./log-service.js');
        const svc = createLogService(handle.db);
        const baseQuery = buildQuery(opts);

        const logs = await svc.logs.query(baseQuery);
        const lowerQ = query.toLowerCase();
        const matched = logs.filter(
          (e) =>
            e.content.toLowerCase().includes(lowerQ) ||
            (e.metadata != null && JSON.stringify(e.metadata).toLowerCase().includes(lowerQ)),
        );

        if (matched.length === 0) {
          console.log('No matching logs found.');
          return;
        }
        for (const entry of matched) {
          const ts = new Date(entry.createdAt).toISOString();
          console.log(`[${ts}] [${entry.level}] [${entry.category}] ${entry.content}`);
        }
      } finally {
        await handle.close();
      }
    });

  // ─── export ─────────────────────────────────────────────────────────────

  log
    .command('export <path>')
    .description('Export matching logs to an NDJSON file')
    .option('--agent <id>', 'Filter by agent/session ID')
    .option('--level <level>', 'Filter by log level (debug|info|warn|error)')
    .option('--category <cat>', 'Filter by category (decision|tool_use|learning|error|general)')
    .option('--tier <tier>', 'Filter by tier (hot|warm|cold)')
    .option('--limit <n>', 'Number of logs to export (default 50)', '50')
    .option('--db <connection-string>', 'Database connection string (or set DATABASE_URL)')
    .action(async (outputPath: string, opts: FilterOptions) => {
      const connStr = resolveConnectionString(opts);
      if (!connStr) {
        console.error('Database connection required: use --db or set DATABASE_URL');
        process.exit(1);
      }

      const handle = await openDb(connStr);
      try {
        const { createLogService } = await import('./log-service.js');
        const svc = createLogService(handle.db);
        const query = buildQuery(opts);

        const logs = await svc.logs.query(query);
        const ndjson = logs.map((e) => JSON.stringify(e)).join('\n');
        writeFileSync(outputPath, ndjson, 'utf8');
        console.log(`Exported ${logs.length} log(s) to ${outputPath}`);
      } finally {
        await handle.close();
      }
    });

  // ─── level ──────────────────────────────────────────────────────────────

  log
    .command('level <level>')
    .description('Set runtime log level for the connected log service')
    .action((level: string) => {
      void level;
      console.log(
        'Runtime log level adjustment is not supported in current mode (DB-backed log service).',
      );
      process.exitCode = 0;
    });
}
