import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { logger } from '../logger.js';

// ---------------------------------------------------------------------------
// IPC command type map — mirrors the methods in mailer-bin's management mode
// ---------------------------------------------------------------------------

interface IDkimVerificationResult {
  is_valid: boolean;
  domain: string | null;
  selector: string | null;
  status: string;
  details: string | null;
}

interface ISpfResult {
  result: string;
  domain: string;
  ip: string;
  explanation: string | null;
}

interface IDmarcResult {
  passed: boolean;
  policy: string;
  domain: string;
  dkim_result: string;
  spf_result: string;
  action: string;
  details: string | null;
}

interface IEmailSecurityResult {
  dkim: IDkimVerificationResult[];
  spf: ISpfResult | null;
  dmarc: IDmarcResult | null;
}

interface IValidationResult {
  valid: boolean;
  formatValid: boolean;
  score: number;
  error: string | null;
}

interface IBounceDetection {
  bounce_type: string;
  category: string;
}

interface IReputationResult {
  ip: string;
  score: number;
  risk_level: string;
  ip_type: string;
  dnsbl_results: Array<{ server: string; listed: boolean; response: string | null }>;
  listed_count: number;
  total_checked: number;
}

interface IVersionInfo {
  bin: string;
  core: string;
  security: string;
  smtp: string;
}

/**
 * Type-safe command map for the mailer-bin IPC bridge.
 */
type TMailerCommands = {
  ping: {
    params: Record<string, never>;
    result: { pong: boolean };
  };
  version: {
    params: Record<string, never>;
    result: IVersionInfo;
  };
  validateEmail: {
    params: { email: string };
    result: IValidationResult;
  };
  detectBounce: {
    params: { smtpResponse?: string; diagnosticCode?: string; statusCode?: string };
    result: IBounceDetection;
  };
  checkIpReputation: {
    params: { ip: string };
    result: IReputationResult;
  };
  verifyDkim: {
    params: { rawMessage: string };
    result: IDkimVerificationResult[];
  };
  signDkim: {
    params: { rawMessage: string; domain: string; selector?: string; privateKey: string };
    result: { header: string; signedMessage: string };
  };
  checkSpf: {
    params: { ip: string; heloDomain: string; hostname?: string; mailFrom: string };
    result: ISpfResult;
  };
  verifyEmail: {
    params: {
      rawMessage: string;
      ip: string;
      heloDomain: string;
      hostname?: string;
      mailFrom: string;
    };
    result: IEmailSecurityResult;
  };
};

// ---------------------------------------------------------------------------
// RustSecurityBridge — singleton wrapper around smartrust.RustBridge
// ---------------------------------------------------------------------------

/**
 * Bridge between TypeScript and the Rust `mailer-bin` binary.
 *
 * Uses `@push.rocks/smartrust` for JSON-over-stdin/stdout IPC.
 * Singleton — access via `RustSecurityBridge.getInstance()`.
 */
export class RustSecurityBridge {
  private static instance: RustSecurityBridge | null = null;

  private bridge: InstanceType<typeof plugins.smartrust.RustBridge<TMailerCommands>>;
  private _running = false;

  private constructor() {
    this.bridge = new plugins.smartrust.RustBridge<TMailerCommands>({
      binaryName: 'mailer-bin',
      cliArgs: ['--management'],
      requestTimeoutMs: 30_000,
      readyTimeoutMs: 10_000,
      localPaths: [
        plugins.path.join(paths.packageDir, 'dist_rust', 'mailer-bin'),
        plugins.path.join(paths.packageDir, 'rust', 'target', 'release', 'mailer-bin'),
        plugins.path.join(paths.packageDir, 'rust', 'target', 'debug', 'mailer-bin'),
      ],
      searchSystemPath: false,
    });

    // Forward lifecycle events
    this.bridge.on('ready', () => {
      this._running = true;
      logger.log('info', 'Rust security bridge is ready');
    });

    this.bridge.on('exit', (code: number | null, signal: string | null) => {
      this._running = false;
      logger.log('warn', `Rust security bridge exited (code=${code}, signal=${signal})`);
    });

    this.bridge.on('stderr', (line: string) => {
      logger.log('debug', `[rust-bridge] ${line}`);
    });
  }

  /** Get or create the singleton instance. */
  public static getInstance(): RustSecurityBridge {
    if (!RustSecurityBridge.instance) {
      RustSecurityBridge.instance = new RustSecurityBridge();
    }
    return RustSecurityBridge.instance;
  }

  /** Whether the Rust process is currently running and accepting commands. */
  public get running(): boolean {
    return this._running;
  }

  // -----------------------------------------------------------------------
  // Lifecycle
  // -----------------------------------------------------------------------

  /**
   * Spawn the Rust binary and wait for the ready signal.
   * @returns `true` if the binary started successfully, `false` otherwise.
   */
  public async start(): Promise<boolean> {
    if (this._running) {
      return true;
    }
    try {
      const ok = await this.bridge.spawn();
      this._running = ok;
      if (ok) {
        logger.log('info', 'Rust security bridge started');
      } else {
        logger.log('warn', 'Rust security bridge failed to start (binary not found or timeout)');
      }
      return ok;
    } catch (err) {
      logger.log('error', `Failed to start Rust security bridge: ${(err as Error).message}`);
      return false;
    }
  }

  /** Kill the Rust process. */
  public async stop(): Promise<void> {
    if (!this._running) {
      return;
    }
    try {
      this.bridge.kill();
      this._running = false;
      logger.log('info', 'Rust security bridge stopped');
    } catch (err) {
      logger.log('error', `Error stopping Rust security bridge: ${(err as Error).message}`);
    }
  }

  // -----------------------------------------------------------------------
  // Commands — thin typed wrappers over sendCommand
  // -----------------------------------------------------------------------

  /** Ping the Rust process. */
  public async ping(): Promise<boolean> {
    const res = await this.bridge.sendCommand('ping', {} as any);
    return res?.pong === true;
  }

  /** Get version information for all Rust crates. */
  public async getVersion(): Promise<IVersionInfo> {
    return this.bridge.sendCommand('version', {} as any);
  }

  /** Validate an email address. */
  public async validateEmail(email: string): Promise<IValidationResult> {
    return this.bridge.sendCommand('validateEmail', { email });
  }

  /** Detect bounce type from SMTP response / diagnostic code. */
  public async detectBounce(opts: {
    smtpResponse?: string;
    diagnosticCode?: string;
    statusCode?: string;
  }): Promise<IBounceDetection> {
    return this.bridge.sendCommand('detectBounce', opts);
  }

  /** Check IP reputation via DNSBL. */
  public async checkIpReputation(ip: string): Promise<IReputationResult> {
    return this.bridge.sendCommand('checkIpReputation', { ip });
  }

  /** Verify DKIM signatures on a raw email message. */
  public async verifyDkim(rawMessage: string): Promise<IDkimVerificationResult[]> {
    return this.bridge.sendCommand('verifyDkim', { rawMessage });
  }

  /** Sign an email with DKIM. */
  public async signDkim(opts: {
    rawMessage: string;
    domain: string;
    selector?: string;
    privateKey: string;
  }): Promise<{ header: string; signedMessage: string }> {
    return this.bridge.sendCommand('signDkim', opts);
  }

  /** Check SPF for a sender. */
  public async checkSpf(opts: {
    ip: string;
    heloDomain: string;
    hostname?: string;
    mailFrom: string;
  }): Promise<ISpfResult> {
    return this.bridge.sendCommand('checkSpf', opts);
  }

  /**
   * Compound email security verification: DKIM + SPF + DMARC in one IPC call.
   *
   * This is the preferred method for inbound email verification — it avoids
   * 3 sequential round-trips and correctly passes raw mail-auth types internally.
   */
  public async verifyEmail(opts: {
    rawMessage: string;
    ip: string;
    heloDomain: string;
    hostname?: string;
    mailFrom: string;
  }): Promise<IEmailSecurityResult> {
    return this.bridge.sendCommand('verifyEmail', opts);
  }
}

// Re-export interfaces for consumers
export type {
  IDkimVerificationResult,
  ISpfResult,
  IDmarcResult,
  IEmailSecurityResult,
  IValidationResult,
  IBounceDetection,
  IReputationResult as IRustReputationResult,
  IVersionInfo,
};
