/**
 * VendorQrScan - camera-based QR scanner for vendor purchase redemption (FDS §5.11)
 * and pickup confirmation.
 *
 * Opens camera via getUserMedia.
 * Uses BarcodeDetector API if available, else manual code entry fallback.
 * On scan: detects token type and POSTs to:
 *   - /api/purchases/[id]/redeem  (redeem tokens: multideal:redeem/... or id:token)
 *   - /api/shipments/[id]/pickup-confirm  (pickup tokens: shipmentId.exp.nonce.sigHex)
 * Success: green confirmation screen.
 * Error: red error screen with reason.
 */

'use client';

import { useEffect, useRef, useState, useCallback } from 'react';
import { getCsrfToken } from '@/lib/csrf';
import { VendorShell } from '@/components/ui/layout/VendorShell';
import { Badge } from '@/components/ui/primitives/Badge';
import { Button } from '@/components/ui/primitives/Button';
import { Input } from '@/components/ui/primitives/Input';
import { Label } from '@/components/ui/primitives/Label';
import { Icon } from '@/components/ui/icons/Icon';
import { useT } from '@/lib/i18n/react';
import { ErrorBoundary } from '@/components/ui/feedback/ErrorBoundary';
import { ErrorState } from '@/components/ui/feedback/ErrorState';
import { EmptyState } from '@/components/ui/feedback/EmptyState';
import { HydratedIsland } from '@/components/HydratedIsland';
import { captureCaught } from '@/lib/observability';

// ─── Types ────────────────────────────────────────────────────────────────────

interface SkuOptionLabel {
  axisName: string;
  optionLabel: string;
}

interface SkuInfo {
  id: string;
  label: string;
  optionLabels: SkuOptionLabel[];
}

type ScanResult =
  | { status: 'idle' }
  | { status: 'scanning' }
  | { status: 'success'; dealName: string; customerName: string; sku: SkuInfo | null }
  | { status: 'pickup_success' }
  | {
      status: 'error';
      errorKey:
        | 'error_already_redeemed'
        | 'error_expired'
        | 'error_wrong_deal'
        | 'error_invalid'
        | 'error_pickup_already_done'
        | 'error_pickup_invalid';
    };

type FlashTone = 'success' | 'error' | null;

// ─── BarcodeDetector type augmentation ────────────────────────────────────────

interface BarcodeResult {
  rawValue: string;
  format: string;
}

interface BarcodeDetectorLike {
  detect(source: ImageBitmapSource): Promise<BarcodeResult[]>;
}

declare const BarcodeDetector:
  | {
      new (options?: { formats: string[] }): BarcodeDetectorLike;
    }
  | undefined;

// ─── Helper: detect pickup HMAC token → shipmentId + token ───────────────────

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const HEX_RE = /^[0-9a-f]+$/i;

/**
 * Pickup tokens produced by PickupPseudoAdapter.signPickupToken have the format:
 *   `${shipmentId}.${exp}.${nonce}.${sigHex}`
 * where shipmentId is a UUID and sigHex is a hex SHA-256 HMAC.
 */
function parsePickupToken(raw: string): { shipmentId: string; token: string } | null {
  const parts = raw.split('.');
  if (parts.length !== 4) return null;
  const [shipmentId, , , sigHex] = parts as [string, string, string, string];
  if (!UUID_RE.test(shipmentId)) return null;
  if (!HEX_RE.test(sigHex) || sigHex.length < 16) return null;
  return { shipmentId, token: raw };
}

// ─── Helper: parse QR token → purchaseId + token ──────────────────────────────

function parseQrValue(raw: string): { purchaseId: string; token: string } | null {
  try {
    // Expected format: multideal:redeem/{purchaseId}/{token}
    const url = new URL(raw);
    const parts = url.pathname.split('/').filter(Boolean);
    if (url.protocol === 'multideal:' && parts[0] === 'redeem' && parts[1] && parts[2]) {
      return { purchaseId: parts[1], token: parts[2] };
    }
    return null;
  } catch (err) {
    captureCaught(err, { scope: 'features.vendor-qr-scan.VendorQrScan', severity: 'warning' });
    // Try plain format: {purchaseId}:{token}
    const colonIdx = raw.indexOf(':');
    if (colonIdx > 0) {
      return { purchaseId: raw.slice(0, colonIdx), token: raw.slice(colonIdx + 1) };
    }
    return null;
  }
}

// ─── Main component ───────────────────────────────────────────────────────────

export function VendorQrScan() {
  const t = useT('vendor_qr_scan');
  const tCommon = useT('common');
  const videoRef = useRef<HTMLVideoElement>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const animFrameRef = useRef<number | null>(null);
  const [scanResult, setScanResult] = useState<ScanResult>({ status: 'idle' });
  const [showManual, setShowManual] = useState(false);
  const [manualCode, setManualCode] = useState('');
  const [lastFailedCode, setLastFailedCode] = useState('');
  const [cameraError, setCameraError] = useState<string | null>(null);
  const [flashTone, setFlashTone] = useState<FlashTone>(null);

  const updateScanResult = useCallback((next: ScanResult) => {
    setScanResult(next);
    setFlashTone(
      next.status === 'success' || next.status === 'pickup_success'
        ? 'success'
        : next.status === 'error'
          ? 'error'
          : null,
    );
  }, []);

  const announcement =
    scanResult.status === 'success'
      ? t('flash_success_announcement')
      : scanResult.status === 'error'
        ? t('flash_error_announcement')
        : scanResult.status === 'pickup_success'
          ? t('pickup_success_title')
          : '';

  useEffect(() => {
    if (
      scanResult.status !== 'success' &&
      scanResult.status !== 'error' &&
      scanResult.status !== 'pickup_success'
    )
      return;

    const timeoutId = window.setTimeout(() => {
      setFlashTone(null);
    }, 1200);

    return () => {
      window.clearTimeout(timeoutId);
    };
  }, [scanResult.status]);

  const redeem = useCallback(
    async (purchaseId: string, token: string) => {
      try {
        const res = await fetch(`/api/purchases/${purchaseId}/redeem`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
          body: JSON.stringify({ token }),
        });
        const data = (await res.json()) as {
          ok: boolean;
          dealName?: string;
          customerName?: string;
          code?: string;
          sku?: SkuInfo | null;
        };

        if (data.ok) {
          updateScanResult({
            status: 'success',
            dealName: data.dealName ?? '',
            customerName: data.customerName ?? '',
            sku: data.sku ?? null,
          });
        } else {
          const code = data.code ?? '';
          const errorKey =
            code === 'ALREADY_REDEEMED'
              ? 'error_already_redeemed'
              : code === 'EXPIRED'
                ? 'error_expired'
                : code === 'WRONG_DEAL'
                  ? 'error_wrong_deal'
                  : 'error_invalid';
          updateScanResult({ status: 'error', errorKey });
          setLastFailedCode(`${purchaseId}:${token}`);
        }
      } catch (err) {
        captureCaught(err, { scope: 'features.vendor-qr-scan.VendorQrScan', severity: 'warning' });
        updateScanResult({ status: 'error', errorKey: 'error_invalid' });
        setLastFailedCode(`${purchaseId}:${token}`);
      }
    },
    [updateScanResult],
  );

  const confirmPickup = useCallback(
    async (shipmentId: string, token: string) => {
      try {
        const res = await fetch(`/api/shipments/${shipmentId}/pickup-confirm`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
          body: JSON.stringify({ token }),
        });
        if (res.ok) {
          updateScanResult({ status: 'pickup_success' });
        } else {
          // Map by HTTP status — pickup-confirm returns error strings, not code fields
          if (res.status === 409) {
            updateScanResult({ status: 'error', errorKey: 'error_pickup_already_done' });
          } else {
            updateScanResult({ status: 'error', errorKey: 'error_pickup_invalid' });
          }
          setLastFailedCode(token);
        }
      } catch (err) {
        captureCaught(err, {
          scope: 'features.vendor-qr-scan.VendorQrScan.confirmPickup',
          severity: 'warning',
        });
        updateScanResult({ status: 'error', errorKey: 'error_pickup_invalid' });
        setLastFailedCode(token);
      }
    },
    [updateScanResult],
  );

  const stopCamera = useCallback(() => {
    if (animFrameRef.current !== null) {
      cancelAnimationFrame(animFrameRef.current);
      animFrameRef.current = null;
    }
    streamRef.current?.getTracks().forEach((t) => t.stop());
    streamRef.current = null;
  }, []);

  const handleScannedValue = useCallback(
    async (raw: string) => {
      if (
        scanResult.status === 'success' ||
        scanResult.status === 'pickup_success' ||
        scanResult.status === 'error'
      )
        return;
      // Try pickup token first (format: shipmentId.exp.nonce.sigHex)
      const pickup = parsePickupToken(raw);
      if (pickup) {
        stopCamera();
        await confirmPickup(pickup.shipmentId, pickup.token);
        return;
      }
      // Fall back to redemption token
      const parsed = parseQrValue(raw);
      if (!parsed) {
        updateScanResult({ status: 'error', errorKey: 'error_invalid' });
        setLastFailedCode(raw);
        return;
      }
      stopCamera();
      await redeem(parsed.purchaseId, parsed.token);
    },
    [scanResult.status, redeem, confirmPickup, stopCamera, updateScanResult],
  );

  const startCamera = useCallback(async () => {
    setCameraError(null);
    updateScanResult({ status: 'scanning' });
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: 'environment' },
      });
      streamRef.current = stream;
      if (videoRef.current) {
        videoRef.current.srcObject = stream;
        await videoRef.current.play();
      }

      // Use BarcodeDetector if available
      if (typeof BarcodeDetector !== 'undefined') {
        const detector = new BarcodeDetector({ formats: ['qr_code'] });

        async function tick() {
          if (!videoRef.current || videoRef.current.readyState < 2) {
            animFrameRef.current = requestAnimationFrame(tick);
            return;
          }
          try {
            const results = await detector.detect(videoRef.current);
            if (results.length > 0 && results[0]) {
              await handleScannedValue(results[0].rawValue);
              return;
            }
          } catch (err) {
            captureCaught(err, {
              scope: 'features.vendor-qr-scan.VendorQrScan',
              severity: 'warning',
            });
            // continue
          }
          animFrameRef.current = requestAnimationFrame(tick);
        }
        animFrameRef.current = requestAnimationFrame(tick);
      } else {
        // No BarcodeDetector - prompt manual entry
        setShowManual(true);
      }
    } catch (err) {
      captureCaught(err, { scope: 'features.vendor-qr-scan.startCamera', severity: 'warning' });
      setCameraError(t('camera_error'));
      updateScanResult({ status: 'idle' });
      setShowManual(true);
    }
  }, [handleScannedValue, t, updateScanResult]);

  const startCameraRef = useRef(startCamera);
  const stopCameraRef = useRef(stopCamera);
  useEffect(() => {
    startCameraRef.current = startCamera;
    stopCameraRef.current = stopCamera;
  }, [startCamera, stopCamera]);

  useEffect(() => {
    const cancelled = { current: false };
    void Promise.resolve().then(() => {
      if (!cancelled.current) void startCameraRef.current();
    });
    return () => {
      cancelled.current = true;
      stopCameraRef.current();
    };
  }, []);

  async function handleManualSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
    e.preventDefault();
    const code = (manualCode || lastFailedCode).trim();
    if (!code) return;
    setManualCode(code);
    stopCamera();
    await handleScannedValue(code);
  }

  function reset() {
    updateScanResult({ status: 'idle' });
    setManualCode('');
    setLastFailedCode('');
    void startCamera();
  }

  function retryFromError() {
    if (lastFailedCode) {
      setManualCode(lastFailedCode);
    }
    updateScanResult({ status: 'idle' });
    setShowManual(true);
    void startCamera();
  }

  return (
    <HydratedIsland>
      <ErrorBoundary>
        <VendorShell variant="dashboard" currentPath="/vendor/scan">
          <section aria-label={t('title')} className="flex flex-col items-center gap-4 px-4 py-4">
            <div className="sr-only" aria-live="assertive" aria-atomic="true">
              {announcement}
            </div>
            {/* Success state */}
            {scanResult.status === 'success' && (
              <div
                role="status"
                aria-live="polite"
                className={
                  flashTone === 'success'
                    ? 'bg-success-100 border-success-500 text-success-700 flex w-full flex-col items-center gap-4 rounded-xl border-2 p-6 text-center shadow-md transition-colors duration-300 motion-reduce:transition-none'
                    : 'bg-success-50 flex w-full flex-col items-center gap-4 rounded-xl p-6 text-center'
                }
              >
                <span className="text-success-600" aria-hidden="true">
                  <Icon name="Check" size="xl" color="success" />
                </span>
                <h2 className="text-success-700 text-xl font-bold">{t('success_title')}</h2>
                <p className="text-success-700 text-sm">{scanResult.dealName}</p>
                {scanResult.customerName && (
                  <p className="text-success-600 text-sm">{scanResult.customerName}</p>
                )}
                {scanResult.sku && scanResult.sku.optionLabels.length > 0 && (
                  <div className="mt-1 flex flex-wrap justify-center gap-2">
                    {scanResult.sku.optionLabels.map((o) => (
                      <Badge key={o.axisName} tone="info" size="sm">
                        <span className="text-muted me-1">{o.axisName}:</span>
                        <span className="font-semibold">{o.optionLabel}</span>
                      </Badge>
                    ))}
                  </div>
                )}
                <Button variant="primary" size="md" onClick={reset} className="mt-2">
                  {t('scan_again')}
                </Button>
                <Button
                  variant="secondary"
                  size="md"
                  onClick={() => {
                    window.location.href = '/vendor/dashboard';
                  }}
                >
                  {tCommon('done')}
                </Button>
              </div>
            )}

            {/* Pickup success state */}
            {scanResult.status === 'pickup_success' && (
              <div
                role="status"
                aria-live="polite"
                className={
                  flashTone === 'success'
                    ? 'bg-success-100 border-success-500 text-success-700 flex w-full flex-col items-center gap-4 rounded-xl border-2 p-6 text-center shadow-md transition-colors duration-300 motion-reduce:transition-none'
                    : 'bg-success-50 flex w-full flex-col items-center gap-4 rounded-xl p-6 text-center'
                }
              >
                <span className="text-success-600" aria-hidden="true">
                  <Icon name="Check" size="xl" color="success" />
                </span>
                <h2 className="text-success-700 text-xl font-bold">{t('pickup_success_title')}</h2>
                <p className="text-success-600 text-sm">{t('pickup_success_detail')}</p>
                <Button variant="primary" size="md" onClick={reset} className="mt-2">
                  {t('scan_again')}
                </Button>
                <Button
                  variant="secondary"
                  size="md"
                  onClick={() => {
                    window.location.href = '/vendor/dashboard';
                  }}
                >
                  {tCommon('done')}
                </Button>
              </div>
            )}

            {/* Error / no-match state */}
            {scanResult.status === 'error' && (
              <div
                className={
                  flashTone === 'error'
                    ? 'bg-danger-100 border-danger-500 flex w-full flex-col gap-4 rounded-xl border-2 p-4 shadow-md transition-colors duration-300 motion-reduce:transition-none'
                    : 'flex w-full flex-col gap-4'
                }
              >
                {scanResult.errorKey === 'error_invalid' ? (
                  <EmptyState
                    title={t('no_match_title')}
                    description={t(scanResult.errorKey)}
                    action={
                      <Button variant="primary" size="md" onClick={retryFromError}>
                        {t('try_again')}
                      </Button>
                    }
                  />
                ) : (
                  <ErrorState
                    title={t('error_heading')}
                    description={t(scanResult.errorKey)}
                    action={
                      <Button variant="primary" size="md" onClick={retryFromError}>
                        {t('try_again')}
                      </Button>
                    }
                  />
                )}
                <form
                  onSubmit={handleManualSubmit}
                  className="flex w-full flex-col gap-3"
                  aria-label={t('manual_entry')}
                >
                  <Label htmlFor="manual-code-error" className="text-sm font-medium">
                    {t('manual_entry')}
                  </Label>
                  <p className="text-text-muted text-xs">{t('manual_helper')}</p>
                  <div className="flex gap-2">
                    <Input
                      id="manual-code-error"
                      data-testid="manual-code-input"
                      type="text"
                      dir="ltr"
                      value={manualCode || lastFailedCode}
                      onChange={(e) => setManualCode(e.target.value)}
                      placeholder={t('manual_placeholder')}
                      className="flex-1"
                    />
                    <Button
                      type="submit"
                      variant="primary"
                      size="md"
                      data-testid="manual-code-submit"
                    >
                      {t('manual_submit')}
                    </Button>
                  </div>
                </form>
              </div>
            )}

            {/* Scanning state */}
            {(scanResult.status === 'scanning' || scanResult.status === 'idle') && (
              <>
                {/* Camera viewfinder */}
                <div className="relative aspect-square w-full max-w-sm overflow-hidden rounded-xl bg-black">
                  <video
                    ref={videoRef}
                    className="h-full w-full object-cover"
                    playsInline
                    muted
                    aria-label={t('instruction')}
                  />
                  {/* Viewfinder overlay */}
                  <div
                    className="pointer-events-none absolute inset-0 flex items-center justify-center"
                    aria-hidden="true"
                  >
                    <div className="h-48 w-48 rounded-xl border-4 border-white/70" />
                  </div>
                </div>

                <p className="text-text-secondary text-center text-sm">
                  {typeof BarcodeDetector === 'undefined' ? t('no_detector') : t('instruction')}
                </p>

                {cameraError && (
                  <p role="alert" className="text-danger-600 text-center text-sm">
                    {cameraError}
                  </p>
                )}
              </>
            )}

            {/* Manual entry fallback */}
            {(showManual || scanResult.status === 'idle') &&
              scanResult.status !== 'success' &&
              scanResult.status !== 'pickup_success' &&
              scanResult.status !== 'error' && (
                <form
                  onSubmit={handleManualSubmit}
                  className="flex w-full flex-col gap-3"
                  aria-label={t('manual_entry')}
                >
                  <Label htmlFor="manual-code" className="text-sm font-medium">
                    {t('manual_entry')}
                  </Label>
                  <p className="text-text-muted text-xs">{t('manual_helper')}</p>
                  <div className="flex gap-2">
                    <Input
                      id="manual-code"
                      data-testid="manual-code-input"
                      type="text"
                      dir="ltr"
                      value={manualCode}
                      onChange={(e) => setManualCode(e.target.value)}
                      placeholder={t('manual_placeholder')}
                      className="flex-1"
                    />
                    <Button
                      type="submit"
                      variant="primary"
                      size="md"
                      data-testid="manual-code-submit"
                    >
                      {t('manual_submit')}
                    </Button>
                  </div>
                </form>
              )}
          </section>
        </VendorShell>
      </ErrorBoundary>
    </HydratedIsland>
  );
}
