/**
 * ComplianceController — SOC 2 Type II and AGNTCY compliance endpoints.
 *
 * Handles endpoints defined in docs/openapi/compliance.yaml:
 *   GET /api/v1/audit/verify        — Audit chain integrity verification (auth required)
 *   GET /api/v1/compliance/controls — SOC 2 control status summary (public)
 *   GET /api/v1/compliance/report   — AGNTCY compliance report (auth required)
 *   GET /api/v1/compliance/agent-cards — AGNTCY agent card export (auth required)
 */

import { Request, Response, NextFunction } from 'express';
import { AuditVerificationService } from '../services/AuditVerificationService.js';
import { ComplianceService } from '../services/ComplianceService.js';
import { getAllControlStatuses } from '../services/ComplianceStatusStore.js';
import { ValidationError } from '../utils/errors.js';
import { ITokenPayload } from '../types/index.js';

// ============================================================================
// Helpers
// ============================================================================

/**
 * Returns `true` if the given string is a valid ISO 8601 date-time string.
 * Uses `Date.parse` — valid ISO 8601 strings produce a finite number;
 * invalid strings produce `NaN`.
 *
 * @param value - The string to validate.
 * @returns `true` if valid ISO 8601 date-time; `false` otherwise.
 */
function isValidIsoDateTime(value: string): boolean {
  const parsed = Date.parse(value);
  return !isNaN(parsed);
}

// ============================================================================
// Controller
// ============================================================================

/**
 * Controller for SOC 2 Type II and AGNTCY compliance API endpoints.
 * Exposes audit chain verification, live control status reporting,
 * AGNTCY compliance report generation, and agent card export.
 */
export class ComplianceController {
  /**
   * @param auditVerificationService - Service for cryptographic audit chain verification.
   * @param complianceService - Service for AGNTCY compliance report and agent card generation.
   */
  constructor(
    private readonly auditVerificationService: AuditVerificationService,
    private readonly complianceService: ComplianceService,
  ) {}

  // ──────────────────────────────────────────────────────────────────────────
  // Handlers
  // ──────────────────────────────────────────────────────────────────────────

  /**
   * GET /api/v1/audit/verify
   *
   * Verifies the cryptographic integrity of the audit event hash chain.
   * Accepts optional `fromDate` and `toDate` ISO 8601 query parameters to restrict
   * the verification window. Returns 200 regardless of whether the chain is intact —
   * check `verified` in the response body.
   *
   * Requires Bearer token with `audit:read` scope (enforced by route middleware).
   *
   * @param req - Express request; optional `fromDate` and `toDate` query params.
   * @param res - Express response.
   * @param next - Express next function.
   */
  async verifyAuditChain(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { fromDate, toDate } = req.query as Record<string, string | undefined>;

      // Validate fromDate if provided
      if (fromDate !== undefined && !isValidIsoDateTime(fromDate)) {
        throw new ValidationError('Invalid query parameter value.', {
          field: 'fromDate',
          reason: 'Must be a valid ISO 8601 date-time string (e.g. 2026-03-01T00:00:00.000Z).',
        });
      }

      // Validate toDate if provided
      if (toDate !== undefined && !isValidIsoDateTime(toDate)) {
        throw new ValidationError('Invalid query parameter value.', {
          field: 'toDate',
          reason: 'Must be a valid ISO 8601 date-time string (e.g. 2026-03-31T23:59:59.999Z).',
        });
      }

      // Validate date range ordering
      if (fromDate !== undefined && toDate !== undefined) {
        if (new Date(fromDate) > new Date(toDate)) {
          throw new ValidationError('Invalid date range.', {
            reason: 'fromDate must be before or equal to toDate.',
          });
        }
      }

      const result = await this.auditVerificationService.verifyChain(fromDate, toDate);

      res.status(200).json(result);
    } catch (err) {
      next(err);
    }
  }

  /**
   * GET /api/v1/compliance/controls
   *
   * Returns a live status snapshot for all five in-scope SOC 2 Trust Services
   * Criteria controls. Status values are maintained by background jobs
   * (SecretsRotationJob, AuditChainVerificationJob) via ComplianceStatusStore.
   *
   * No authentication required — this is a public health-style endpoint.
   * Sets `Cache-Control: public, max-age=60` to permit 60-second downstream caching.
   *
   * @param _req - Express request (unused).
   * @param res - Express response.
   * @param next - Express next function.
   */
  async getComplianceControls(
    _req: Request,
    res: Response,
    next: NextFunction,
  ): Promise<void> {
    try {
      const controls = getAllControlStatuses();

      res.setHeader('Cache-Control', 'public, max-age=60');
      res.status(200).json({ controls });
    } catch (err) {
      next(err);
    }
  }

  /**
   * GET /api/v1/compliance/report
   *
   * Generates and returns an AGNTCY compliance report for the authenticated tenant.
   * The report covers agent-identity verification and audit-trail integrity.
   * Reports are cached in Redis for 5 minutes; sets `X-Cache: HIT` when served from cache.
   *
   * Requires Bearer token authentication (tenant extracted from req.user.sub).
   *
   * @param req - Express request; tenant derived from authenticated user context.
   * @param res - Express response.
   * @param next - Express next function.
   */
  async getComplianceReport(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const user = req.user as ITokenPayload | undefined;
      const tenantId = user?.organization_id ?? user?.sub ?? '';

      const report = await this.complianceService.generateReport(tenantId);

      if (report.from_cache === true) {
        res.setHeader('X-Cache', 'HIT');
      }

      res.status(200).json(report);
    } catch (err) {
      next(err);
    }
  }

  /**
   * GET /api/v1/compliance/agent-cards
   *
   * Exports all active agents for the authenticated tenant as AGNTCY-standard
   * agent card JSON objects.
   *
   * Requires Bearer token authentication (tenant extracted from req.user.sub).
   *
   * @param req - Express request; tenant derived from authenticated user context.
   * @param res - Express response.
   * @param next - Express next function.
   */
  async exportAgentCards(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const user = req.user as ITokenPayload | undefined;
      const tenantId = user?.organization_id ?? user?.sub ?? '';

      const cards = await this.complianceService.exportAgentCards(tenantId);

      res.status(200).json(cards);
    } catch (err) {
      next(err);
    }
  }
}
