import { Command } from 'commander';
import chalk from 'chalk';
import { requireConfig } from '../config';
import { apiRequest } from '../api';

interface AuditEvent {
  id: string;
  timestamp: string;
  action: string;
  agentId?: string;
  tenantId?: string;
  outcome: string;
  details?: Record<string, unknown>;
}

interface AuditLogsResponse {
  events: AuditEvent[];
  nextCursor?: string;
}

function formatEvent(event: AuditEvent): string {
  const ts = chalk.dim(new Date(event.timestamp).toLocaleString());
  const outcome =
    event.outcome === 'success'
      ? chalk.green(event.outcome)
      : chalk.red(event.outcome);
  const action = chalk.cyan(event.action);
  const agentPart =
    event.agentId !== undefined
      ? ' ' + chalk.dim('agent=' + event.agentId)
      : '';

  return `${ts}  ${action}  outcome=${outcome}${agentPart}  id=${chalk.dim(event.id)}`;
}

export function registerTailAuditLog(program: Command): void {
  program
    .command('tail-audit-log')
    .description(
      'Poll and stream audit log events every 5 seconds (Ctrl+C to stop)',
    )
    .option('--agent-id <id>', 'Filter events for a specific agent ID')
    .action(async (options: { agentId?: string }) => {
      const config = requireConfig();

      console.log(
        chalk.bold('Tailing audit log') +
          (options.agentId !== undefined
            ? chalk.dim(` (agent: ${options.agentId})`)
            : '') +
          chalk.dim(' — press Ctrl+C to stop'),
      );
      console.log(chalk.dim('─'.repeat(60)));

      const seenIds = new Set<string>();
      let cursor: string | undefined;
      let running = true;

      process.on('SIGINT', () => {
        running = false;
        console.log();
        console.log(chalk.dim('Stopped.'));
        process.exit(0);
      });

      while (running) {
        try {
          const params: Record<string, string> = {};
          if (options.agentId !== undefined) {
            params['agentId'] = options.agentId;
          }
          if (cursor !== undefined) {
            params['cursor'] = cursor;
          }
          // Request events from the last poll window
          params['limit'] = '50';

          const data = await apiRequest<AuditLogsResponse | AuditEvent[]>(
            config,
            '/audit/logs',
            { params },
          );

          const events: AuditEvent[] = Array.isArray(data)
            ? data
            : (data as AuditLogsResponse).events ?? [];

          if (!Array.isArray(data) && (data as AuditLogsResponse).nextCursor !== undefined) {
            cursor = (data as AuditLogsResponse).nextCursor;
          }

          for (const event of events) {
            if (!seenIds.has(event.id)) {
              seenIds.add(event.id);
              console.log(formatEvent(event));
            }
          }

          // Keep the seenIds set bounded to avoid unbounded memory growth
          if (seenIds.size > 10_000) {
            const arr = Array.from(seenIds);
            const keep = arr.slice(arr.length - 5_000);
            seenIds.clear();
            for (const id of keep) seenIds.add(id);
          }
        } catch (err) {
          console.error(
            chalk.yellow('⚠') +
              ' Poll error: ' +
              (err instanceof Error ? err.message : String(err)),
          );
        }

        // Wait 5 seconds between polls
        await new Promise<void>((resolve) => {
          const timer = setTimeout(resolve, 5000);
          // Allow the timer to be garbage-collected if process exits
          if (typeof timer.unref === 'function') timer.unref();
        });
      }
    });
}
