import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
import { Request, Response, NextFunction } from 'express';
import { env } from '../config/env';
import { logger } from '../utils/logger';
import type { TLSSocket } from 'tls';

/**
 * Load allowed fingerprints from env var or from the auto-generated config file.
 * The config file is written during phone-home cert installation.
 */
function loadAllowedFingerprints(): string[] {
  // First check env var
  if (env.ALLOWED_CCP_FINGERPRINTS) {
    return env.ALLOWED_CCP_FINGERPRINTS.split(',').map((f) => f.trim().toLowerCase());
  }

  // Fall back to the auto-generated fingerprint file from phone-home registration
  try {
    const configPath = path.join(env.AGENT_DATA_DIR, 'ccp-fingerprint');
    const fingerprint = fs.readFileSync(configPath, 'utf-8').trim().toLowerCase();
    if (fingerprint) return [fingerprint];
  } catch {
    // No fingerprint file — fingerprint pinning not available
  }

  return [];
}

// Cache the fingerprints at startup — reload requires restart
const allowedFingerprints = loadAllowedFingerprints();

/**
 * mTLS authentication middleware.
 * Verifies that the connecting client presented a valid certificate
 * signed by the trusted CA, and checks against allowed fingerprints.
 */
export function mtlsAuth(req: Request, res: Response, next: NextFunction) {
  const socket = req.socket as TLSSocket;

  // Check that the client presented a certificate and it was authorized by the TLS layer
  if (!socket.authorized) {
    const authError = socket.authorizationError;
    logger.warn(`[mtls] Client certificate rejected: ${authError}`);
    res.status(401).json({ error: 'UNAUTHORIZED', message: 'Invalid client certificate' });
    return;
  }

  const peerCert = socket.getPeerCertificate();
  if (!peerCert || !peerCert.raw) {
    logger.warn('[mtls] No peer certificate presented');
    res.status(401).json({ error: 'UNAUTHORIZED', message: 'No client certificate' });
    return;
  }

  // SECURITY: Check fingerprint against allowed list (env var or auto-generated file)
  if (allowedFingerprints.length > 0) {
    const fingerprint = crypto.createHash('sha256').update(peerCert.raw).digest('hex');
    if (!allowedFingerprints.includes(fingerprint)) {
      logger.warn(`[mtls] Client fingerprint ${fingerprint.substring(0, 16)}... not in allowed list`);
      res.status(403).json({ error: 'FORBIDDEN', message: 'Client certificate not authorized' });
      return;
    }
  } else {
    // No fingerprint pinning configured — log a warning but allow (CA validation is still enforced)
    logger.warn('[mtls] No fingerprint pinning configured — relying on CA chain validation only');
  }

  next();
}
