(() => {
  'use strict';

  const config = window.wpagentlyChat;
  if (!config) return;

  const home = new URL(config.homeUrl);
  home.search = '';
  home.hash = '';
  if (!home.pathname.endsWith('/')) home.pathname += '/';

  const log = document.querySelector('#wpagently-chat-log');
  const form = document.querySelector('#wpagently-chat-form');
  const messageInput = document.querySelector('#wpagently-chat-message');
  const userSelect = document.querySelector('#wpagently-chat-user');
  const preview = document.querySelector('#wpagently-preview');
  const previewForm = document.querySelector('#wpagently-preview-form');
  const previewPath = document.querySelector('#wpagently-preview-path');
  const previewError = document.querySelector('#wpagently-preview-error');
  const previewRefresh = document.querySelector('#wpagently-preview-refresh');
  const previewPicker = document.querySelector('#wpagently-preview-picker');
  const previewClear = document.querySelector('#wpagently-preview-clear');
  const previewSelection = document.querySelector('#wpagently-preview-selection');
  const history = [];
  let visualContext = null;
  let pickerActive = false;
  let highlighted = null;
  const pickerDocuments = new WeakSet();

  function normalizeSpace(value, limit) {
    return String(value || '').replace(/\s+/g, ' ').trim().slice(0, limit);
  }

  function safeSelectorToken(value) {
    return /^[A-Za-z_][A-Za-z0-9_-]{0,79}$/.test(value);
  }

  function pathWithinHome(pathname) {
    return home.pathname === '/'
      || pathname === home.pathname.slice(0, -1)
      || pathname.startsWith(home.pathname);
  }

  function pathRelativeToHome(url) {
    if (url.origin !== home.origin || !pathWithinHome(url.pathname)) return '';
    if (home.pathname === '/') return url.pathname;
    if (url.pathname === home.pathname.slice(0, -1)) return '/';
    return url.pathname.slice(home.pathname.length - 1);
  }

  function normalizedPreviewPath(pathname) {
    let decoded = String(pathname);
    for (let depth = 0; depth < 3; depth += 1) {
      if (/%(?:2f|5c)/i.test(decoded)) throw new Error('encoded path separator');
      const next = decodeURIComponent(decoded);
      if (next === decoded) break;
      decoded = next;
    }
    if (/%[0-9a-f]{2}/i.test(decoded)) throw new Error('over-encoded preview path');
    decoded = decoded.replace(/\\/g, '/').replace(/\/{2,}/g, '/');
    if (/[\u0000-\u001F\u007F]/.test(decoded) || /(?:^|\/)\.{1,2}(?:\/|$)/.test(decoded)) {
      throw new Error('invalid preview path');
    }
    return decoded;
  }

  function previewPathIsDenied(pathname) {
    const normalizedPath = normalizedPreviewPath(pathname);
    return config.previewDeniedPaths.some((path) => {
      const base = path.replace(/\/$/, '');
      return normalizedPath === base || normalizedPath.startsWith(`${base}/`);
    });
  }

  function previewTargetForUrl(value) {
    const target = new URL(value, home);
    if (target.origin !== home.origin || !pathWithinHome(target.pathname) || previewPathIsDenied(target.pathname)) {
      throw new Error('invalid preview path');
    }
    return target;
  }

  function previewTargetForPath(value) {
    const requested = String(value || '');
    if (!requested.startsWith('/') || requested.startsWith('//')) throw new Error('invalid preview path');
    return previewTargetForUrl(new URL(requested.slice(1), home));
  }

  function navigatePreview(value) {
    const target = previewTargetForUrl(value);
    target.searchParams.set('wpagently_preview', '1');
    target.searchParams.set('_wpnonce', config.previewNonce);
    previewError.textContent = '';
    clearVisualSelection();
    setPickerActive(false);
    preview.src = target.href;
  }

  function previewDocumentIsAuthorized() {
    try {
      const target = previewTargetForUrl(preview.contentWindow.location.href);
      return target.searchParams.get('wpagently_preview') === '1'
        && target.searchParams.get('_wpnonce') === config.previewNonce;
    } catch {
      return false;
    }
  }

  function selectorFor(element) {
    const parts = [];
    let current = element;
    for (let depth = 0; current && current.nodeType === 1 && depth < 6; depth += 1) {
      const tag = current.tagName.toLowerCase();
      if (current.id && safeSelectorToken(current.id)) {
        parts.unshift(`#${current.id}`);
        break;
      }
      let part = tag;
      const classes = Array.from(current.classList || []).filter(safeSelectorToken).slice(0, 2);
      if (classes.length) {
        part += classes.map((name) => `.${name}`).join('');
      } else if (current.parentElement) {
        const siblings = Array.from(current.parentElement.children).filter((item) => item.tagName === current.tagName);
        if (siblings.length > 1) part += `:nth-of-type(${siblings.indexOf(current) + 1})`;
      }
      parts.unshift(part);
      if (tag === 'body') break;
      current = current.parentElement;
    }
    return parts.join(' > ').slice(0, 500);
  }

  function currentPreviewPath() {
    const url = new URL(preview.contentWindow.location.href);
    return pathRelativeToHome(url).slice(0, 2048);
  }

  function isRenderedTextNode(node, root) {
    let current = node.parentElement;
    const boundary = root.parentElement;
    while (current && current !== boundary) {
      const style = current.ownerDocument.defaultView?.getComputedStyle(current);
      if (
        current.hidden
        || current.getAttribute('aria-hidden') === 'true'
        || style?.display === 'none'
        || style?.visibility === 'hidden'
        || style?.visibility === 'collapse'
        || style?.contentVisibility === 'hidden'
        || style?.opacity === '0'
      ) {
        return false;
      }
      current = current.parentElement;
    }
    return true;
  }

  function elementText(element) {
    const walker = element.ownerDocument.createTreeWalker(element, 4);
    const parts = [];
    let length = 0;
    let visited = 0;
    for (let node = walker.nextNode(); node && visited < 200 && length < 600; node = walker.nextNode()) {
      visited += 1;
      const parentTag = node.parentElement?.tagName?.toLowerCase();
      if (parentTag === 'script' || parentTag === 'style' || parentTag === 'noscript') continue;
      if (!isRenderedTextNode(node, element)) continue;
      const value = normalizeSpace(node.nodeValue, 600 - length);
      if (!value) continue;
      parts.push(value);
      length += value.length + 1;
    }
    return normalizeSpace(parts.join(' '), 500);
  }

  function restoreHighlight() {
    if (!highlighted) return;
    highlighted.element.style.outline = highlighted.outline;
    highlighted.element.style.outlineOffset = highlighted.outlineOffset;
    highlighted = null;
  }

  function clearVisualSelection() {
    restoreHighlight();
    visualContext = null;
    if (previewSelection) {
      previewSelection.hidden = true;
      previewSelection.textContent = '';
    }
    if (previewClear) previewClear.hidden = true;
  }

  function showVisualSelection(element) {
    restoreHighlight();
    const path = currentPreviewPath();
    if (!path) {
      clearVisualSelection();
      setPickerActive(false);
      previewError.textContent = config.i18n.pickerUnavailable;
      return;
    }
    const text = elementText(element);
    const accessibleName = normalizeSpace(
      element.getAttribute('aria-label') || element.getAttribute('alt') || element.getAttribute('title'),
      200,
    );
    visualContext = {
      path,
      selector: selectorFor(element),
      tag: element.tagName.toLowerCase().slice(0, 32),
      text,
      accessible_name: accessibleName,
    };
    highlighted = {
      element,
      outline: element.style.outline,
      outlineOffset: element.style.outlineOffset,
    };
    element.style.outline = '3px solid #8a5615';
    element.style.outlineOffset = '2px';
    if (previewSelection) {
      const detail = [visualContext.selector, text].filter(Boolean).join(' · ');
      previewSelection.textContent = `${config.i18n.selectedElement}\n${detail}`;
      previewSelection.hidden = false;
    }
    if (previewClear) previewClear.hidden = false;
    previewError.textContent = '';
  }

  function setPickerActive(active) {
    pickerActive = Boolean(active);
    if (previewPicker) {
      previewPicker.setAttribute('aria-pressed', pickerActive ? 'true' : 'false');
      previewPicker.textContent = pickerActive ? config.i18n.selecting : config.i18n.selectElement;
    }
  }

  function attachPreviewPicker() {
    try {
      if (!previewDocumentIsAuthorized()) throw new Error('unauthorized preview document');
      const previewDocument = preview.contentDocument;
      if (!previewDocument?.documentElement) throw new Error('unavailable');
      if (pickerDocuments.has(previewDocument)) return;
      previewDocument.addEventListener(
        'click',
        (event) => {
          if (!(event.target instanceof preview.contentWindow.Element)) return;
          if (pickerActive) {
            event.preventDefault();
            event.stopImmediatePropagation();
            showVisualSelection(event.target);
            setPickerActive(false);
            return;
          }
          const anchor = event.target.closest('a[href]');
          if (!anchor) return;
          event.preventDefault();
          event.stopImmediatePropagation();
          try {
            if (anchor.hasAttribute('download')) throw new Error('download not allowed');
            const target = previewTargetForUrl(anchor.href);
            const current = new URL(preview.contentWindow.location.href);
            const sameDocument = target.origin === current.origin
              && target.pathname === current.pathname
              && target.search === current.search;
            if (sameDocument && target.hash) {
              preview.contentWindow.location.hash = target.hash;
              return;
            }
            navigatePreview(target);
          } catch {
            previewError.textContent = config.i18n.badPreviewUrl;
          }
        },
        true,
      );
      pickerDocuments.add(previewDocument);
      previewError.textContent = '';
    } catch {
      setPickerActive(false);
      previewError.textContent = config.i18n.pickerUnavailable;
    }
  }

  function addMessage(content, role = 'assistant') {
    const item = document.createElement('div');
    item.className = `wpagently-message wpagently-message-${role}`;
    item.textContent = content;
    log.append(item);
    log.scrollTop = log.scrollHeight;
    return item;
  }

  async function request(url, body) {
    const response = await fetch(url, {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce },
      body: JSON.stringify(body),
    });
    const data = await response.json().catch(() => ({}));
    if (!response.ok) throw new Error(data.message || config.i18n.failed);
    return data;
  }

  function renderAction(planId, action) {
    const card = document.createElement('div');
    card.className = 'wpagently-action';
    const label = document.createElement('strong');
    label.textContent = action.summary;
    const ability = document.createElement('code');
    ability.textContent = action.ability;
    const details = document.createElement('details');
    const summary = document.createElement('summary');
    summary.textContent = config.i18n.plannedInputs;
    const args = document.createElement('pre');
    args.textContent = JSON.stringify(action.args, null, 2);
    const button = document.createElement('button');
    button.type = 'button';
    button.className = 'button button-primary';
    button.textContent = config.i18n.approve;
    const status = document.createElement('p');
    status.className = 'wpagently-action-status';
    details.append(summary, args);
    card.append(label, ability, details, button, status);
    log.append(card);

    button.addEventListener('click', async () => {
      button.disabled = true;
      status.textContent = config.i18n.approving;
      try {
        const result = await request(config.executeUrl, {
          plan_id: planId,
          action_id: action.id,
        });
        if (result.status !== 'completed') throw new Error(result.result?.message || config.i18n.actionFailed);
        status.textContent = config.i18n.completed;
        card.classList.add('is-completed');
        preview.src = preview.src;
      } catch (error) {
        status.textContent = error.message || config.i18n.actionFailed;
        card.classList.add('has-error');
      }
    });
  }

  form?.addEventListener('submit', async (event) => {
    event.preventDefault();
    const content = messageInput.value.trim();
    if (!content || !userSelect.value) return;
    addMessage(content, 'user');
    messageInput.value = '';
    const pending = addMessage(config.i18n.working, 'status');
    form.querySelector('button[type="submit"]').disabled = true;
    try {
      const plan = await request(config.planUrl, {
        message: content,
        history: history.slice(-10),
        executor_id: Number(userSelect.value),
        visual_context: visualContext,
      });
      pending.remove();
      addMessage(plan.message || config.i18n.failed);
      history.push({ role: 'user', content }, { role: 'assistant', content: plan.message || '' });
      history.splice(0, Math.max(0, history.length - 10));
      for (const action of plan.actions || []) renderAction(plan.plan_id, action);
    } catch (error) {
      pending.textContent = error.message || config.i18n.failed;
      pending.classList.add('has-error');
    } finally {
      form.querySelector('button[type="submit"]').disabled = false;
      messageInput.focus();
    }
  });

  previewForm?.addEventListener('submit', (event) => {
    event.preventDefault();
    try {
      navigatePreview(previewTargetForPath(previewPath.value));
    } catch {
      previewError.textContent = config.i18n.badPreviewUrl;
    }
  });

  previewRefresh?.addEventListener('click', () => {
    clearVisualSelection();
    setPickerActive(false);
    preview.src = preview.src;
  });

  previewPicker?.addEventListener('click', () => {
    setPickerActive(!pickerActive);
    if (pickerActive) attachPreviewPicker();
  });

  previewClear?.addEventListener('click', clearVisualSelection);

  preview?.addEventListener('load', () => {
    clearVisualSelection();
    if (!previewDocumentIsAuthorized()) {
      setPickerActive(false);
      previewError.textContent = config.i18n.badPreviewUrl;
      if (preview.getAttribute('src') !== 'about:blank') preview.src = 'about:blank';
      return;
    }
    attachPreviewPicker();
  });

  attachPreviewPicker();
})();
