/**
 * Content Security Policy utilities for Electron app
 */

export interface CSPConfig {
  defaultSrc: readonly string[];
  scriptSrc: readonly string[];
  styleSrc: readonly string[];
  imgSrc: readonly string[];
  fontSrc: readonly string[];
  connectSrc: readonly string[];
  frameSrc: readonly string[];
  objectSrc: readonly string[];
  baseUri: readonly string[];
  formAction: readonly string[];
  frameAncestors: readonly string[];
}

type CSPDirective = keyof CSPConfig;

const NONCE_PLACEHOLDER = "'nonce-{NONCE}'" as const;
const NONCE_SIZE = 16;

export class CSPManager {
  private static nonce: string | null = null;

  private static readonly DEVELOPMENT_CONFIG: CSPConfig = {
    defaultSrc: ["'self'"],
    scriptSrc: [
      "'self'", 
      "'unsafe-inline'",  // Required for Vite in dev mode
      "'unsafe-eval'",    // Required for React DevTools
      "http://localhost:*"
    ],
    styleSrc: [
      "'self'", 
      "'unsafe-inline'",  // Required for styled-components/emotion
      "http://localhost:*"
    ],
    imgSrc: [
      "'self'", 
      "data:", 
      "blob:",
      "http://localhost:*"
    ],
    fontSrc: [
      "'self'", 
      "data:",
      "http://localhost:*"
    ],
    connectSrc: [
      "'self'",
      "http://localhost:*",
      "https://localhost:*",
      "ws://localhost:*",
      "wss://localhost:*"
    ],
    frameSrc: [
      "'self'",
      "http://localhost:8080",   // Guacamole in development
      "https://localhost:8080"
    ],
    objectSrc: ["'none'"],
    baseUri: ["'self'"],
    formAction: ["'self'"],
    frameAncestors: ["'none'"]
  };

  private static readonly PRODUCTION_CONFIG: CSPConfig = {
    defaultSrc: ["'self'"],
    scriptSrc: [
      "'self'",
      "'nonce-{NONCE}'"  // Nonce for inline scripts
    ],
    styleSrc: [
      "'self'",
      "'nonce-{NONCE}'",  // Nonce for inline styles
      "https://fonts.googleapis.com"
    ],
    imgSrc: [
      "'self'", 
      "data:", 
      "blob:"
    ],
    fontSrc: [
      "'self'", 
      "data:",
      "https://fonts.gstatic.com"
    ],
    connectSrc: [
      "'self'",
      `https://${import.meta.env.VITE_PROD_DOMAIN || 'mc.exbytestudios.com'}`,
      `wss://${import.meta.env.VITE_PROD_DOMAIN || 'mc.exbytestudios.com'}`
    ],
    frameSrc: [
      "'self'",
      `https://${import.meta.env.VITE_PROD_DOMAIN || 'mc.exbytestudios.com'}`
    ],
    objectSrc: ["'none'"],
    baseUri: ["'self'"],
    formAction: ["'self'"],
    frameAncestors: ["'none'"]
  };

  /**
   * Generate random nonce
   */
  static generateNonce(): string {
    const array = new Uint8Array(NONCE_SIZE);
    crypto.getRandomValues(array);
    return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
  }

  /**
   * Set nonce for use in CSP
   */
  static setNonce(nonce: string): void {
    this.nonce = nonce;
  }

  /**
   * Get current nonce
   */
  static getNonce(): string | null {
    return this.nonce;
  }

  /**
   * Get CSP configuration for current environment
   * Dynamically adds API URL from env variables
   */
  static getConfig(): CSPConfig {
    const baseConfig = this.getBaseConfig();
    const apiUrl = import.meta.env.VITE_API_URL;
    
    if (!apiUrl) {
      return baseConfig;
    }
    
    return this.addApiUrlToConfig(baseConfig, apiUrl);
  }

  private static getBaseConfig(): CSPConfig {
    return process.env.NODE_ENV === 'development' 
      ? this.DEVELOPMENT_CONFIG 
      : this.PRODUCTION_CONFIG;
  }

  private static addApiUrlToConfig(config: CSPConfig, apiUrl: string): CSPConfig {
    try {
      const url = new URL(apiUrl);
      const { domain, wssDomain } = this.buildDomains(url);
      
      const updatedConfig: CSPConfig = { ...config };
      
      if (!config.connectSrc.includes(domain)) {
        updatedConfig.connectSrc = [...config.connectSrc, domain, wssDomain];
      }
      
      if (!config.frameSrc.includes(domain)) {
        updatedConfig.frameSrc = [...config.frameSrc, domain];
      }
      
      console.log('CSP updated with API domain:', domain);
      return updatedConfig;
    } catch (error) {
      console.error('Failed to parse VITE_API_URL for CSP:', error);
      return config;
    }
  }

  private static buildDomains(url: URL): { domain: string; wssDomain: string } {
    const protocol = url.protocol.replace(':', '');
    const portSuffix = url.port ? `:${url.port}` : '';
    const domain = `${protocol}://${url.hostname}${portSuffix}`;
    const wssProtocol = protocol === 'https' ? 'wss' : 'ws';
    const wssDomain = `${wssProtocol}://${url.hostname}${portSuffix}`;
    
    return { domain, wssDomain };
  }

  /**
   * Generate CSP string from configuration
   */
  static generateCSPString(config: CSPConfig = this.getConfig()): string {
    const directives: string[] = [
      this.buildDirective('default-src', config.defaultSrc),
      this.buildDirective('script-src', config.scriptSrc),
      this.buildDirective('style-src', config.styleSrc),
      this.buildDirective('img-src', config.imgSrc),
      this.buildDirective('font-src', config.fontSrc),
      this.buildDirective('connect-src', config.connectSrc),
      this.buildDirective('frame-src', config.frameSrc),
      this.buildDirective('object-src', config.objectSrc),
      this.buildDirective('base-uri', config.baseUri),
      this.buildDirective('form-action', config.formAction),
      this.buildDirective('frame-ancestors', config.frameAncestors),
    ];

    return directives.join(' ').trim();
  }

  private static buildDirective(name: string, sources: readonly string[]): string {
    const processed = this.processSources([...sources]);
    return `${name} ${processed.join(' ')};`;
  }

  private static processSources(sources: string[]): string[] {
    return sources.map((source) => {
      if (source === NONCE_PLACEHOLDER && this.nonce) {
        return `'nonce-${this.nonce}'`;
      }
      return source;
    });
  }

  /**
   * Apply CSP to document
   */
  static applyCSP(config: CSPConfig = this.getConfig()): void {
    if (typeof document === 'undefined') return;

    // Generate nonce for production
    if (process.env.NODE_ENV === 'production' && !this.nonce) {
      this.nonce = this.generateNonce();
    }

    // Remove existing CSP meta tag
    const existingCSP = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
    if (existingCSP) {
      existingCSP.remove();
    }

    // Create new CSP meta tag
    const meta = document.createElement('meta');
    meta.setAttribute('http-equiv', 'Content-Security-Policy');
    meta.setAttribute('content', this.generateCSPString(config));
    
    document.head.appendChild(meta);

    // Add nonce to meta tag for JavaScript access
    if (this.nonce) {
      const nonceMeta = document.createElement('meta');
      nonceMeta.setAttribute('name', 'csp-nonce');
      nonceMeta.setAttribute('content', this.nonce);
      document.head.appendChild(nonceMeta);
    }
  }

  /**
   * Setup CSP violation reporting
   */
  static setupCSPReporting(): void {
    if (typeof document === 'undefined') return;

    document.addEventListener('securitypolicyviolation', (event) => {
      console.warn('CSP Violation:', {
        directive: event.violatedDirective,
        blockedURI: event.blockedURI,
        lineNumber: event.lineNumber,
        columnNumber: event.columnNumber,
        sourceFile: event.sourceFile,
        sample: event.sample
      });

      // In production, send to server
      if (process.env.NODE_ENV === 'production') {
        // fetch('/api/csp-violation', {
        //   method: 'POST',
        //   headers: { 'Content-Type': 'application/json' },
        //   body: JSON.stringify({
        //     directive: event.violatedDirective,
        //     blockedURI: event.blockedURI,
        //     sourceFile: event.sourceFile,
        //     timestamp: new Date().toISOString()
        //   })
        // });
      }
    });
  }

  /**
   * Validate CSP configuration
   */
  static validateConfig(config: CSPConfig): string[] {
    const errors: string[] = [];

    // Check for dangerous directives
    if (config.scriptSrc.includes("'unsafe-inline'")) {
      errors.push("script-src contains 'unsafe-inline' - security risk");
    }

    if (config.scriptSrc.includes("'unsafe-eval'")) {
      errors.push("script-src contains 'unsafe-eval' - security risk");
    }

    if (config.objectSrc.includes("'self'") || config.objectSrc.length === 0) {
      errors.push("object-src should be 'none' for security");
    }

    if (config.frameAncestors.includes("'self'")) {
      errors.push("frame-ancestors should be 'none' for clickjacking protection");
    }

    return errors;
  }
}

// Export convenience functions
export const generateCSP = CSPManager.generateCSPString;
export const applyCSP = CSPManager.applyCSP;
export const setupCSPReporting = CSPManager.setupCSPReporting; 