'use client';

/**
 * Reusable, admin-driven CAPTCHA widget.
 *
 * Fetches the active CAPTCHA config from /api/captcha/config and renders
 * whatever the merchant enabled in Shopware (Settings → CAPTCHA): Honeypot,
 * Basic image captcha, Google reCAPTCHA v2 (checkbox/invisible) and/or v3.
 * Several can be active at once — all are rendered and all are collected.
 *
 * Usage:
 *   const captchaRef = useRef<CaptchaHandle>(null);
 *   <Captcha ref={captchaRef} action="contact" />
 *   const payload = await captchaRef.current?.getPayload();
 *   // include payload in the request body (contact form) OR
 *   // await runCaptchaGate(payload) before a direct Store API call.
 */

import {
  forwardRef,
  useCallback,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';
import type { CaptchaPayload, PublicCaptchaConfig } from '@/lib/captcha-types';

export interface CaptchaHandle {
  /** Collect the current CAPTCHA values (async: reCAPTCHA tokens are minted here). */
  getPayload: () => Promise<CaptchaPayload>;
  /** Reset the widgets after a failed/successful submit. */
  reset: () => void;
  /** True once the config has loaded. */
  isReady: () => boolean;
}

interface Props {
  /** reCAPTCHA v3 action name (analytics in the Google console). */
  action?: string;
}

declare global {
  interface Window {
    grecaptcha?: any;
  }
}

// Load Google's reCAPTCHA api.js once per page.
let grecaptchaScriptPromise: Promise<void> | null = null;
function loadRecaptchaScript(renderParam: string): Promise<void> {
  if (typeof window === 'undefined') return Promise.resolve();
  if (window.grecaptcha?.render) return Promise.resolve();
  if (grecaptchaScriptPromise) return grecaptchaScriptPromise;
  grecaptchaScriptPromise = new Promise<void>((resolve, reject) => {
    const script = document.createElement('script');
    script.src = `https://www.google.com/recaptcha/api.js?render=${renderParam}`;
    script.async = true;
    script.defer = true;
    script.onload = () => resolve();
    script.onerror = () => reject(new Error('Failed to load reCAPTCHA'));
    document.head.appendChild(script);
  });
  return grecaptchaScriptPromise;
}

const Captcha = forwardRef<CaptchaHandle, Props>(function Captcha({ action = 'submit' }, ref) {
  const [config, setConfig] = useState<PublicCaptchaConfig | null>(null);

  // Basic (image) captcha
  const [basicImage, setBasicImage] = useState<string | null>(null);
  const [basicToken, setBasicToken] = useState('');
  const [basicAnswer, setBasicAnswer] = useState('');

  // Honeypot
  const [honeypot, setHoneypot] = useState('');

  // reCAPTCHA v2 widget
  const v2ContainerRef = useRef<HTMLDivElement | null>(null);
  const v2WidgetId = useRef<number | null>(null);

  const loadBasic = useCallback(async () => {
    try {
      const res = await fetch('/api/captcha');
      const data = await res.json();
      setBasicImage(data.imageBase64);
      setBasicToken(data.token);
      setBasicAnswer('');
    } catch {
      // non-fatal
    }
  }, []);

  // 1. Fetch the active CAPTCHA config.
  useEffect(() => {
    let cancelled = false;
    fetch('/api/captcha/config', { cache: 'no-store' })
      .then((r) => r.json())
      .then((cfg: PublicCaptchaConfig) => {
        if (!cancelled) setConfig(cfg);
      })
      .catch(() => {
        if (!cancelled) {
          setConfig({
            honeypot: false,
            basicCaptcha: false,
            googleReCaptchaV2: null,
            googleReCaptchaV3: null,
          });
        }
      });
    return () => {
      cancelled = true;
    };
  }, []);

  // 2. Initialise the active widgets once config arrives.
  useEffect(() => {
    if (!config) return;
    if (config.basicCaptcha) loadBasic();

    if (config.googleReCaptchaV2 || config.googleReCaptchaV3) {
      // v3 needs ?render=<siteKey>; v2 uses ?render=explicit.
      const renderParam = config.googleReCaptchaV3?.siteKey || 'explicit';
      loadRecaptchaScript(renderParam)
        .then(() => {
          if (
            config.googleReCaptchaV2 &&
            v2ContainerRef.current &&
            v2WidgetId.current === null &&
            window.grecaptcha?.render
          ) {
            window.grecaptcha.ready(() => {
              try {
                v2WidgetId.current = window.grecaptcha.render(v2ContainerRef.current, {
                  sitekey: config.googleReCaptchaV2!.siteKey,
                  size: config.googleReCaptchaV2!.invisible ? 'invisible' : 'normal',
                });
              } catch {
                // widget may already be rendered
              }
            });
          }
        })
        .catch(() => {});
    }
  }, [config, loadBasic]);

  useImperativeHandle(
    ref,
    () => ({
      isReady: () => config !== null,
      reset: () => {
        if (config?.basicCaptcha) loadBasic();
        if (
          config?.googleReCaptchaV2 &&
          v2WidgetId.current !== null &&
          window.grecaptcha?.reset
        ) {
          try {
            window.grecaptcha.reset(v2WidgetId.current);
          } catch {
            // ignore
          }
        }
        setHoneypot('');
      },
      getPayload: async () => {
        const payload: CaptchaPayload = {};
        if (!config) return payload;

        if (config.honeypot) payload.honeypot = honeypot;

        if (config.basicCaptcha) {
          payload.basicCaptchaAnswer = basicAnswer;
          payload.basicCaptchaToken = basicToken;
        }

        if (config.googleReCaptchaV2 && v2WidgetId.current !== null && window.grecaptcha) {
          let tok = '';
          try {
            if (config.googleReCaptchaV2.invisible) {
              tok = await window.grecaptcha.execute(v2WidgetId.current);
            } else {
              tok = window.grecaptcha.getResponse(v2WidgetId.current) || '';
            }
          } catch {
            tok = '';
          }
          payload.recaptchaV2Token = tok;
        }

        if (config.googleReCaptchaV3 && window.grecaptcha) {
          const siteKey = config.googleReCaptchaV3.siteKey;
          payload.recaptchaV3Token = await new Promise<string>((resolve) => {
            try {
              window.grecaptcha.ready(() => {
                window.grecaptcha
                  .execute(siteKey, { action })
                  .then((t: string) => resolve(t))
                  .catch(() => resolve(''));
              });
            } catch {
              resolve('');
            }
          });
        }

        return payload;
      },
    }),
    [config, honeypot, basicAnswer, basicToken, loadBasic, action]
  );

  if (!config) return null;

  const nothingToShow =
    !config.honeypot &&
    !config.basicCaptcha &&
    !config.googleReCaptchaV2 &&
    !config.googleReCaptchaV3;
  if (nothingToShow) return null;

  return (
    <div className="space-y-3">
      {/* Honeypot — visually hidden trap field; real users never fill it. */}
      {config.honeypot && (
        <div
          aria-hidden="true"
          style={{
            position: 'absolute',
            left: '-9999px',
            top: '-9999px',
            height: 0,
            width: 0,
            overflow: 'hidden',
          }}
        >
          <label>
            Leave this field empty
            <input
              type="text"
              name="url_confirm"
              tabIndex={-1}
              autoComplete="off"
              value={honeypot}
              onChange={(e) => setHoneypot(e.target.value)}
            />
          </label>
        </div>
      )}

      {/* Basic (image) captcha */}
      {config.basicCaptcha && basicImage && (
        <div>
          <label className="block text-sm font-medium text-surface-700 mb-2">
            To continue, enter the result shown <span className="text-red-500">*</span>
          </label>
          <div className="flex items-center gap-3 mb-2">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={basicImage}
              alt="Captcha challenge"
              className="h-12 rounded border border-surface-200 select-none"
              draggable={false}
            />
            <button
              type="button"
              onClick={loadBasic}
              title="Refresh captcha"
              className="p-2 text-surface-400 hover:text-surface-700 transition-colors rounded-lg hover:bg-surface-100"
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                className="w-5 h-5"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
                />
              </svg>
            </button>
          </div>
          <input
            type="text"
            inputMode="numeric"
            value={basicAnswer}
            onChange={(e) => setBasicAnswer(e.target.value)}
            autoComplete="off"
            placeholder="Your answer"
            className="w-full sm:w-48 px-3 py-2 border border-surface-200 rounded-lg text-sm focus:outline-none focus:border-brand-400"
          />
        </div>
      )}

      {/* Google reCAPTCHA v2 — container the widget renders into (hidden when invisible). */}
      {config.googleReCaptchaV2 && (
        <div ref={v2ContainerRef} className={config.googleReCaptchaV2.invisible ? 'hidden' : ''} />
      )}

      {/* Google reCAPTCHA v3 — no UI; just the required attribution notice. */}
      {config.googleReCaptchaV3 && (
        <p className="text-xs text-surface-500">
          This site is protected by reCAPTCHA and the Google{' '}
          <a
            href="https://policies.google.com/privacy"
            target="_blank"
            rel="noreferrer"
            className="underline hover:text-surface-600"
          >
            Privacy Policy
          </a>{' '}
          and{' '}
          <a
            href="https://policies.google.com/terms"
            target="_blank"
            rel="noreferrer"
            className="underline hover:text-surface-600"
          >
            Terms of Service
          </a>{' '}
          apply.
        </p>
      )}
    </div>
  );
});

export default Captcha;
