import { createWriteStream, existsSync, mkdirSync, promises as fs } from 'fs';
import { dirname } from 'path';
import type {
  AuditEvent,
  AuditConfig,
  ToolStartEvent,
  ToolSuccessEvent,
  ToolErrorEvent,
  ToolDeniedEvent,
  ToolApprovalEvent,
  SkillsInstallerExecutionBlockedEvent,
  SkillsInstallerCommandResultEvent,
  SkillsScanEvent,
  SessionCreateEvent,
  SessionMessageEvent,
  SessionDeleteEvent,
  SessionCompactEvent,
  CronTriggerEvent,
  WebhookReceiveEvent,
  HeartbeatCycleEvent,
  HeartbeatCheckEvent,
  HeartbeatFailEvent,
  HeartbeatRecoverEvent,
  GmailPollEvent,
  GmailNewEmailEvent,
  SecurityElevationEvent,
} from './types.js';
import { AuditRotator } from './rotation.js';

export class AuditLogger {
  private writeStream: import('fs').WriteStream | null = null;
  private config: AuditConfig;
  private rotator: AuditRotator;

  constructor(config: AuditConfig) {
    this.config = config;
    this.rotator = new AuditRotator(config);

    if (!this.config.enabled) {
      return;
    }

    this.ensureLogDirectory();
    this.rotator.checkRotation();
    this.writeStream = createWriteStream(config.path, { flags: 'a' });
  }

  private ensureLogDirectory(): void {
    const logDir = dirname(this.config.path);
    if (!existsSync(logDir)) {
      mkdirSync(logDir, { recursive: true });
    }
  }

  private write(event: Omit<AuditEvent, 'timestamp'>): void {
    if (!this.config.enabled || !this.writeStream) {
      return;
    }

    this.rotator.checkRotation();

    const fullEvent: AuditEvent = { ...event, timestamp: Date.now() };
    this.writeStream!.write(JSON.stringify(fullEvent) + '\n');
  }

  private shouldLog(category: 'tools' | 'sessions' | 'automation', level: string): boolean {
    const levelOrder = { debug: 0, info: 1, warn: 2, error: 3 };
    const configLevel = this.config.levels[category];
    return levelOrder[level as keyof typeof levelOrder] >= levelOrder[configLevel];
  }

  // ── Tool Events ───────────────────────────────────────────────

  toolStart(event: ToolStartEvent): void {
    if (!this.shouldLog('tools', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'tool.start', event: event as unknown as Record<string, unknown> });
  }

  toolSuccess(event: ToolSuccessEvent): void {
    if (!this.shouldLog('tools', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'tool.success', event: event as unknown as Record<string, unknown> });
  }

  toolError(event: ToolErrorEvent): void {
    if (!this.shouldLog('tools', 'error')) {return;}
    this.write({ level: 'error', event_type: 'tool.error', event: event as unknown as Record<string, unknown> });
  }

  toolDenied(event: ToolDeniedEvent): void {
    if (!this.shouldLog('tools', 'warn')) {return;}
    this.write({ level: 'warn', event_type: 'tool.denied', event: event as unknown as Record<string, unknown> });
  }

  toolApproval(event: ToolApprovalEvent): void {
    if (!this.shouldLog('tools', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'tool.approval', event: event as unknown as Record<string, unknown> });
  }

  skillsInstallerExecutionBlocked(event: SkillsInstallerExecutionBlockedEvent): void {
    if (!this.shouldLog('tools', 'warn')) {return;}
    this.write({
      level: 'warn',
      event_type: 'skills.installer.execution_blocked',
      event: event as unknown as Record<string, unknown>,
    });
  }

  skillsInstallerCommandResult(event: SkillsInstallerCommandResultEvent): void {
    const level = event.status === 'succeeded' ? 'debug' : event.reason === 'allowlist_blocked' ? 'warn' : 'error';
    if (!this.shouldLog('tools', level)) {return;}
    this.write({
      level,
      event_type: 'skills.installer.command_result',
      event: event as unknown as Record<string, unknown>,
    });
  }

  skillsScan(event: SkillsScanEvent): void {
    const level = event.ok ? 'debug' : 'warn';
    if (!this.shouldLog('tools', level)) {return;}
    this.write({
      level,
      event_type: event.ok ? 'skills.scan.pass' : 'skills.scan.fail',
      event: event as unknown as Record<string, unknown>,
    });
  }

  securityElevationEnabled(event: SecurityElevationEvent): void {
    if (!this.shouldLog('tools', 'info')) {return;}
    this.write({ level: 'info', event_type: 'security.elevation.enabled', event: event as unknown as Record<string, unknown> });
  }

  securityElevationDisabled(event: SecurityElevationEvent): void {
    if (!this.shouldLog('tools', 'info')) {return;}
    this.write({ level: 'info', event_type: 'security.elevation.disabled', event: event as unknown as Record<string, unknown> });
  }

  securityElevationExpired(event: SecurityElevationEvent): void {
    if (!this.shouldLog('tools', 'info')) {return;}
    this.write({ level: 'info', event_type: 'security.elevation.expired', event: event as unknown as Record<string, unknown> });
  }

  // ── Session Events ───────────────────────────────────────────

  sessionCreate(event: SessionCreateEvent): void {
    if (!this.shouldLog('sessions', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'session.create', event: event as unknown as Record<string, unknown> });
  }

  sessionMessage(event: SessionMessageEvent): void {
    if (!this.shouldLog('sessions', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'session.message', event: event as unknown as Record<string, unknown> });
  }

  sessionDelete(event: SessionDeleteEvent): void {
    if (!this.shouldLog('sessions', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'session.delete', event: event as unknown as Record<string, unknown> });
  }

  sessionCompact(event: SessionCompactEvent): void {
    if (!this.shouldLog('sessions', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'session.compact', event: event as unknown as Record<string, unknown> });
  }

  sessionTransfer(from: string, to: string, messageCount: number): void {
    if (!this.shouldLog('sessions', 'debug')) {return;}
    this.write({
      level: 'debug',
      event_type: 'session.transfer',
      event: { from_session: from, to_session: to, message_count: messageCount },
    });
  }

  // ── Automation Events ───────────────────────────────────────

  // Cron
  cronTrigger(event: CronTriggerEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'cron.trigger', event: event as unknown as Record<string, unknown> });
  }

  cronAdd(jobName: string, schedule: string): void {
    if (!this.shouldLog('automation', 'info')) {return;}
    this.write({
      level: 'info',
      event_type: 'cron.add',
      event: { job_name: jobName, schedule },
    });
  }

  cronRemove(jobName: string): void {
    if (!this.shouldLog('automation', 'info')) {return;}
    this.write({
      level: 'info',
      event_type: 'cron.remove',
      event: { job_name: jobName },
    });
  }

  // Webhook
  webhookReceive(event: WebhookReceiveEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'webhook.receive', event: event as unknown as Record<string, unknown> });
  }

  webhookNotFound(webhookName: string): void {
    if (!this.shouldLog('automation', 'warn')) {return;}
    this.write({
      level: 'warn',
      event_type: 'webhook.not_found',
      event: { webhook_name: webhookName },
    });
  }

  webhookDenied(webhookName: string, reason: string): void {
    if (!this.shouldLog('automation', 'warn')) {return;}
    this.write({
      level: 'warn',
      event_type: 'webhook.denied',
      event: { webhook_name: webhookName, reason },
    });
  }

  // Heartbeat
  heartbeatCycle(event: HeartbeatCycleEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'heartbeat.cycle', event: event as unknown as Record<string, unknown> });
  }

  heartbeatCheck(event: HeartbeatCheckEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'heartbeat.check', event: event as unknown as Record<string, unknown> });
  }

  heartbeatFail(event: HeartbeatFailEvent): void {
    if (!this.shouldLog('automation', 'warn')) {return;}
    this.write({ level: 'warn', event_type: 'heartbeat.fail', event: event as unknown as Record<string, unknown> });
  }

  heartbeatRecover(event: HeartbeatRecoverEvent): void {
    if (!this.shouldLog('automation', 'info')) {return;}
    this.write({ level: 'info', event_type: 'heartbeat.recover', event: event as unknown as Record<string, unknown> });
  }

  // Gmail
  gmailPoll(event: GmailPollEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'gmail.poll', event: event as unknown as Record<string, unknown> });
  }

  gmailNewEmail(event: GmailNewEmailEvent): void {
    if (!this.shouldLog('automation', 'debug')) {return;}
    this.write({ level: 'debug', event_type: 'gmail.new_email', event: event as unknown as Record<string, unknown> });
  }

  gmailError(error: string, context?: string): void {
    if (!this.shouldLog('automation', 'error')) {return;}
    this.write({
      level: 'error',
      event_type: 'gmail.error',
      event: { error, context },
    });
  }

  // ── System Events ────────────────────────────────────────────

  systemStart(component: string, config?: Record<string, unknown>): void {
    if (!this.config.enabled) {return;}
    this.write({
      level: 'info',
      event_type: 'system.start',
      event: { component, config },
    });
  }

  systemStop(component: string, reason?: string): void {
    if (!this.config.enabled) {return;}
    this.write({
      level: 'info',
      event_type: 'system.stop',
      event: { component, reason },
    });
  }

  systemConfig(component: string, action: string, config: Record<string, unknown>): void {
    if (!this.config.enabled) {return;}
    this.write({
      level: 'info',
      event_type: 'system.config',
      event: { component, action, config },
    });
  }

  // ── Lifecycle ───────────────────────────────────────────────

  async close(): Promise<void> {
    if (this.writeStream) {
      await new Promise<void>((resolve) => {
        this.writeStream!.end(() => resolve());
      });
      this.writeStream = null;
    }
  }
}
