/**
 * Floating KeyPilot onboarding walkthrough panel (content-script friendly).
 * Shell + checklist DOM live in onboarding-shared.js (shared with early-inject).
 */
import { CSS_CLASSES, Z_INDEX } from '../config/constants.js';
import { applyPopupThemeVars } from './popup-theme-vars.js';
import {
  ONBOARDING_DEFAULT_TITLE,
  ONBOARDING_PANEL_CLASS,
  ONBOARDING_PANEL_Z_FALLBACK,
  createOnboardingShell,
  ensureOnboardingOverlay,
  queryOnboardingShellRefs,
  renderOnboardingSlideSurface,
  setOnboardingOverlayOpen,
  setOnboardingPanelVisible,
  updateOnboardingChrome
} from './onboarding-shared.js';

function stripListeners(btn) {
  try {
    if (!btn || !btn.parentNode) return btn;
    const clone = btn.cloneNode(true);
    btn.parentNode.replaceChild(clone, btn);
    return clone;
  } catch {
    return btn;
  }
}

export class OnboardingPanel {
  /**
   * @param {Object} params
   * @param {() => void} params.onRequestClose
   * @param {() => void} [params.onRequestPrev]
   * @param {() => void} [params.onRequestNext]
   * @param {() => void} [params.onRequestReset]
   * @param {(taskId: string) => void} [params.onRequestUncheckTask]
   */
  constructor({ onRequestClose, onRequestPrev, onRequestNext, onRequestReset, onRequestUncheckTask } = {}) {
    this.root = null;
    this.body = null;
    this.slideSurface = null;
    this.titleEl = null;
    this.stepEl = null;
    this.closeBtn = null;
    this.prevBtn = null;
    this.nextBtn = null;
    this.resetBtn = null;
    this._overlayEl = null;
    this._overlayTitleEl = null;
    this._overlayMsgEl = null;
    this._overlayPrimaryBtn = null;
    this._overlaySecondaryBtn = null;
    this._overlayOnPrimary = null;
    this._overlayOnSecondary = null;
    this._lastRenderedSlideId = null;
    this._lastRenderedSlideIndex = null;
    this._onRequestClose = typeof onRequestClose === 'function' ? onRequestClose : null;
    this._onRequestPrev = typeof onRequestPrev === 'function' ? onRequestPrev : null;
    this._onRequestNext = typeof onRequestNext === 'function' ? onRequestNext : null;
    this._onRequestReset = typeof onRequestReset === 'function' ? onRequestReset : null;
    this._onRequestUncheckTask = typeof onRequestUncheckTask === 'function' ? onRequestUncheckTask : null;
    this._onCloseClick = this._onCloseClick.bind(this);
    this._onPrevClick = this._onPrevClick.bind(this);
    this._onNextClick = this._onNextClick.bind(this);
    this._onResetClick = this._onResetClick.bind(this);
    this._onOverlayPrimary = this._onOverlayPrimary.bind(this);
    this._onOverlaySecondary = this._onOverlaySecondary.bind(this);
    this._onTaskRowClick = this._onTaskRowClick.bind(this);

    // Tip bubble under the control-strip On/Off segment (shown while KP is off
    // after the walkthrough "turn off" step).
    this._reEnableTipEl = null;
    this._reEnableTipArrow = null;
    this._reEnableTipMsg = null;
    this._onReEnableTipOutside = this._onReEnableTipOutside.bind(this);
    this._onReEnableTipResize = this._onReEnableTipResize.bind(this);
    this._reEnableTipAnchor = null;
  }

  isVisible() {
    return !!(this.root && this.root.isConnected && this.root.hidden === false);
  }

  show() {
    if (window !== window.top) return;
    this._ensure();
    setOnboardingPanelVisible(this.root, true);
  }

  hide() {
    setOnboardingPanelVisible(this.root, false);
  }

  /**
   * Small speech-bubble under the control strip status control.
   * Click outside dismisses it (interrupted); status click is ignored as outside.
   *
   * @param {Object} [opts]
   * @param {Element|null} [opts.anchorEl]
   * @param {string} [opts.message]
   */
  showReEnableTip({
    anchorEl = null,
    message = 'Click the control strip again to turn KeyPilot back on.'
  } = {}) {
    if (window !== window.top) return;
    this.hideReEnableTip();

    const anchor =
      anchorEl ||
      document.querySelector('.kp-control-strip [data-kp-control-strip-status="true"]') ||
      document.querySelector('.kp-control-strip');
    if (!anchor || !anchor.isConnected) return;

    const tip = document.createElement('div');
    tip.setAttribute('data-kp-onboarding-reenable-tip', 'true');
    tip.setAttribute('role', 'status');
    Object.assign(tip.style, {
      position: 'fixed',
      zIndex: String((Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK) + 2),
      maxWidth: '260px',
      padding: '10px 12px',
      borderRadius: '12px',
      border: '1px solid rgba(255,255,255,0.16)',
      background: 'rgba(18, 18, 18, 0.96)',
      color: 'rgba(255,255,255,0.95)',
      boxShadow: '0 10px 28px rgba(0,0,0,0.45)',
      fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif',
      fontSize: '13px',
      fontWeight: '600',
      lineHeight: '1.35',
      pointerEvents: 'auto'
    });

    const arrow = document.createElement('div');
    arrow.setAttribute('data-kp-onboarding-reenable-tip-arrow', 'true');
    Object.assign(arrow.style, {
      position: 'absolute',
      width: '0',
      height: '0',
      borderLeft: '8px solid transparent',
      borderRight: '8px solid transparent',
      borderBottom: '8px solid rgba(18, 18, 18, 0.96)',
      top: '-8px',
      left: '20px',
      filter: 'drop-shadow(0 -1px 0 rgba(255,255,255,0.16))'
    });

    const msg = document.createElement('div');
    msg.textContent = String(message || '');
    tip.appendChild(arrow);
    tip.appendChild(msg);

    try { applyPopupThemeVars(tip); } catch { /* ignore */ }

    (document.body || document.documentElement).appendChild(tip);
    this._reEnableTipEl = tip;
    this._reEnableTipArrow = arrow;
    this._reEnableTipMsg = msg;
    this._reEnableTipAnchor = anchor;

    this._positionReEnableTip();

    // Outside click: capture phase so we see it before other handlers; skip tip + status btn.
    try {
      document.addEventListener('pointerdown', this._onReEnableTipOutside, true);
      window.addEventListener('resize', this._onReEnableTipResize, true);
      window.addEventListener('scroll', this._onReEnableTipResize, true);
    } catch { /* ignore */ }
  }

  hideReEnableTip() {
    try {
      document.removeEventListener('pointerdown', this._onReEnableTipOutside, true);
      window.removeEventListener('resize', this._onReEnableTipResize, true);
      window.removeEventListener('scroll', this._onReEnableTipResize, true);
    } catch { /* ignore */ }
    try {
      if (this._reEnableTipEl && this._reEnableTipEl.parentNode) {
        this._reEnableTipEl.parentNode.removeChild(this._reEnableTipEl);
      }
    } catch { /* ignore */ }
    this._reEnableTipEl = null;
    this._reEnableTipArrow = null;
    this._reEnableTipMsg = null;
    this._reEnableTipAnchor = null;
  }

  isReEnableTipVisible() {
    return !!(this._reEnableTipEl && this._reEnableTipEl.isConnected);
  }

  _positionReEnableTip() {
    const tip = this._reEnableTipEl;
    const anchor = this._reEnableTipAnchor;
    if (!tip || !anchor || !anchor.isConnected) return;
    try {
      const rect = anchor.getBoundingClientRect();
      const tipRect = tip.getBoundingClientRect();
      const gap = 10;
      let left = Math.round(rect.left + rect.width / 2 - Math.min(tipRect.width, 260) / 2);
      left = Math.max(8, Math.min(left, window.innerWidth - tipRect.width - 8));
      let top = Math.round(rect.bottom + gap);
      // Prefer below the strip; if off-screen, flip above.
      if (top + tipRect.height > window.innerHeight - 8) {
        top = Math.max(8, Math.round(rect.top - tipRect.height - gap));
        if (this._reEnableTipArrow) {
          Object.assign(this._reEnableTipArrow.style, {
            top: 'auto',
            bottom: '-8px',
            borderBottom: 'none',
            borderTop: '8px solid rgba(18, 18, 18, 0.96)'
          });
        }
      } else if (this._reEnableTipArrow) {
        Object.assign(this._reEnableTipArrow.style, {
          top: '-8px',
          bottom: 'auto',
          borderTop: 'none',
          borderBottom: '8px solid rgba(18, 18, 18, 0.96)'
        });
      }
      tip.style.left = `${left}px`;
      tip.style.top = `${top}px`;

      // Point the arrow toward the anchor center.
      if (this._reEnableTipArrow) {
        const arrowLeft = Math.round(rect.left + rect.width / 2 - left - 8);
        this._reEnableTipArrow.style.left = `${Math.max(12, Math.min(arrowLeft, tipRect.width - 24))}px`;
      }
    } catch { /* ignore */ }
  }

  _onReEnableTipOutside(e) {
    try {
      const t = e?.target;
      if (!t || !this._reEnableTipEl) return;
      if (this._reEnableTipEl.contains(t)) return;
      // Clicks on the status control are intentional (toggle back on) — keep tip until ON.
      if (typeof t.closest === 'function') {
        if (t.closest('[data-kp-control-strip-status="true"]')) return;
        if (t.closest('[data-kp-onboarding-reenable-tip="true"]')) return;
      }
      this.hideReEnableTip();
    } catch { /* ignore */ }
  }

  _onReEnableTipResize() {
    this._positionReEnableTip();
  }

  /**
   * @param {Object} params
   * @param {string} params.title
   * @param {string} [params.bodyText]
   * @param {string} params.slideId
   * @param {number} params.slideIndex
   * @param {number} params.slideCount
   * @param {Array} params.tasks
   * @param {Set<string>} params.completedTaskIds
   * @param {{type:'slide', dir:1|-1}|null} [params.transition]
   * @param {boolean} [params.forceRebuild]
   * @returns {Promise<void>}
   */
  async render({
    title,
    bodyText = '',
    slideId,
    slideIndex,
    slideCount,
    tasks,
    completedTaskIds,
    lastCompletedTaskId = null,
    showTip = true,
    transition = null,
    forceRebuild = false
  }) {
    if (!this.root || this.root.hidden) return;

    try {
      const targetSurface = this.slideSurface || this.body;
      const refs = {
        root: this.root,
        titleEl: this.titleEl,
        stepEl: this.stepEl,
        prevBtn: this.prevBtn,
        nextBtn: this.nextBtn
      };

      const updateDom = () => {
        updateOnboardingChrome(refs, {
          title: title || ONBOARDING_DEFAULT_TITLE,
          slideId,
          slideIndex,
          slideCount
        });
        renderOnboardingSlideSurface(targetSurface, {
          tasks,
          completedTaskIds,
          lastCompletedTaskId,
          bodyText,
          forceRebuild,
          showTip: showTip !== false,
          onTaskRowClick: this._onTaskRowClick
        });
      };

      const doSlide = !!(transition && transition.type === 'slide');
      const dir = doSlide && transition?.dir === -1 ? -1 : 1;

      if (!doSlide) {
        updateDom();
        this._lastRenderedSlideId = String(slideId || '');
        this._lastRenderedSlideIndex = Number(slideIndex) || 0;
        return;
      }

      // Prefer View Transitions API.
      try {
        if (document && typeof document.startViewTransition === 'function' && targetSurface) {
          try { this.root?.style?.setProperty?.('--kp-onboarding-slide-dir', String(dir)); } catch { /* ignore */ }
          try { targetSurface.style.viewTransitionName = 'kp-onboarding-slide-surface'; } catch { /* ignore */ }

          const vt = document.startViewTransition(() => {
            updateDom();
          });
          await Promise.resolve(vt?.finished).catch(() => {});

          try { targetSurface.style.viewTransitionName = ''; } catch { /* ignore */ }
          this._lastRenderedSlideId = String(slideId || '');
          this._lastRenderedSlideIndex = Number(slideIndex) || 0;
          return;
        }
      } catch {
        // fall back
      }

      try {
        targetSurface.style.transform = `translateX(${dir > 0 ? '100%' : '-100%'})`;
        targetSurface.style.opacity = '0.7';
      } catch { /* ignore */ }

      updateDom();

      try {
        const anim = targetSurface.animate([
          { transform: `translateX(${dir > 0 ? '100%' : '-100%'})`, opacity: 0.7 },
          { transform: 'translateX(0%)', opacity: 1 }
        ], { duration: 220, easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)', fill: 'both' });
        await Promise.resolve(anim?.finished).catch(() => {});
      } catch { /* ignore */ }

      try {
        targetSurface.style.transform = '';
        targetSurface.style.opacity = '';
      } catch { /* ignore */ }

      this._lastRenderedSlideId = String(slideId || '');
      this._lastRenderedSlideIndex = Number(slideIndex) || 0;
    } catch (e) {
      try {
        const surface = this.slideSurface || this.body;
        while (surface && surface.firstChild) surface.removeChild(surface.firstChild);
        const msg = document.createElement('div');
        msg.textContent = 'Unable to render onboarding panel on this page.';
        surface?.appendChild(msg);
      } catch {
        // ignore
      }
      console.warn('[KeyPilot] Failed to render onboarding panel:', e);
    }
  }

  showOverlay({ title, message, primaryText = 'OK', secondaryText = '', onPrimary = null, onSecondary = null } = {}) {
    this._ensure();
    if (!this.root || !this._overlayEl) return;

    this._overlayOnPrimary = typeof onPrimary === 'function' ? onPrimary : null;
    this._overlayOnSecondary = typeof onSecondary === 'function' ? onSecondary : null;

    try { this._overlayTitleEl.textContent = String(title || 'Nice!'); } catch { /* ignore */ }
    try { this._overlayMsgEl.textContent = String(message || ''); } catch { /* ignore */ }
    try {
      this._overlayPrimaryBtn.textContent = String(primaryText || 'OK');
      this._overlayPrimaryBtn.hidden = false;
    } catch { /* ignore */ }
    try {
      const showSecondary = !!(secondaryText && String(secondaryText).trim());
      this._overlaySecondaryBtn.textContent = String(secondaryText || '');
      this._overlaySecondaryBtn.hidden = !showSecondary;
    } catch { /* ignore */ }

    setOnboardingOverlayOpen(this._overlayEl, true, this.root);
  }

  hideOverlay() {
    if (!this._overlayEl) return;
    setOnboardingOverlayOpen(this._overlayEl, false, this.root);
    this._overlayOnPrimary = null;
    this._overlayOnSecondary = null;
  }

  _ensure() {
    if (this.root && this.root.isConnected) return;

    // Adopt early-inject shell when present (avoids flicker).
    try {
      const existing = document.querySelector(`.${ONBOARDING_PANEL_CLASS}[data-kp-early-onboarding="true"]`);
      if (existing && existing.isConnected) {
        const refs = queryOnboardingShellRefs(existing);
        if (refs) {
          try {
            existing.style.zIndex = String(Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK);
          } catch { /* ignore */ }
          applyPopupThemeVars(existing);

          let { closeBtn, prevBtn, nextBtn, resetBtn } = refs;
          closeBtn = stripListeners(closeBtn);
          prevBtn = stripListeners(prevBtn);
          nextBtn = stripListeners(nextBtn);
          resetBtn = stripListeners(resetBtn);

          if (closeBtn) closeBtn.addEventListener('click', this._onCloseClick);
          if (prevBtn) prevBtn.addEventListener('click', this._onPrevClick);
          if (nextBtn) nextBtn.addEventListener('click', this._onNextClick);
          if (resetBtn) resetBtn.addEventListener('click', this._onResetClick);

          this._bindShell({
            root: existing,
            body: refs.body,
            slideSurface: refs.slideSurface,
            titleEl: refs.titleEl,
            stepEl: refs.stepEl,
            closeBtn,
            prevBtn,
            nextBtn,
            resetBtn
          });
          this._ensureOverlay(existing);
          return;
        }
      }
    } catch { /* ignore */ }

    const shell = createOnboardingShell(document, {
      zIndex: Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK,
      early: false,
      initiallyHidden: true,
      includeViewTransitions: true,
      applyTheme: applyPopupThemeVars,
      navDisabled: false
    });

    shell.prevBtn.addEventListener('click', this._onPrevClick);
    shell.nextBtn.addEventListener('click', this._onNextClick);
    shell.resetBtn.addEventListener('click', this._onResetClick);
    shell.closeBtn.addEventListener('click', this._onCloseClick);

    (document.body || document.documentElement).appendChild(shell.root);
    this._bindShell(shell);
    this._ensureOverlay(shell.root);
  }

  _bindShell(shell) {
    this.root = shell.root;
    this.body = shell.body;
    this.slideSurface = shell.slideSurface;
    this.titleEl = shell.titleEl || null;
    this.stepEl = shell.stepEl || null;
    this.closeBtn = shell.closeBtn || null;
    this.prevBtn = shell.prevBtn || null;
    this.nextBtn = shell.nextBtn || null;
    this.resetBtn = shell.resetBtn || null;
  }

  _ensureOverlay(rootEl) {
    try {
      if (this._overlayEl && this._overlayEl.isConnected) return;

      const bodyHost =
        rootEl?.querySelector?.('[data-kp-onboarding-body="true"]') ||
        rootEl?.querySelector?.(':scope > div[data-kp-onboarding-body]') ||
        null;
      const host = bodyHost || rootEl;
      const overlayRefs = ensureOnboardingOverlay(host);
      if (!overlayRefs) return;

      this._overlayEl = overlayRefs.overlayEl;
      this._overlayTitleEl = overlayRefs.titleEl;
      this._overlayMsgEl = overlayRefs.msgEl;
      this._overlayPrimaryBtn = overlayRefs.primaryBtn;
      this._overlaySecondaryBtn = overlayRefs.secondaryBtn;

      try { this._overlayPrimaryBtn?.removeEventListener?.('click', this._onOverlayPrimary); } catch { /* ignore */ }
      try { this._overlaySecondaryBtn?.removeEventListener?.('click', this._onOverlaySecondary); } catch { /* ignore */ }
      try { this._overlayPrimaryBtn?.addEventListener?.('click', this._onOverlayPrimary); } catch { /* ignore */ }
      try { this._overlaySecondaryBtn?.addEventListener?.('click', this._onOverlaySecondary); } catch { /* ignore */ }

      // Normalize visibility if overlay already existed (hostile pages may override [hidden]).
      try {
        const isHidden = this._overlayEl.hidden === true || this._overlayEl.hasAttribute('hidden');
        setOnboardingOverlayOpen(this._overlayEl, !isHidden, this.root);
      } catch { /* ignore */ }
    } catch {
      // ignore
    }
  }

  _onOverlayPrimary(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    const cb = this._overlayOnPrimary;
    this.hideOverlay();
    try { cb?.(); } catch { /* ignore */ }
  }

  _onOverlaySecondary(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    const cb = this._overlayOnSecondary;
    this.hideOverlay();
    try { cb?.(); } catch { /* ignore */ }
  }

  _onCloseClick(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch {
      // ignore
    }
    try {
      if (this._onRequestClose) this._onRequestClose();
    } catch (err) {
      console.warn('[KeyPilot Onboarding] Error in onRequestClose:', err);
    }
  }

  _onPrevClick(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    try {
      if (this._onRequestPrev) this._onRequestPrev();
    } catch { /* ignore */ }
  }

  _onNextClick(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    try {
      if (this._onRequestNext) this._onRequestNext();
    } catch { /* ignore */ }
  }

  _onResetClick(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    try {
      const result = this._onRequestReset ? this._onRequestReset() : null;
      if (result && typeof result.then === 'function') {
        result.catch((err) => {
          console.warn('[KeyPilot Onboarding] reset failed:', err);
        });
      }
    } catch (err) {
      console.warn('[KeyPilot Onboarding] reset handler error:', err);
    }
  }

  _onTaskRowClick(e) {
    try {
      e.preventDefault();
      e.stopPropagation();
    } catch { /* ignore */ }
    try {
      const row = e?.currentTarget || e?.target?.closest?.('[data-kp-onboarding-task-id]');
      if (!row) return;
      if (row.getAttribute('data-kp-onboarding-uncheckable') !== 'true') return;
      const taskId = row.getAttribute('data-kp-onboarding-task-id');
      if (!taskId || !this._onRequestUncheckTask) return;
      this._onRequestUncheckTask(String(taskId));
    } catch { /* ignore */ }
  }

  /**
   * Play the F-key click border effect around the walkthrough panel (marquee/flash/etc).
   * Uses the same CSS classes as OverlayManager so it matches user settings visuals.
   * @param {'marquee'|'flash'|'dash'|'scale'} [effect]
   */
  playBorderEffect(effect = 'marquee') {
    try {
      this._ensure();
      const host = this.root;
      if (!host || !host.isConnected) return;
      const rect = host.getBoundingClientRect();
      if (!rect || rect.width < 2 || rect.height < 2) return;

      const map = {
        marquee: { className: CSS_CLASSES.FOCUS_MARQUEE || 'kpv2-focus-marquee', ms: 1200 },
        flash: { className: CSS_CLASSES.FOCUS_FLASH || 'kpv2-focus-flash', ms: 500 },
        scale: { className: CSS_CLASSES.FOCUS_PULSE || 'kpv2-focus-pulse', ms: 800 },
        dash: { className: CSS_CLASSES.FOCUS_DASH || 'kpv2-focus-dash', ms: 1100 }
      };
      const kind = map[String(effect || 'marquee')] || map.marquee;

      const pulse = document.createElement('div');
      pulse.className = kind.className;
      pulse.setAttribute('aria-hidden', 'true');
      pulse.setAttribute('data-kp-onboarding-border-effect', String(effect || 'marquee'));
      Object.assign(pulse.style, {
        left: `${rect.left}px`,
        top: `${rect.top}px`,
        width: `${rect.width}px`,
        height: `${rect.height}px`,
        borderRadius: '14px',
        zIndex: String((Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK) + 5),
        pointerEvents: 'none'
      });
      (document.body || document.documentElement).appendChild(pulse);
      window.setTimeout(() => {
        try {
          if (pulse.parentNode) pulse.parentNode.removeChild(pulse);
        } catch { /* ignore */ }
      }, kind.ms + 80);
    } catch {
      // ignore
    }
  }
}
