// Autofill Harvesting Detector - Detects hidden field autofill attacks
// Browser Techniques Line 198: Autofill harvesting
// Hidden forms attempt to trigger autofill and read values

import { BaseDetector } from '../base-detector.js';

export class AutofillHarvestingDetector extends BaseDetector {
  constructor(config = {}) {
    super({
      name: 'autofill-harvesting',
      category: 'data-theft',
      version: '1.0.0',
      description: 'Detects autofill harvesting attempts using hidden forms',
      weight: 3,
      severity: 'high',
      ...config
    });
  }

  async detect(target, ruleRegistry) {
    const findings = [];

    // This detector requires DOM data from content script
    const forms = target.forms || [];
    const inputs = target.inputs || [];

    if (forms.length === 0 && inputs.length === 0) {
      return this.createResult(false, [], 0);
    }

    const rules = ruleRegistry.getRules('data-theft', 'autofill-harvesting');

    if (!rules) {
      return this.createResult(false, [], 0);
    }

    // 1. Detect hidden forms with autocomplete-enabled fields
    for (const form of forms) {
      if (this.isHiddenForm(form) && this.hasAutocompleteFields(form, rules)) {
        findings.push(this.createFinding({
          pattern: 'hidden-form-autofill',
          matched: form.id || 'hidden form',
          description: 'Hidden form with autocomplete-enabled sensitive fields',
          severity: 'critical',
          weight: 5,
          metadata: {
            formId: form.id,
            isHidden: form.isHidden,
            autocompleteFieldCount: this.countAutocompleteFields(form, rules),
            technique: 'autofill-harvesting-hidden'
          }
        }));
      }
    }

    // 2. Check for off-screen positioned input fields
    for (const input of inputs) {
      if (this.isOffScreen(input) && this.isSensitiveAutocompleteField(input, rules)) {
        findings.push(this.createFinding({
          pattern: 'offscreen-autofill-field',
          matched: input.name || input.autocomplete,
          description: 'Off-screen input field with sensitive autocomplete attribute',
          severity: 'high',
          weight: 4,
          metadata: {
            inputName: input.name,
            autocomplete: input.autocomplete,
            position: { top: input.top, left: input.left },
            technique: 'autofill-harvesting-offscreen'
          }
        }));
      }
    }

    // 3. Detect invisible inputs (opacity:0 or visibility:hidden)
    for (const input of inputs) {
      if (this.isInvisible(input) && this.isSensitiveAutocompleteField(input, rules)) {
        findings.push(this.createFinding({
          pattern: 'invisible-autofill-field',
          matched: input.name || input.autocomplete,
          description: 'Invisible input field attempting to harvest autofill data',
          severity: 'critical',
          weight: 5,
          metadata: {
            inputName: input.name,
            autocomplete: input.autocomplete,
            opacity: input.opacity,
            visibility: input.visibility,
            technique: 'autofill-harvesting-invisible'
          }
        }));
      }
    }

    // 4. Detect forms that auto-submit after autofill
    for (const form of forms) {
      if (form.autoSubmitsAfterAutofill) {
        findings.push(this.createFinding({
          pattern: 'auto-submit-after-autofill',
          matched: form.action,
          description: 'Form automatically submits after autofill is triggered',
          severity: 'critical',
          weight: 5,
          metadata: {
            formAction: form.action,
            formId: form.id,
            technique: 'autofill-harvesting-autosubmit'
          }
        }));
      }
    }

    // 5. Detect excessive autocomplete fields in single form
    for (const form of forms) {
      const sensitiveFieldCount = this.countSensitiveAutocompleteFields(form, rules);
      const threshold = rules.excessiveAutocompleteThreshold || 5;

      if (sensitiveFieldCount > threshold) {
        findings.push(this.createFinding({
          pattern: 'excessive-autocomplete-fields',
          matched: `${sensitiveFieldCount} sensitive autocomplete fields`,
          description: 'Form contains excessive number of autocomplete-enabled sensitive fields',
          severity: 'high',
          weight: 4,
          metadata: {
            fieldCount: sensitiveFieldCount,
            formId: form.id,
            technique: 'autofill-harvesting-excessive'
          }
        }));
      }
    }

    // 6. Detect rapid form creation/deletion patterns
    if (target.rapidFormChanges && target.rapidFormChanges > (rules.rapidFormChangeThreshold || 3)) {
      findings.push(this.createFinding({
        pattern: 'rapid-form-manipulation',
        matched: `${target.rapidFormChanges} rapid form changes`,
        description: 'Rapid form creation/deletion pattern (autofill scraping)',
        severity: 'high',
        weight: 4,
        metadata: {
          changeCount: target.rapidFormChanges,
          technique: 'autofill-harvesting-rapid'
        }
      }));
    }

    // 7. Detect forms with only autocomplete fields (no visible UI)
    for (const form of forms) {
      if (form.visibleInputCount === 0 && this.hasAutocompleteFields(form, rules)) {
        findings.push(this.createFinding({
          pattern: 'autocomplete-only-form',
          matched: form.id || 'hidden autocomplete form',
          description: 'Form contains only autocomplete fields with no visible inputs',
          severity: 'critical',
          weight: 5,
          metadata: {
            formId: form.id,
            autocompleteFieldCount: this.countAutocompleteFields(form, rules),
            technique: 'autofill-harvesting-noui'
          }
        }));
      }
    }

    // 8. Detect zero-size input fields with autocomplete
    for (const input of inputs) {
      if (this.isZeroSize(input) && this.isSensitiveAutocompleteField(input, rules)) {
        findings.push(this.createFinding({
          pattern: 'zerosize-autofill-field',
          matched: input.name || input.autocomplete,
          description: 'Zero-size input field with autocomplete attribute',
          severity: 'critical',
          weight: 5,
          metadata: {
            inputName: input.name,
            autocomplete: input.autocomplete,
            width: input.width,
            height: input.height,
            technique: 'autofill-harvesting-zerosize'
          }
        }));
      }
    }

    const matched = findings.length > 0;
    const riskScore = findings.reduce((sum, f) => sum + (f.weight || 1), 0);

    return this.createResult(matched, findings, riskScore);
  }

  isHiddenForm(form) {
    return form.isHidden ||
           form.display === 'none' ||
           form.visibility === 'hidden' ||
           form.opacity < 0.1;
  }

  hasAutocompleteFields(form, rules) {
    if (!form.inputs) return false;

    const sensitiveAutocomplete = rules.sensitiveAutocompleteAttributes || [
      'name', 'email', 'tel', 'address', 'cc-number', 'cc-exp', 'cc-csc'
    ];

    return form.inputs.some(input =>
      input.autocomplete &&
      sensitiveAutocomplete.some(attr =>
        input.autocomplete.toLowerCase().includes(attr)
      )
    );
  }

  countAutocompleteFields(form, rules) {
    if (!form.inputs) return 0;

    const sensitiveAutocomplete = rules.sensitiveAutocompleteAttributes || [
      'name', 'email', 'tel', 'address', 'cc-number', 'cc-exp', 'cc-csc'
    ];

    return form.inputs.filter(input =>
      input.autocomplete &&
      sensitiveAutocomplete.some(attr =>
        input.autocomplete.toLowerCase().includes(attr)
      )
    ).length;
  }

  countSensitiveAutocompleteFields(form, rules) {
    return this.countAutocompleteFields(form, rules);
  }

  isSensitiveAutocompleteField(input, rules) {
    if (!input.autocomplete) return false;

    const sensitiveAutocomplete = rules.sensitiveAutocompleteAttributes || [
      'name', 'email', 'tel', 'address-line', 'postal-code',
      'cc-number', 'cc-exp', 'cc-csc', 'cc-name',
      'username', 'current-password', 'new-password'
    ];

    const autocompleteLower = input.autocomplete.toLowerCase();
    return sensitiveAutocomplete.some(attr => autocompleteLower.includes(attr));
  }

  isOffScreen(input) {
    if (!input.top && !input.left) return false;

    return input.top < -100 ||
           input.left < -100 ||
           input.top > 10000 ||
           input.left > 10000;
  }

  isInvisible(input) {
    return input.visibility === 'hidden' ||
           input.display === 'none' ||
           input.opacity < 0.1;
  }

  isZeroSize(input) {
    return (input.width !== undefined && input.width < 1) ||
           (input.height !== undefined && input.height < 1);
  }
}
