import { useCallback, useEffect, useRef, useState } from 'react';
import { useRouter } from 'next/router';
import Layout from '../components/Layout';
import ScannerCamera from '../components/scanner/ScannerCamera';
import ScannerCheckoutSheet from '../components/scanner/ScannerCheckoutSheet';
import ScannerResultPanel from '../components/scanner/ScannerResultPanel';
import ScannerHistoryStrip, {
  TAB_QUEUE,
  TAB_RECENT,
} from '../components/scanner/ScannerHistoryStrip';
import ScannerTips from '../components/scanner/ScannerTips';
import ScannerToast from '../components/scanner/ScannerToast';
import { Modal, Button } from '../components/ui';
import GlassSurface from '../components/ui/GlassSurface.js';
import { useAuth } from '../lib/use-auth';
import { useScannerSession } from '../lib/use-scanner-session.js';
import { useScannerQueue } from '../lib/use-scanner-queue.js';
import { useCameraScanner } from '../lib/use-camera-scanner.js';
import { useScannerIdentification } from '../lib/use-scanner-identification.js';
import { runSequentialGalleryIdentify } from '../lib/scanner-batch-identify.js';
import { clearScannerCartStorage } from '../lib/scanner-session.js';
import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js';

const TOAST_DURATION_MS = 2500;

export default function Scanner() {
  const { user, loading: authLoading } = useAuth();
  const router = useRouter();
  const [isDesktop, setIsDesktop] = useState(false);
  const [isCheckoutOpen, setIsCheckoutOpen] = useState(false);
  const [isListPickerOpen, setIsListPickerOpen] = useState(false);
  const [showLeaveModal, setShowLeaveModal] = useState(false);
  const [listCommitError, setListCommitError] = useState(null);
  const [inspectorCommitError, setInspectorCommitError] = useState(null);
  const [focusedCardId, setFocusedCardId] = useState(null);
  const [stripActiveTab, setStripActiveTab] = useState(TAB_RECENT);
  const [isAutoDetectPaused, setIsAutoDetectPaused] = useState(() => {
    if (typeof window === 'undefined') return false;
    return window.matchMedia('(max-width: 767px)').matches;
  });
  const [pageToast, setPageToast] = useState({ message: '', visible: false, type: 'success' });
  const [galleryBusy, setGalleryBusy] = useState(false);
  const [batchBusy, setBatchBusy] = useState(false);
  const [batchProgress, setBatchProgress] = useState(null);

  const verificationPausedRef = useRef(false);
  const autoDetectPausedRef = useRef(false);
  const prevCardCountRef = useRef(0);
  const scannedCardsRef = useRef([]);
  const toastTimerRef = useRef(null);
  const batchCancelRef = useRef(false);
  const deskGalleryInputRef = useRef(null);
  const batchInputRef = useRef(null);
  const disambiguationActiveRef = useRef(false);

  const { scanDefaults } = useScannerSession();
  const queue = useScannerQueue({ user, scanDefaults, deckMode: null });

  const onVerifyCardRef = useRef(null);

  const camera = useCameraScanner({
    onError: (msg) => console.error('Camera error:', msg),
    onVerifyCard: (cardTracker) => onVerifyCardRef.current?.(cardTracker),
    verificationPausedRef,
    autoDetectPausedRef,
  });

  const identification = useScannerIdentification({
    onCardScanned: queue.handleCardScanned,
    onError: (msg) => console.error('Identification error:', msg),
    onTrackerComplete: camera.removeTrackedCard,
    onTrackerReset: camera.resetTrackedCard,
    videoRef: camera.videoRef,
    canvasRef: camera.canvasRef,
    onVerifyCardRef,
    verificationPausedRef,
  });

  const unprocessedCount = queue.unprocessedCount;
  const focusedCard =
    queue.scannedCards.find((card) => card.id === focusedCardId) ?? null;

  useEffect(() => {
    scannedCardsRef.current = queue.scannedCards;
  }, [queue.scannedCards]);

  useEffect(() => {
    const mq = window.matchMedia('(min-width: 768px)');
    const sync = () => setIsDesktop(mq.matches);
    sync();
    mq.addEventListener('change', sync);
    return () => mq.removeEventListener('change', sync);
  }, []);

  useEffect(() => {
    if (!authLoading && !user) {
      router.push(`/login?returnUrl=${encodeURIComponent('/scanner')}`);
    }
  }, [authLoading, user, router]);

  useEffect(() => {
    disambiguationActiveRef.current = Boolean(identification.disambiguation);
  }, [identification.disambiguation]);

  useEffect(() => {
    const mobileCheckoutPauses =
      typeof window !== 'undefined' &&
      window.matchMedia('(max-width: 767px)').matches &&
      isCheckoutOpen;

    verificationPausedRef.current =
      mobileCheckoutPauses ||
      isListPickerOpen ||
      Boolean(identification.disambiguation);

    autoDetectPausedRef.current = isAutoDetectPaused;
  }, [
    isCheckoutOpen,
    isListPickerOpen,
    isAutoDetectPaused,
    identification.disambiguation,
  ]);

  useEffect(() => {
    if (queue.scannedCards.length > prevCardCountRef.current) {
      const latestUnprocessed = queue.scannedCards.find((card) => !card.processed);
      if (latestUnprocessed) {
        // Auto-focus the newest unprocessed scan when the queue grows.
        // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional focus sync on enqueue
        setFocusedCardId(latestUnprocessed.id);
      }
    }
    prevCardCountRef.current = queue.scannedCards.length;
  }, [queue.scannedCards]);

  useEffect(() => {
    return () => clearTimeout(toastTimerRef.current);
  }, []);

  const showPageToast = useCallback((message, type = 'success') => {
    clearTimeout(toastTimerRef.current);
    setPageToast({ message, visible: true, type });
    toastTimerRef.current = setTimeout(() => {
      setPageToast((current) => ({ ...current, visible: false }));
    }, TOAST_DURATION_MS);
  }, []);

  const handleLeaveConfirm = () => {
    clearScannerCartStorage();
    queue.clearScannedCards();
    setShowLeaveModal(false);
    router.back();
  };

  const handleListPick = async (collectionId) => {
    setListCommitError(null);
    const card = isDesktop ? focusedCard : null;
    const selectedIds = isDesktop
      ? card
        ? [card.id]
        : []
      : queue.scannedCards
          .filter((entry) => queue.selectedCards.has(entry.id))
          .map((entry) => entry.id);

    if (selectedIds.length === 0) return;

    if (isDesktop && card) {
      const success = await queue.addSingleCardToCollection(card, collectionId);
      if (!success) {
        setListCommitError('Could not add cards to the list. Try again.');
        return;
      }
      setIsListPickerOpen(false);
      const remaining = queue.scannedCards.filter(
        (entry) => !entry.processed && entry.id !== card.id
      );
      setFocusedCardId(remaining[0]?.id ?? null);
      return;
    }

    const successfulIds = await queue.commitSelectedToCollection(collectionId);
    if (!successfulIds?.length) {
      setListCommitError('Could not add cards to the list. Try again.');
      return;
    }

    setIsListPickerOpen(false);
  };

  const handleInspectorAddToOwned = async (card) => {
    setInspectorCommitError(null);
    const success = await queue.addSingleCardToOwned(card);
    if (!success) {
      setInspectorCommitError('Could not add card. Try again.');
      return;
    }

    showPageToast(`Added to ${VOCAB.MY_COLLECTION}`);
    const remaining = queue.scannedCards.filter(
      (entry) => !entry.processed && entry.id !== card.id
    );
    setFocusedCardId(remaining[0]?.id ?? null);
  };

  const handleInspectorAddToList = () => {
    if (!focusedCard) return;
    setListCommitError(null);
    setIsListPickerOpen(true);
  };

  const handleRescan = async (card) => {
    queue.updateCardMetadata(card.id, {
      processed: false,
      identifyFailed: false,
      identifyFailureReason: undefined,
      confidence: undefined,
    });

    if (card.scanImageUrl) {
      try {
        const response = await fetch(card.scanImageUrl);
        const blob = await response.blob();
        const file = new File([blob], 'rescan.jpg', { type: blob.type || 'image/jpeg' });
        await identification.identifyFromGalleryFile(file);
      } catch (error) {
        showPageToast(error.message || 'Rescan failed', 'error');
      }
      return;
    }

    showPageToast('Rescan from camera', 'info');
  };

  const enqueueFailedIdentify = useCallback(
    (file, error) => {
      const baseName = file?.name?.replace(/\.[^.]+$/, '') || 'Unknown image';
      queue.handleCardScanned({
        name: baseName,
        set: 'Batch scan',
        identifyFailed: true,
        identifyFailureReason:
          error?.message || 'Could not identify card from gallery image',
      });
    },
    [queue]
  );

  const identifyGalleryFileOrThrow = useCallback(
    async (file) => {
      const countBefore = scannedCardsRef.current.length;

      await identification.identifyFromGalleryFile(file);

      await new Promise((resolve) => setTimeout(resolve, 50));

      if (scannedCardsRef.current.length > countBefore) return;
      if (disambiguationActiveRef.current) return;

      throw new Error('Could not identify card from gallery image');
    },
    [identification]
  );

  const handleDeskGalleryChange = async (event) => {
    const file = event.target.files?.[0];
    event.target.value = '';
    if (!file) return;

    setGalleryBusy(true);
    try {
      await identification.identifyFromGalleryFile(file);
    } finally {
      setGalleryBusy(false);
    }
  };

  const handleBatchChange = async (event) => {
    const files = event.target.files;
    event.target.value = '';
    if (!files?.length) return;

    batchCancelRef.current = false;
    setStripActiveTab(TAB_QUEUE);
    setBatchBusy(true);
    setBatchProgress({ active: true, current: 0, total: files.length });

    try {
      await runSequentialGalleryIdentify(files, identifyGalleryFileOrThrow, {
        cancelRef: batchCancelRef,
        onProgress: ({ current, total }) => {
          setBatchProgress({
            active: true,
            current,
            total,
            onCancel: () => {
              batchCancelRef.current = true;
            },
          });
        },
        onFileError: ({ file, error }) => {
          enqueueFailedIdentify(file, error);
        },
      });
    } finally {
      setBatchBusy(false);
      setBatchProgress(null);
      batchCancelRef.current = false;
    }
  };

  const handleClearAll = () => {
    if (batchProgress?.active) return;
    clearScannerCartStorage();
    queue.clearScannedCards();
    setFocusedCardId(null);
  };

  const handleBack = () => {
    if (unprocessedCount > 0) {
      setShowLeaveModal(true);
      return;
    }
    router.back();
  };

  if (authLoading) {
    return (
      <Layout user={null}>
        <div className="flex items-center justify-center min-h-[50vh]">
          <div
            className="animate-spin rounded-full h-12 w-12 border-b-2"
            style={{ borderColor: 'var(--text-accent)' }}
          />
        </div>
      </Layout>
    );
  }

  if (!user) {
    return null;
  }

  const listPickerDescription = isDesktop
    ? `Add ${focusedCard?.name ?? 'this card'} to one of your lists.`
    : 'Add the selected cards to one of your lists.';

  return (
    <Layout user={user} chrome={isDesktop ? 'default' : 'immersive'}>
      <div className="flex flex-col h-full min-h-0 gap-4">
        {isDesktop && (
          <div className="hidden md:flex items-start justify-between gap-4">
            <div>
              <h1
                className="text-2xl font-semibold"
                style={{ color: 'var(--text-primary)' }}
              >
                Card Scanner
              </h1>
              <p className="text-sm mt-1" style={{ color: 'var(--text-secondary)' }}>
                Identify cards with your webcam and add them to {VOCAB.MY_COLLECTION}.
              </p>
            </div>
            <ScannerTips />
          </div>
        )}

        <div className="relative flex flex-col md:flex-row flex-1 min-h-0 max-md:fixed max-md:inset-0">
          <div
            className="flex-1 min-h-0 min-w-0 flex flex-col gap-3"
            inert={isCheckoutOpen ? true : undefined}
            aria-hidden={isCheckoutOpen || undefined}
          >
            <ScannerCamera
              queue={queue}
              camera={camera}
              identification={identification}
              variant={isDesktop ? 'workstation' : 'default'}
              autoDetectOn={!isAutoDetectPaused}
              onBack={handleBack}
              onOpenCheckout={() => setIsCheckoutOpen(true)}
              onGalleryIdentify={(file) => identification.identifyFromGalleryFile(file)}
              latestPeekCard={queue.scannedCards[0] ?? null}
              cartCount={unprocessedCount}
              isCheckoutOpen={isCheckoutOpen}
              verificationPausedRef={verificationPausedRef}
            />

            {isDesktop && (
              <GlassSurface
                tint="mid"
                rim="subtle"
                blur="mid"
                className="hidden md:flex flex-wrap items-center gap-3 rounded-xl px-4 py-3"
              >
                <div className="flex flex-col gap-1 min-w-[180px] flex-1">
                  <label
                    htmlFor="scanner-camera-select"
                    className="text-xs font-medium"
                    style={{ color: 'var(--text-secondary)' }}
                  >
                    Camera
                  </label>
                  <select
                    id="scanner-camera-select"
                    value={camera.selectedDeviceId}
                    onChange={(event) => camera.setSelectedDeviceId(event.target.value)}
                    disabled={camera.devicePickerStatus !== 'ready'}
                    aria-describedby={
                      camera.devicePickerMessage
                        ? 'scanner-camera-select-hint'
                        : undefined
                    }
                    className="w-full px-3 py-2 rounded-xl text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
                    style={{
                      backgroundColor: 'var(--bg-secondary)',
                      border: '1px solid var(--border)',
                      color: 'var(--text-primary)',
                      minHeight: 44,
                      '--tw-ring-color': 'var(--accent-ember)',
                      '--tw-ring-offset-color': 'transparent',
                    }}
                  >
                    {camera.videoDevices.map((device) => (
                      <option key={device.deviceId || device.label} value={device.deviceId}>
                        {device.label || 'Camera'}
                      </option>
                    ))}
                  </select>
                  {camera.devicePickerMessage && (
                    <p
                      id="scanner-camera-select-hint"
                      className="text-xs"
                      style={{ color: 'var(--text-secondary)' }}
                    >
                      {camera.devicePickerMessage}
                    </p>
                  )}
                </div>

                <Button
                  variant="secondary"
                  size="sm"
                  loading={galleryBusy}
                  disabled={galleryBusy || batchBusy}
                  onClick={() => deskGalleryInputRef.current?.click()}
                >
                  Upload Image
                </Button>

                <Button
                  variant="secondary"
                  size="sm"
                  loading={batchBusy}
                  disabled={galleryBusy || batchBusy}
                  onClick={() => batchInputRef.current?.click()}
                >
                  Batch Scan
                </Button>

                <div className="flex items-center gap-2">
                  <span
                    id="scanner-auto-detect-label"
                    className="text-sm font-medium"
                    style={{ color: 'var(--text-secondary)' }}
                  >
                    Auto-detect
                  </span>
                  <button
                    type="button"
                    role="switch"
                    aria-checked={!isAutoDetectPaused}
                    aria-labelledby="scanner-auto-detect-label"
                    onClick={() => setIsAutoDetectPaused((paused) => !paused)}
                    className="relative inline-flex h-7 w-12 flex-shrink-0 rounded-full transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
                    style={{
                      backgroundColor: !isAutoDetectPaused
                        ? 'var(--accent-ember)'
                        : 'var(--bg-tertiary)',
                      border: '1px solid var(--border)',
                      minWidth: 44,
                      minHeight: 44,
                      '--tw-ring-color': 'var(--accent-ember)',
                      '--tw-ring-offset-color': 'transparent',
                    }}
                  >
                    <span
                      className="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow transition duration-200"
                      style={{
                        transform: !isAutoDetectPaused
                          ? 'translateX(1.35rem)'
                          : 'translateX(0.15rem)',
                        marginTop: '0.35rem',
                      }}
                      aria-hidden="true"
                    />
                  </button>
                </div>

                <input
                  ref={deskGalleryInputRef}
                  type="file"
                  accept="image/*"
                  className="sr-only"
                  tabIndex={-1}
                  onChange={handleDeskGalleryChange}
                />
                <input
                  ref={batchInputRef}
                  type="file"
                  accept="image/*"
                  multiple
                  className="sr-only"
                  tabIndex={-1}
                  onChange={handleBatchChange}
                />
              </GlassSurface>
            )}
          </div>

          <div className="md:hidden">
            {isCheckoutOpen && (
              <ScannerCheckoutSheet
                queue={queue}
                onClose={() => setIsCheckoutOpen(false)}
                onOpenListPicker={() => setIsListPickerOpen(true)}
                trapActive={!isListPickerOpen}
              />
            )}
          </div>

          {isDesktop && (
            <div
              className="hidden md:flex md:w-[360px] md:flex-shrink-0 md:min-h-0 md:pl-4"
            >
              <ScannerResultPanel
                focusedCard={focusedCard}
                queue={queue}
                isIdentifying={
                  identification.isIdentifying || galleryBusy || batchBusy
                }
                commitError={inspectorCommitError}
                onAddToOwned={handleInspectorAddToOwned}
                onAddToList={handleInspectorAddToList}
                onRescan={handleRescan}
              />
            </div>
          )}
        </div>

        {isDesktop && (
          <div className="hidden md:block">
            <ScannerHistoryStrip
              scannedCards={queue.scannedCards}
              ownershipMap={queue.ownershipMap}
              focusedCardId={focusedCardId}
              onFocusCard={setFocusedCardId}
              activeTab={stripActiveTab}
              onTabChange={setStripActiveTab}
              batchProgress={batchProgress}
              onClearAll={handleClearAll}
              onCommitSelectedToOwned={() => queue.commitSelectedToOwned()}
              onOpenListPicker={() => setIsListPickerOpen(true)}
              isProcessing={queue.isProcessing}
            />
          </div>
        )}

        <ScannerToast
          message={pageToast.message}
          visible={pageToast.visible}
          type={pageToast.type}
        />

        <Modal
          open={showLeaveModal}
          onClose={() => setShowLeaveModal(false)}
          title="Leave scanner?"
          description={`${unprocessedCount} scanned ${unprocessedCount === 1 ? 'card' : 'cards'} haven't been added yet. Leaving will clear your cart.`}
        >
          <div className="flex flex-col sm:flex-row gap-3 sm:justify-end">
            <Button variant="secondary" onClick={() => setShowLeaveModal(false)}>
              Keep scanning
            </Button>
            <Button variant="danger" onClick={handleLeaveConfirm}>
              Leave
            </Button>
          </div>
        </Modal>

        <Modal
          open={isListPickerOpen}
          onClose={() => {
            setIsListPickerOpen(false);
            setListCommitError(null);
          }}
          title="Choose a List"
          description={listPickerDescription}
        >
          {listCommitError && (
            <div
              className="mb-3 px-3 py-2 rounded-lg text-sm"
              style={{
                backgroundColor: 'color-mix(in srgb, var(--color-error) 12%, transparent)',
                border: '1px solid color-mix(in srgb, var(--color-error) 35%, transparent)',
                color: 'var(--color-error)',
              }}
              role="alert"
            >
              {listCommitError}
            </div>
          )}
          <ul className="space-y-2 max-h-[50vh] overflow-y-auto">
            {queue.collections.map((collection) => (
              <li key={collection.id}>
                <button
                  type="button"
                  onClick={() => handleListPick(collection.id)}
                  disabled={queue.isProcessing}
                  className="w-full text-left px-4 py-3 rounded-xl transition-opacity hover:opacity-80 disabled:opacity-50 glass-panel"
                  style={{ color: 'var(--text-primary)', minHeight: 44 }}
                >
                  {collectionDisplayName(collection)}
                </button>
              </li>
            ))}
            {queue.collections.length === 0 && (
              <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                No lists yet. Create a list from the Lists page first.
              </p>
            )}
          </ul>
        </Modal>
      </div>
    </Layout>
  );
}
