// Policy Engine - Evaluates detections against policy rules
// Determines actions: block, warn, allow, quarantine, report

export class PolicyEngine {
  constructor(config = {}) {
    this.policies = new Map();
    this.defaultPolicy = this.createDefaultPolicy();
    this.config = config;
  }

  /**
   * Load a policy from JSON
   * @param {string} policyId - Unique policy identifier
   * @param {object} policyData - Policy configuration
   */
  loadPolicy(policyId, policyData) {
    this.validatePolicy(policyData);
    this.policies.set(policyId, policyData);
  }

  /**
   * Load policy from remote endpoint
   * @param {string} endpoint - URL to fetch policy from
   * @param {string} policyId - Policy identifier
   */
  async loadPolicyFromEndpoint(endpoint, policyId = 'remote') {
    try {
      const response = await fetch(endpoint, {
        headers: {
          'Content-Type': 'application/json'
        }
      });

      if (!response.ok) {
        throw new Error(`Failed to fetch policy: ${response.status}`);
      }

      const policyData = await response.json();
      this.loadPolicy(policyId, policyData);
      return true;
    } catch (error) {
      console.error('Failed to load remote policy:', error);
      return false;
    }
  }

  /**
   * Evaluate detection against policy
   * @param {object} detection - Detection result from analyzer
   * @param {string} policyId - Policy to evaluate against (defaults to 'default')
   * @returns {object} - Action decision with enforcement details
   */
  evaluateDetection(detection, policyId = 'default') {
    const policy = this.policies.get(policyId) || this.defaultPolicy;

    // If no findings, allow
    if (!detection.matched || detection.findings.length === 0) {
      return {
        action: 'allow',
        reason: 'No threats detected',
        enforcement: null
      };
    }

    // Evaluate each finding against policy rules
    let maxSeverityAction = 'allow';
    let maxSeverityLevel = 0;
    const matchedRules = [];

    for (const finding of detection.findings) {
      const ruleMatch = this.matchPolicyRule(finding, policy);

      if (ruleMatch) {
        matchedRules.push(ruleMatch);

        const severityLevel = this.getSeverityLevel(ruleMatch.action);
        if (severityLevel > maxSeverityLevel) {
          maxSeverityLevel = severityLevel;
          maxSeverityAction = ruleMatch.action;
        }
      }
    }

    // Calculate overall action based on risk score and matched rules
    const riskBasedAction = this.getRiskBasedAction(detection.riskScore, policy);
    const finalAction = this.resolveFinalAction(maxSeverityAction, riskBasedAction);

    return {
      action: finalAction,
      reason: this.getActionReason(detection, matchedRules),
      enforcement: this.getEnforcementDetails(finalAction, detection),
      matchedRules,
      riskScore: detection.riskScore,
      findings: detection.findings
    };
  }

  /**
   * Match finding against policy rules
   * @param {object} finding - Individual finding
   * @param {object} policy - Policy to match against
   * @returns {object|null} - Matched rule or null
   */
  matchPolicyRule(finding, policy) {
    const rules = policy.rules || [];

    for (const rule of rules) {
      // Match by severity
      if (rule.severity && finding.severity === rule.severity) {
        return rule;
      }

      // Match by detector name
      if (rule.detector && finding.metadata?.technique?.includes(rule.detector)) {
        return rule;
      }

      // Match by technique/pattern
      if (rule.technique && finding.metadata?.technique === rule.technique) {
        return rule;
      }

      // Match by category
      if (rule.category && finding.category === rule.category) {
        return rule;
      }
    }

    // Fall back to default rule
    return policy.defaultRule || null;
  }

  /**
   * Get action based on risk score
   * @param {number} riskScore - Overall risk score
   * @param {object} policy - Policy configuration
   * @returns {string} - Action to take
   */
  getRiskBasedAction(riskScore, policy) {
    const thresholds = policy.riskThresholds || {
      critical: 15,  // Block
      high: 10,      // Warn
      medium: 5,     // Report
      low: 0         // Allow
    };

    if (riskScore >= thresholds.critical) return 'block';
    if (riskScore >= thresholds.high) return 'warn';
    if (riskScore >= thresholds.medium) return 'report';
    return 'allow';
  }

  /**
   * Resolve final action when multiple actions are suggested
   * @param {string} ruleAction - Action from matched rule
   * @param {string} riskAction - Action from risk score
   * @returns {string} - Final action
   */
  resolveFinalAction(ruleAction, riskAction) {
    const actionPriority = {
      'block': 4,
      'quarantine': 3,
      'warn': 2,
      'report': 1,
      'allow': 0
    };

    const ruleLevel = actionPriority[ruleAction] || 0;
    const riskLevel = actionPriority[riskAction] || 0;

    // Return the more severe action
    return ruleLevel >= riskLevel ? ruleAction : riskAction;
  }

  /**
   * Get severity level for action comparison
   * @param {string} action - Action name
   * @returns {number} - Severity level
   */
  getSeverityLevel(action) {
    const levels = {
      'block': 4,
      'quarantine': 3,
      'warn': 2,
      'report': 1,
      'allow': 0
    };

    return levels[action] || 0;
  }

  /**
   * Get human-readable reason for action
   * @param {object} detection - Detection result
   * @param {array} matchedRules - Matched policy rules
   * @returns {string} - Action reason
   */
  getActionReason(detection, matchedRules) {
    if (matchedRules.length === 0) {
      return `Risk score ${detection.riskScore} requires action`;
    }

    const topRule = matchedRules[0];
    return topRule.description || `Matched policy rule: ${topRule.name}`;
  }

  /**
   * Get enforcement details for action
   * @param {string} action - Action to enforce
   * @param {object} detection - Detection result
   * @returns {object} - Enforcement details
   */
  getEnforcementDetails(action, detection) {
    switch (action) {
      case 'block':
        return {
          type: 'block',
          message: 'This page has been blocked due to security threats',
          redirectTo: 'chrome-extension://blocked.html',
          showWarning: true
        };

      case 'quarantine':
        return {
          type: 'quarantine',
          message: 'This page contains suspicious content',
          restrictActions: ['forms', 'downloads', 'clipboard'],
          showWarning: true
        };

      case 'warn':
        return {
          type: 'warn',
          message: 'Warning: This page may contain security threats',
          allowContinue: true,
          showWarning: true
        };

      case 'report':
        return {
          type: 'report',
          message: null,
          silent: true,
          logOnly: true
        };

      case 'allow':
      default:
        return null;
    }
  }

  /**
   * Validate policy structure
   * @param {object} policy - Policy to validate
   * @throws {Error} - If policy is invalid
   */
  validatePolicy(policy) {
    if (!policy.version) {
      throw new Error('Policy must have a version');
    }

    if (!policy.rules && !policy.defaultRule) {
      throw new Error('Policy must have rules or defaultRule');
    }

    // Validate each rule
    if (policy.rules) {
      for (const rule of policy.rules) {
        if (!rule.action) {
          throw new Error('Policy rule must have an action');
        }

        const validActions = ['block', 'quarantine', 'warn', 'report', 'allow'];
        if (!validActions.includes(rule.action)) {
          throw new Error(`Invalid action: ${rule.action}`);
        }
      }
    }
  }

  /**
   * Create default policy
   * @returns {object} - Default policy configuration
   */
  createDefaultPolicy() {
    return {
      version: '1.0.0',
      name: 'Default Policy',
      description: 'Default security policy for PhishProtect',
      riskThresholds: {
        critical: 15,
        high: 10,
        medium: 5,
        low: 0
      },
      rules: [
        {
          name: 'Block Critical Threats',
          severity: 'critical',
          action: 'block',
          description: 'Block all critical severity threats'
        },
        {
          name: 'Warn High Threats',
          severity: 'high',
          action: 'warn',
          description: 'Warn user about high severity threats'
        },
        {
          name: 'Report Medium Threats',
          severity: 'medium',
          action: 'report',
          description: 'Report medium severity threats'
        }
      ],
      defaultRule: {
        name: 'Default Allow',
        action: 'allow',
        description: 'Allow by default if no rules match'
      }
    };
  }

  /**
   * Get all loaded policies
   * @returns {array} - Array of policy IDs
   */
  getLoadedPolicies() {
    return Array.from(this.policies.keys());
  }

  /**
   * Remove a policy
   * @param {string} policyId - Policy to remove
   */
  removePolicy(policyId) {
    this.policies.delete(policyId);
  }

  /**
   * Export policy as JSON
   * @param {string} policyId - Policy to export
   * @returns {object} - Policy data
   */
  exportPolicy(policyId) {
    return this.policies.get(policyId);
  }
}
