import { defineTrait } from '../define.js';
import { prefersReducedMotion } from '../motion.js';

/**
 * `success-checkmark` — SVG path stroke-draw animation. "I confirmed it
 * worked." Pairs with the `validation` trait's success branch and any
 * imperative success milestone (form-saved, payment-confirmed,
 * email-sent).
 *
 * Listens for `validated` with `detail.valid === true`, watches the
 * `data-success-checkmark-trigger` attribute for toggles, and
 * declares a `data-success-checkmark-position` config attribute
 * (`top-right` default, or `center`) to anchor the SVG overlay.
 *
 * On trigger:
 *   1. Stamp an absolutely-positioned SVG checkmark on the host.
 *   2. Animate stroke-dashoffset from full path length → 0 over ~400ms.
 *   3. After ~1.5s total, fade opacity 1 → 0 over 300ms.
 *   4. Remove the SVG, dispatch `success-checkmark-done`.
 *
 * Reduced-motion: render the checkmark instantly (no stroke-draw),
 * still fade out cleanly. The done event fires either way so caller
 * logic gated on it doesn't stall.
 *
 * Composes naturally with `validation`:
 *   <input-ui traits="validation success-checkmark" data-validate="email">
 */

const STROKE_DRAW_MS = 400;
const VISIBLE_HOLD_MS = 1500; // total visible time before fade starts
const FADE_OUT_MS = 300;
const PATH_LENGTH = 36;       // approximate path length for the checkmark below

// Style ID — checkmark keyframes are shared across all instances since
// they don't carry per-host config. Mounted once into <head>, never removed
// (cheap, dedupe by id).
const SHARED_STYLE_ID = 'adia-success-checkmark-keyframes';

function ensureSharedStyles() {
  if (typeof document === 'undefined') return;
  if (document.getElementById(SHARED_STYLE_ID)) return;
  const style = document.createElement('style');
  style.id = SHARED_STYLE_ID;
  style.textContent = `
    @keyframes adia-success-checkmark-draw {
      from { stroke-dashoffset: ${PATH_LENGTH}; }
      to   { stroke-dashoffset: 0; }
    }
    @keyframes adia-success-checkmark-fade {
      from { opacity: 1; }
      to   { opacity: 0; }
    }
  `;
  document.head.appendChild(style);
}

function buildSvg(position, reduced, hostRect) {
  const SVG_NS = 'http://www.w3.org/2000/svg';
  const svg = document.createElementNS(SVG_NS, 'svg');
  svg.setAttribute('viewBox', '0 0 24 24');
  svg.setAttribute('width', '24');
  svg.setAttribute('height', '24');
  svg.setAttribute('aria-hidden', 'true');

  // Render in viewport coordinates against the body so `overflow: hidden`
  // ancestors (cards, buttons, drawers) can't clip the checkmark. The
  // anchor is the host's getBoundingClientRect() at draw time — for the
  // ~1.8s animation window the host is unlikely to move enough to matter.
  let top, left;
  if (position === 'center') {
    top  = hostRect.top + hostRect.height / 2 - 12;
    left = hostRect.left + hostRect.width / 2 - 12;
  } else {
    // top-right (default) — protrude beyond the corner by ~8px so the
    // checkmark reads as an applied stamp, not a content element.
    top  = hostRect.top - 8;
    left = hostRect.right - 16;
  }

  svg.style.cssText = `
    position: fixed;
    top: ${top}px;
    left: ${left}px;
    pointer-events: none;
    color: var(--a-success, #22c55e);
    z-index: 99999;
  `;

  // Optional disc background — gives the checkmark a visible bedding
  // when stamped on photographic or low-contrast hosts.
  const circle = document.createElementNS(SVG_NS, 'circle');
  circle.setAttribute('cx', '12');
  circle.setAttribute('cy', '12');
  circle.setAttribute('r', '11');
  circle.setAttribute('fill', 'currentColor');
  svg.appendChild(circle);

  const path = document.createElementNS(SVG_NS, 'path');
  // ~36 unit path: M6 12 L10 16 L18 8.
  path.setAttribute('d', 'M6 12 L10 16 L18 8');
  path.setAttribute('fill', 'none');
  path.setAttribute('stroke', 'var(--a-success-fg, white)');
  path.setAttribute('stroke-width', '2.5');
  path.setAttribute('stroke-linecap', 'round');
  path.setAttribute('stroke-linejoin', 'round');
  path.setAttribute('stroke-dasharray', String(PATH_LENGTH));

  if (reduced) {
    // Render the checkmark instantly — no stroke-draw — but still let
    // the fade-out keyframe run so the surface tears down gracefully.
    path.setAttribute('stroke-dashoffset', '0');
  } else {
    path.setAttribute('stroke-dashoffset', String(PATH_LENGTH));
    path.style.animation = `adia-success-checkmark-draw ${STROKE_DRAW_MS}ms ease-out forwards`;
  }

  svg.appendChild(path);
  return svg;
}

export const successCheckmark = defineTrait({
  name: 'success-checkmark',
  category: 'animation-feedback',
  description: 'Stroke-draw checkmark on validation success',
  attributes: ['data-success-checkmark-active'],
  events: ['success-checkmark-done'],
  config: ['data-success-checkmark-position', 'data-success-checkmark-trigger'],
  setup({ host }) {
    const activeTimers = new Set();
    const liveSvgs = new Set();
    let lastValid = host.hasAttribute('data-validation-valid');

    function readPosition() {
      const v = host.getAttribute('data-success-checkmark-position');
      return v === 'center' ? 'center' : 'top-right';
    }

    function fireDone() {
      // Only clear active when no in-flight checkmark remains.
      if (liveSvgs.size === 0) host.removeAttribute('data-success-checkmark-active');
      host.dispatchEvent(new CustomEvent('success-checkmark-done', { bubbles: true }));
    }

    function teardownSvg(svg) {
      liveSvgs.delete(svg);
      svg.remove();
      fireDone();
    }

    function draw() {
      ensureSharedStyles();

      const reduced = prefersReducedMotion();
      const position = readPosition();

      // Anchor against host's viewport rect at trigger time. Detached
      // or happy-dom hosts return zero rects, which renders the SVG at
      // the viewport origin — fine for tests, fine in real browsers
      // because real hosts have real rects.
      const hostRect = host.getBoundingClientRect?.() ||
        { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };

      const svg = buildSvg(position, reduced, hostRect);

      // Append at body level so overflow:hidden ancestors can't clip.
      document.body.appendChild(svg);
      liveSvgs.add(svg);
      host.setAttribute('data-success-checkmark-active', '');

      // After the visible-hold window, kick off the fade-out. Use a
      // single timer rather than chaining animationend so detached or
      // early-removed SVGs don't strand listeners.
      const fadeStart = setTimeout(() => {
        activeTimers.delete(fadeStart);
        if (!liveSvgs.has(svg)) return;
        svg.style.animation = `adia-success-checkmark-fade ${FADE_OUT_MS}ms ease-out forwards`;

        const fadeEnd = setTimeout(() => {
          activeTimers.delete(fadeEnd);
          if (liveSvgs.has(svg)) teardownSvg(svg);
        }, FADE_OUT_MS);
        activeTimers.add(fadeEnd);
      }, VISIBLE_HOLD_MS);
      activeTimers.add(fadeStart);
    }

    function onValidated(e) {
      if (e?.detail && e.detail.valid === true) draw();
    }

    // Watch data-validation-valid (false → true) + the manual trigger.
    const observer = new MutationObserver((muts) => {
      for (const m of muts) {
        if (m.attributeName === 'data-validation-valid') {
          const nowValid = host.hasAttribute('data-validation-valid');
          if (nowValid && !lastValid) draw();
          lastValid = nowValid;
        } else if (m.attributeName === 'data-success-checkmark-trigger') {
          if (host.hasAttribute('data-success-checkmark-trigger')) draw();
        }
      }
    });
    observer.observe(host, {
      attributes: true,
      attributeFilter: ['data-validation-valid', 'data-success-checkmark-trigger'],
    });

    host.addEventListener('validated', onValidated);

    return () => {
      observer.disconnect();
      host.removeEventListener('validated', onValidated);
      for (const t of activeTimers) clearTimeout(t);
      activeTimers.clear();
      // SVGs live at body level (not host), so removing them is the
      // only cleanup needed — no host.style.position to restore.
      for (const svg of liveSvgs) svg.remove();
      liveSvgs.clear();
      host.removeAttribute('data-success-checkmark-active');
    };
  },
});
