import type { SecurityHeadersConfig } from "./security-types";

export class InvalidSecurityHeadersConfig extends Error {
  constructor(message: string) {
    super(message);
    this.name = "InvalidSecurityHeadersConfig";
    Object.setPrototypeOf(this, InvalidSecurityHeadersConfig.prototype);
  }
}

function validateOrigins(origins: string[], field: string): void {
  for (const origin of origins) {
    try {
      new URL(origin);
    } catch {
      throw new InvalidSecurityHeadersConfig(
        `Invalid URL in ${field}: "${origin}"`,
      );
    }
  }
}

export function buildSecurityHeaders(
  opts: SecurityHeadersConfig,
): Record<string, string> {
  const {
    mode,
    nonce,
    allowedConnectOrigins = [],
    allowedImgOrigins = [],
    allowedFontOrigins = [],
  } = opts;

  validateOrigins(allowedConnectOrigins, "allowedConnectOrigins");
  validateOrigins(allowedImgOrigins, "allowedImgOrigins");
  validateOrigins(allowedFontOrigins, "allowedFontOrigins");

  const imgSrc = ["'self'", "data:", ...allowedImgOrigins].join(" ");
  const fontSrc = ["'self'", ...allowedFontOrigins].join(" ");

  let csp: string;
  if (mode === "prod") {
    const nonceClause = nonce ? ` 'nonce-${nonce}'` : "";
    const connectSrc = ["'self'", ...allowedConnectOrigins].join(" ");
    csp = [
      "default-src 'self'",
      `script-src 'strict-dynamic'${nonceClause}`,
      "style-src 'self' 'unsafe-inline'",
      `img-src ${imgSrc}`,
      `font-src ${fontSrc}`,
      `connect-src ${connectSrc}`,
      "frame-ancestors 'none'",
      "base-uri 'self'",
      "form-action 'self'",
      "object-src 'none'",
    ].join("; ");
  } else {
    const connectSrc = [
      "'self'",
      "ws:",
      "localhost:*",
      "127.0.0.1:*",
      ...allowedConnectOrigins,
    ].join(" ");
    csp = [
      "default-src 'self'",
      "script-src 'unsafe-inline' 'unsafe-eval'",
      "style-src 'self' 'unsafe-inline'",
      `img-src ${imgSrc}`,
      `font-src ${fontSrc}`,
      `connect-src ${connectSrc}`,
      "frame-ancestors 'none'",
      "base-uri 'self'",
      "form-action 'self'",
      "object-src 'none'",
    ].join("; ");
  }

  return {
    "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
    "X-Frame-Options": "DENY",
    "X-Content-Type-Options": "nosniff",
    "Referrer-Policy": "strict-origin-when-cross-origin",
    "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
    "Content-Security-Policy": csp,
  };
}
