import { useCallback, useEffect, useRef, useState } from 'react';
import {
  addScannedCardToCollection,
  addScannedCardToDeck,
  addScannedCardToOwned,
  createScannerCollection,
  fetchBatchOwnership,
  fetchScannerCollections,
  fetchScannerDecks,
} from './scanner-route-api.js';
import {
  loadScannerCart,
  mergeScannedCardEntry,
  saveScannerCart,
} from './scanner-session.js';

const UNDO_TTL_MS = 30_000;
const UNDO_CLEANUP_INTERVAL_MS = 5_000;
const OWNERSHIP_DEBOUNCE_MS = 500;
const CART_PERSIST_DEBOUNCE_MS = 300;

/**
 * Scanned-card queue, bulk selection, in-flight guards, and destination lists.
 */
export function useScannerQueue({ user, sessionDestination, scanDefaults, deckMode }) {
  const [scannedCards, setScannedCards] = useState(() => {
    const cart = loadScannerCart();
    return cart?.scannedCards ?? [];
  });
  const [collections, setCollections] = useState([]);
  const [decks, setDecks] = useState([]);
  const [selectedCards, setSelectedCards] = useState(() => {
    const cart = loadScannerCart();
    return new Set(cart?.selectedCardIds ?? []);
  });
  const [bulkAction, setBulkAction] = useState('');
  const [bulkTarget, setBulkTarget] = useState('');
  const [isProcessing, setIsProcessing] = useState(false);
  const [autoRouteError, setAutoRouteError] = useState(null);
  const [showCreateCollection, setShowCreateCollection] = useState(false);
  const [newCollectionName, setNewCollectionName] = useState('');
  const [undoStack, setUndoStack] = useState([]);
  const [ownershipMap, setOwnershipMap] = useState({});

  const addingInFlightRef = useRef(new Set());
  const [addingCardIds, setAddingCardIds] = useState(() => new Set());
  const cartPersistTimerRef = useRef(null);

  const persistCart = (cards, selection) => {
    saveScannerCart({
      scannedCards: cards,
      selectedCardIds: Array.from(selection),
    });
  };

  useEffect(() => {
    clearTimeout(cartPersistTimerRef.current);
    cartPersistTimerRef.current = setTimeout(() => {
      persistCart(scannedCards, selectedCards);
    }, CART_PERSIST_DEBOUNCE_MS);

    return () => clearTimeout(cartPersistTimerRef.current);
  }, [scannedCards, selectedCards]);

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

    let cancelled = false;

    (async () => {
      try {
        const [loadedCollections, loadedDecks] = await Promise.all([
          fetchScannerCollections(),
          fetchScannerDecks(),
        ]);
        if (cancelled) return;
        setCollections(loadedCollections);
        setDecks(loadedDecks);
      } catch (error) {
        console.error('Error loading scanner destinations:', error);
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [user]);

  const syncAddingState = () => {
    setAddingCardIds(new Set(addingInFlightRef.current));
  };

  const tryBeginAdding = (cardId) => {
    if (addingInFlightRef.current.has(cardId)) return false;
    addingInFlightRef.current.add(cardId);
    syncAddingState();
    return true;
  };

  const endAdding = (cardId) => {
    addingInFlightRef.current.delete(cardId);
    syncAddingState();
  };

  // --- Undo stack: clear expired entries every 5s ---
  useEffect(() => {
    const interval = setInterval(() => {
      const cutoff = Date.now() - UNDO_TTL_MS;
      setUndoStack((prev) => {
        const next = prev.filter((entry) => entry.timestamp > cutoff);
        return next.length === prev.length ? prev : next;
      });
    }, UNDO_CLEANUP_INTERVAL_MS);
    return () => clearInterval(interval);
  }, []);

  // --- Batch ownership: debounced fetch for new card IDs ---
  const ownershipTimerRef = useRef(null);

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

    const dbIds = scannedCards
      .filter((c) => c.databaseId)
      .map((c) => c.databaseId);
    const newIds = dbIds.filter((id) => !(id in ownershipMap));

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

    clearTimeout(ownershipTimerRef.current);
    ownershipTimerRef.current = setTimeout(async () => {
      try {
        const result = await fetchBatchOwnership(newIds);
        setOwnershipMap((prev) => ({ ...prev, ...result }));
      } catch (err) {
        console.error('Batch ownership fetch failed:', err);
      }
    }, OWNERSHIP_DEBOUNCE_MS);

    return () => clearTimeout(ownershipTimerRef.current);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- ownershipMap in deps would cause infinite loop
  }, [scannedCards, user]);

  // --- Deck mode completion: derived state, no effect needed ---
  const deckComplete = Boolean(deckMode && scannedCards.length >= deckMode.targetSize);

  const markCardAsProcessed = (cardId, action) => {
    setScannedCards((prev) =>
      prev.map((card) =>
        card.id === cardId ? { ...card, processed: true, processedAction: action } : card
      )
    );
    setUndoStack((prev) => [...prev, { cardId, action, timestamp: Date.now() }]);
  };

  const undoCardProcess = useCallback((cardId) => {
    setScannedCards((prev) =>
      prev.map((card) =>
        card.id === cardId ? { ...card, processed: false, processedAction: null } : card
      )
    );
    setUndoStack((prev) => prev.filter((u) => u.cardId !== cardId));
  }, []);

  const handleCardScanned = (cardData) => {
    setAutoRouteError(null);

    const { cardEntry, scannedCards: nextQueue } = mergeScannedCardEntry(
      scannedCards,
      cardData,
      scanDefaults
    );
    setScannedCards(nextQueue);
    setSelectedCards((prev) => {
      const next = new Set([...prev, cardEntry.id]);
      persistCart(nextQueue, next);
      return next;
    });
  };

  const incrementCardQuantity = (cardId) => {
    setScannedCards((prev) =>
      prev.map((card) =>
        card.id === cardId ? { ...card, quantity: (card.quantity || 1) + 1 } : card
      )
    );
  };

  const decrementCardQuantity = (cardId) => {
    setScannedCards((prev) =>
      prev.map((card) =>
        card.id === cardId
          ? { ...card, quantity: Math.max(1, (card.quantity || 1) - 1) }
          : card
      )
    );
  };

  const updateCardMetadata = (cardId, patch) => {
    setScannedCards((prev) =>
      prev.map((card) => (card.id === cardId ? { ...card, ...patch } : card))
    );
  };

  const addSingleCardToOwned = async (card) => {
    if (!tryBeginAdding(card.id)) return false;
    try {
      await addScannedCardToOwned(card);
      markCardAsProcessed(card.id, 'owned');
      return true;
    } catch (error) {
      console.error('Error adding card to owned:', error);
      return false;
    } finally {
      endAdding(card.id);
    }
  };

  const addSingleCardToCollection = async (card, collectionId) => {
    if (!tryBeginAdding(card.id)) return false;
    try {
      await addScannedCardToCollection(card, collectionId);
      markCardAsProcessed(card.id, 'collection');
      return true;
    } catch (error) {
      console.error('Error adding card to collection:', error);
      return false;
    } finally {
      endAdding(card.id);
    }
  };

  const addSingleCardToDeck = async (card, deckId) => {
    if (!tryBeginAdding(card.id)) return;
    try {
      await addScannedCardToDeck(card, deckId);
      markCardAsProcessed(card.id, 'deck');
    } catch (error) {
      console.error('Error adding card to deck:', error);
    } finally {
      endAdding(card.id);
    }
  };

  const handleBulkAction = async (actionOverride, targetOverride) => {
    const action = actionOverride ?? bulkAction;
    const target = targetOverride ?? bulkTarget;
    if (!action || selectedCards.size === 0) return [];

    setIsProcessing(true);
    const successfulIds = [];
    try {
      const cardsToProcess = scannedCards.filter((card) => selectedCards.has(card.id));

      for (const card of cardsToProcess) {
        if (action === 'owned') {
          if (!tryBeginAdding(card.id)) continue;
          try {
            await addScannedCardToOwned(card);
            successfulIds.push(card.id);
          } catch (error) {
            console.error('Error adding card to owned:', error);
          } finally {
            endAdding(card.id);
          }
        } else if (action === 'collection' && target) {
          if (!tryBeginAdding(card.id)) continue;
          try {
            await addScannedCardToCollection(card, target);
            successfulIds.push(card.id);
          } catch (error) {
            console.error('Error adding card to collection:', error);
          } finally {
            endAdding(card.id);
          }
        } else if (action === 'deck' && target) {
          if (!tryBeginAdding(card.id)) continue;
          try {
            await addScannedCardToDeck(card, target);
            successfulIds.push(card.id);
          } catch (error) {
            console.error('Error adding card to deck:', error);
          } finally {
            endAdding(card.id);
          }
        }
      }

      if (successfulIds.length > 0) {
        setScannedCards((prev) => prev.filter((card) => !successfulIds.includes(card.id)));
        setSelectedCards((prev) => {
          const next = new Set(prev);
          successfulIds.forEach((id) => next.delete(id));
          return next;
        });
      }

      setBulkAction('');
      setBulkTarget('');
    } catch (error) {
      console.error('Error processing bulk action:', error);
    } finally {
      setIsProcessing(false);
    }

    return successfulIds;
  };

  const commitSelectedToOwned = async () => handleBulkAction('owned');
  const commitSelectedToCollection = async (collectionId) =>
    handleBulkAction('collection', collectionId);

  const unprocessedCount = scannedCards.filter((card) => !card.processed).length;

  const createCollection = async () => {
    if (!newCollectionName.trim()) return;

    try {
      const newCollection = await createScannerCollection(newCollectionName.trim());
      setCollections((prev) => [newCollection, ...prev]);
      setBulkTarget(newCollection.id.toString());
      setNewCollectionName('');
      setShowCreateCollection(false);
    } catch (error) {
      console.error('Error creating collection:', error);
    }
  };

  const clearScannedCards = () => {
    setScannedCards([]);
    setSelectedCards(new Set());
  };

  const removeScannedCard = (cardId) => {
    setScannedCards((prev) => prev.filter((card) => card.id !== cardId));
    setSelectedCards((prev) => {
      const next = new Set(prev);
      next.delete(cardId);
      return next;
    });
  };

  const toggleCardSelection = (cardId) => {
    setSelectedCards((prev) => {
      const next = new Set(prev);
      if (next.has(cardId)) {
        next.delete(cardId);
      } else {
        next.add(cardId);
      }
      return next;
    });
  };

  const clearSelection = () => {
    setSelectedCards(new Set());
  };

  return {
    scannedCards,
    collections,
    decks,
    selectedCards,
    isProcessing,
    addingCardIds,
    autoRouteError,
    showCreateCollection,
    setShowCreateCollection,
    newCollectionName,
    setNewCollectionName,
    handleCardScanned,
    incrementCardQuantity,
    decrementCardQuantity,
    updateCardMetadata,
    addSingleCardToOwned,
    addSingleCardToCollection,
    addSingleCardToDeck,
    handleBulkAction,
    commitSelectedToOwned,
    commitSelectedToCollection,
    unprocessedCount,
    createCollection,
    clearScannedCards,
    removeScannedCard,
    toggleCardSelection,
    clearSelection,
    undoCardProcess,
    undoStack,
    deckComplete,
    ownershipMap,
  };
}
