(function () {
  'use strict';

  const FIELD_ATTRS = [
    'name', 'id', 'type', 'autocomplete', 'placeholder', 'aria-label',
    'inputmode', 'maxlength', 'class', 'readonly', 'disabled',
  ];

  let fieldCache = [];
  let lastScan = 0;

  function getLabelText(element) {
    if (element.hasAttribute('aria-label')) {
      return element.getAttribute('aria-label');
    }
    const id = element.id;
    if (id) {
      const label = document.querySelector(`label[for="${CSS.escape(id)}"]`);
      if (label) return label.textContent.trim();
    }
    const parent = element.closest('label');
    if (parent) return parent.textContent.trim();
    const wrappingLabel = element.parentElement?.closest('label');
    if (wrappingLabel) return wrappingLabel.textContent.trim();
    const nearestLabel = element.closest('div, li, td, p, span')
        ?.querySelector('label');
    if (nearestLabel) return nearestLabel.textContent.trim();
    const prevSibling = element.previousElementSibling;
    if (prevSibling && (prevSibling.tagName === 'LABEL' ||
        prevSibling.tagName === 'SPAN' || prevSibling.tagName === 'DIV')) {
      return prevSibling.textContent.trim();
    }
    return null;
  }

  function getSectionTitle(element) {
    const fieldset = element.closest('fieldset');
    if (fieldset) {
      const legend = fieldset.querySelector('legend');
      if (legend) return legend.textContent.trim();
    }
    const section = element.closest('section, div[role="group"], form > div');
    if (section) {
      const heading = section.querySelector('h1, h2, h3, h4, h5, h6');
      if (heading) return heading.textContent.trim();
    }
    return null;
  }

  function getSelectOptions(select) {
    return Array.from(select.options).map(o => o.text.trim()).filter(Boolean);
  }

  function isVisible(element) {
    if (element.type === 'hidden') return false;
    const rect = element.getBoundingClientRect();
    if (rect.width === 0 && rect.height === 0) return false;
    const style = window.getComputedStyle(element);
    if (style.display === 'none') return false;
    if (style.visibility === 'hidden') return false;
    return true;
  }

  function gatherFieldData(element) {
    const tagName = element.tagName.toLowerCase();
    const isInput = tagName === 'input' || tagName === 'textarea';
    const isSelect = tagName === 'select';

    if (!isInput && !isSelect) return null;

    const field = {
      id: `${tagName}_${element.name || element.id || Math.random().toString(36).slice(2)}`,
      origin: `web:${location.origin}`,
      formId: element.form?.id || element.form?.name || null,
      tagName: tagName,
      inputType: isInput ? (element.type || 'text').toLowerCase() : null,
      name: element.name || null,
      htmlId: element.id || null,
      className: element.className || null,
      autocomplete: element.autocomplete || null,
      placeholder: element.placeholder || null,
      label: getLabelText(element),
      ariaLabel: element.getAttribute('aria-label'),
      nearbyText: null,
      sectionTitle: getSectionTitle(element),
      locale: document.documentElement.lang || null,
      options: isSelect ? getSelectOptions(element) : [],
      visible: isVisible(element),
      enabled: !element.disabled,
      readonly: element.readOnly || element.hasAttribute('readonly'),
      userEdited: false,
    };

    const container = element.closest('div, li, td, p');
    if (container && container.textContent.trim().length < 200) {
      field.nearbyText = container.textContent.trim();
    } else if (element.parentElement &&
        element.parentElement.textContent.trim().length < 200) {
      field.nearbyText = element.parentElement.textContent.trim();
    }

    return field;
  }

  function scanFormFields() {
    const forms = document.querySelectorAll('form');
    const fields = [];

    forms.forEach(form => {
      const elements = form.querySelectorAll(
          'input:not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), select, textarea');
      elements.forEach(el => {
        const data = gatherFieldData(el);
        if (data) {
          data.formId = form.id || form.name || null;
          fields.push(data);
        }
      });
    });

    document.querySelectorAll(
        'input:not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="hidden"]), select, textarea'
    ).forEach(el => {
      if (!el.closest('form') && !fields.find(f => f.id.endsWith(el.name || el.id || ''))) {
        const data = gatherFieldData(el);
        if (data) fields.push(data);
      }
    });

    fieldCache = fields;
    lastScan = Date.now();
    return fields;
  }

  function getFieldValue(fieldId) {
    const el = findElementByFieldId(fieldId);
    if (!el) return null;
    if (el.tagName === 'SELECT') return el.value;
    return el.value;
  }

  function findElementByFieldId(fieldId) {
    for (const el of document.querySelectorAll('input, select, textarea')) {
      const tag = el.tagName.toLowerCase();
      const id = `${tag}_${el.name || el.id || ''}`;
      if (id === fieldId) return el;
    }
    return null;
  }

  function fillField(fieldId, value) {
    const el = findElementByFieldId(fieldId);
    if (!el) return false;

    const tag = el.tagName.toLowerCase();
    if (tag === 'select') {
      for (let i = 0; i < el.options.length; i++) {
        if (el.options[i].value === value ||
            el.options[i].text === value) {
          el.selectedIndex = i;
          break;
        }
      }
    } else {
      const nativeSetter = Object.getOwnPropertyDescriptor(
          HTMLInputElement.prototype, 'value');
      if (nativeSetter && nativeSetter.set) {
        nativeSetter.set.call(el, value);
      } else {
        el.value = value;
      }
    }

    el.dispatchEvent(new Event('input', {bubbles: true}));
    el.dispatchEvent(new Event('change', {bubbles: true}));
    return true;
  }

  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    switch (message.type) {
      case 'dudou.autofill.scan':
        const fields = scanFormFields();
        sendResponse({fields});
        break;
      case 'dudou.autofill.getFields':
        sendResponse({fields: fieldCache});
        break;
      case 'dudou.autofill.fill':
        const results = {};
        if (message.payload && message.payload.fills) {
          message.payload.fills.forEach(fill => {
            results[fill.fieldId] = fillField(fill.fieldId, fill.value);
          });
        }
        sendResponse({results});
        break;
      case 'dudou.autofill.getValue':
        sendResponse({value: getFieldValue(message.payload.fieldId)});
        break;
      case 'dudou.autofill.getFormValues':
        const values = captureFormValues(document);
        sendResponse({values});
        break;
    }
  });

  function classifyField(field) {
    const locale = document.documentElement.lang || navigator.language || 'en';
    if (typeof window.dudouClassifyField !== 'function') return null;
    try {
      const result = JSON.parse(window.dudouClassifyField(JSON.stringify(field), locale));
      return result.error ? null : result;
    } catch(e) {
      return null;
    }
  }

  function elemToFieldContext(el) {
    const tagName = el.tagName.toLowerCase();
    return {
      id: `${tagName}_${el.name || el.id || Math.random().toString(36).slice(2)}`,
      origin: `web:${location.origin}`,
      tagName: tagName,
      inputType: el.type || 'text',
      name: el.name || null,
      htmlId: el.id || null,
      className: el.className || null,
      autocomplete: el.autocomplete || null,
      placeholder: el.placeholder || null,
      label: getLabelText(el),
      ariaLabel: el.getAttribute('aria-label'),
      nearbyText: null,
      sectionTitle: getSectionTitle(el),
      visible: isVisible(el),
      enabled: !el.disabled,
      readonly: el.readOnly || el.hasAttribute('readonly'),
    };
  }

  function captureFormValues(context) {
    const result = {};
    const els = context.querySelectorAll('input, select, textarea');
    els.forEach(el => {
      const ac = el.autocomplete || '';
      const name = el.name || '';
      const type = (el.type || '').toLowerCase();
      if (type === 'hidden' || type === 'submit' || type === 'button' || type === 'reset' || type === 'image') return;
      if (ac) {
        result[ac] = el.value;
      } else if (name) {
        result[name] = el.value;
      }
    });
    result._origin = `web:${location.origin}`;
    result._url = location.href;
    return result;
  }

  function findFormByField(el) {
    if (el.form) return el.form;
    return el.closest('form');
  }

  function getAutofillableFields(form) {
    const fields = {};
    if (!form) return fields;
    form.querySelectorAll('input, select, textarea').forEach(el => {
      const ac = (el.autocomplete || '').toLowerCase();
      if (ac && ['username', 'current-password', 'new-password', 'email', 'tel', 'name', 'given-name', 'family-name'].includes(ac)) {
        fields[ac] = el;
      }
    });
    return fields;
  }

  let focusedCandidate = null;

  function handleFieldFocus(el) {
    if (el.tagName !== 'INPUT') return false;
    if (focusedCandidate && focusedCandidate.el === el) return true;
    const ctx = elemToFieldContext(el);
    const decision = classifyField(ctx);
    if (!decision || !decision.fieldType || decision.confidence < 0.50) {
      if (focusedCandidate) {
        focusedCandidate = null;
        window.__dudouAutofill && window.__dudouAutofill.hideDropdown();
      }
      return false;
    }
    focusedCandidate = {el, fieldType: decision.wireType || decision.fieldType};
    const origin = ctx.origin;
    if (window.__dudouAutofill) {
      window.__dudouAutofill.showBadge(el);
      window.__dudouAutofill.checkAndShowFill(el, origin, focusedCandidate.fieldType);
    }
    return true;
  }

  document.addEventListener('focusin', (e) => {
    handleFieldFocus(e.target);
  });

  document.addEventListener('click', (e) => {
    handleFieldFocus(e.target);
  }, true);

  let saveShownForOrigin = new Set();

  function findFormCredentials(form) {
    let usernameValue, passwordValue;
    const els = form.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"])');
    els.forEach(el => {
      if (!el.value) return;
      const ctx = elemToFieldContext(el);
      const d = classifyField(ctx);
      if (!d || !d.fieldType || d.confidence < 0.50) return;
      const ft = d.wireType || d.fieldType;
      if (ft === 'username' || ft === 'email') {
        usernameValue = el.value;
      } else if (ft === 'current-password' || ft === 'new-password') {
        passwordValue = el.value;
      }
    });
    if (!usernameValue || !passwordValue) return null;
    const out = captureFormValues(form);
    out._username = usernameValue;
    out._password = passwordValue;
    return out;
  }

  function maybeOfferSave(triggerEl) {
    const form = triggerEl.form || triggerEl.closest('form');
    if (!form) return;
    const origin = `web:${location.origin}`;
    if (saveShownForOrigin.has(origin)) return;
    const values = findFormCredentials(form);
    if (!values) return;
    window.__dudouAutofill && window.__dudouAutofill.checkAndShowSave(values, () => saveShownForOrigin.add(origin));
  }

  document.addEventListener('change', (e) => {
    const el = e.target;
    if (el.tagName !== 'INPUT') return;
    if (!el.value || el.value.length < 2) return;
    const ctx = elemToFieldContext(el);
    const decision = classifyField(ctx);
    if (!decision || !decision.fieldType || decision.confidence < 0.50) return;
    maybeOfferSave(el);
  }, true);

  document.addEventListener('submit', (e) => {
    const form = e.target;
    if (form.tagName !== 'FORM') return;
    const origin = `web:${location.origin}`;
    if (saveShownForOrigin.has(origin)) return;
    const values = findFormCredentials(form);
    if (!values) return;
    e.preventDefault();
    window.__dudouAutofill.checkAndShowSave(values,
      () => saveShownForOrigin.add(origin),
      () => HTMLFormElement.prototype.submit.call(form)
    );
  }, true);

  scanFormFields();

  const observer = new MutationObserver(() => {
    const now = Date.now();
    if (now - lastScan > 2000) {
      scanFormFields();
    }
  });
  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
})();
