import React, { useEffect, useId, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useNavigate } from 'react-router-dom';
import { Html5Qrcode } from 'html5-qrcode';
import { FaCamera, FaTimes } from 'react-icons/fa';
import { parseQrPayloadToAppPath } from '../utils/qrNavigation';

export interface QRScannerProps {
  isOpen: boolean;
  onClose: () => void;
  /** Override default navigation; receive original QR text + parsed in-app path */
  onScan?: (decodedText: string, appPath: string | null) => void;
  title?: string;
  /** Prefer rear camera on phones */
  facingMode?: 'environment' | 'user';
}

/**
 * Camera QR scanner modal. On success, navigates inside the SPA (including mobile webview)
 * using React Router so users stay in the app instead of opening an external browser.
 */
const QRScanner: React.FC<QRScannerProps> = ({
  isOpen,
  onClose,
  onScan,
  title = 'Scan QR Code',
  facingMode = 'environment',
}) => {
  const navigate = useNavigate();
  const reactId = useId();
  const readerId = `qr-reader-${reactId.replace(/:/g, '')}`;
  const scannerRef = useRef<Html5Qrcode | null>(null);
  const handledRef = useRef(false);
  const [error, setError] = useState<string | null>(null);
  const [starting, setStarting] = useState(false);

  const stopScanner = async () => {
    const scanner = scannerRef.current;
    scannerRef.current = null;
    if (!scanner) return;
    try {
      if (scanner.isScanning) {
        await scanner.stop();
      }
      scanner.clear();
    } catch {
      // ignore stop/clear errors when camera already released
    }
  };

  const handleDecoded = async (decodedText: string) => {
    if (handledRef.current) return;
    handledRef.current = true;

    const appPath = parseQrPayloadToAppPath(decodedText);

    if (onScan) {
      await stopScanner();
      onClose();
      onScan(decodedText, appPath);
      return;
    }

    if (appPath) {
      await stopScanner();
      onClose();
      // In-app navigation — works in mobile webview / PWA without leaving the app
      navigate(appPath);
      return;
    }

    handledRef.current = false;
    setError(`Unrecognized QR code: ${decodedText}`);
  };

  useEffect(() => {
    if (!isOpen) return;

    let cancelled = false;
    handledRef.current = false;
    setError(null);
    setStarting(true);

    const start = async () => {
      try {
        await stopScanner();
        if (cancelled) return;

        const scanner = new Html5Qrcode(readerId);
        scannerRef.current = scanner;

        await scanner.start(
          { facingMode },
          {
            fps: 10,
            qrbox: (viewfinderWidth, viewfinderHeight) => {
              const edge = Math.min(viewfinderWidth, viewfinderHeight);
              const size = Math.max(180, Math.floor(edge * 0.7));
              return { width: size, height: size };
            },
            aspectRatio: 1,
          },
          (decodedText) => {
            void handleDecoded(decodedText);
          },
          () => {
            // ignore frame-level "not found" callbacks
          }
        );
      } catch (err) {
        console.error('QR scanner start failed:', err);
        if (!cancelled) {
          const message =
            err instanceof Error ? err.message : 'Could not open camera';
          setError(
            message.includes('NotAllowedError') || message.toLowerCase().includes('permission')
              ? 'Camera permission denied. Please allow camera access and try again.'
              : `Camera unavailable: ${message}`
          );
        }
      } finally {
        if (!cancelled) setStarting(false);
      }
    };

    // Small delay so the reader DOM node is mounted
    const timer = window.setTimeout(() => {
      void start();
    }, 100);

    return () => {
      cancelled = true;
      window.clearTimeout(timer);
      void stopScanner();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isOpen, readerId, facingMode]);

  if (!isOpen) return null;

  return createPortal(
    <div className="fixed inset-0 z-[10050] flex items-center justify-center bg-black/70 p-4">
      <div className="w-full max-w-md overflow-hidden rounded-xl bg-white shadow-2xl dark:bg-gray-800">
        <div className="flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-700">
          <h3 className="flex items-center gap-2 text-base font-semibold text-gray-900 dark:text-white">
            <FaCamera className="text-teal-600" />
            {title}
          </h3>
          <button
            type="button"
            onClick={() => {
              void stopScanner().then(onClose);
            }}
            className="rounded-lg p-2 text-gray-500 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700"
            aria-label="Close scanner"
          >
            <FaTimes />
          </button>
        </div>

        <div className="space-y-3 p-4">
          <p className="text-xs text-gray-500 dark:text-gray-400">
            Point the camera at an asset QR code. You will stay inside the app.
          </p>

          {starting && !error && (
            <p className="text-center text-sm text-teal-600 dark:text-teal-400">Starting camera…</p>
          )}

          {error && (
            <div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/20 dark:text-red-300">
              {error}
            </div>
          )}

          <div
            id={readerId}
            className="overflow-hidden rounded-lg bg-black [&_video]:max-h-[60vh] [&_video]:w-full [&_video]:object-cover"
          />
        </div>

        <div className="border-t border-gray-200 px-4 py-3 dark:border-gray-700">
          <button
            type="button"
            onClick={() => {
              void stopScanner().then(onClose);
            }}
            className="w-full rounded-lg bg-gray-200 px-4 py-2 text-sm font-medium text-gray-800 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
          >
            Cancel
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
};

export interface ScanQRButtonProps {
  className?: string;
  label?: string;
  title?: string;
  onScan?: (decodedText: string, appPath: string | null) => void;
}

/**
 * Reusable Scan QR trigger + scanner modal. Use anywhere you need camera QR → in-app navigation.
 */
export const ScanQRButton: React.FC<ScanQRButtonProps> = ({
  className,
  label = 'Scan QR',
  title,
  onScan,
}) => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button
        type="button"
        onClick={() => setIsOpen(true)}
        className={
          className ||
          'bg-teal-600 hover:bg-teal-700 text-white px-4 py-2 rounded-lg flex items-center justify-center gap-2 shadow transition-all w-full sm:w-auto'
        }
      >
        <FaCamera />
        <span className="font-medium">{label}</span>
      </button>
      <QRScanner
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        onScan={onScan}
        title={title}
      />
    </>
  );
};

export default QRScanner;
