// @vitest-environment jsdom
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act, waitFor } from '@testing-library/react';

const routeApiMocks = vi.hoisted(() => ({
  addScannedCardToOwned: vi.fn().mockResolvedValue(undefined),
  addScannedCardToCollection: vi.fn().mockResolvedValue(undefined),
  addScannedCardToDeck: vi.fn().mockResolvedValue(undefined),
  routeScannedCardToDestination: vi.fn().mockResolvedValue(undefined),
  fetchScannerCollections: vi.fn().mockResolvedValue([]),
  fetchScannerDecks: vi.fn().mockResolvedValue([]),
  fetchBatchOwnership: vi.fn().mockResolvedValue({}),
  createScannerCollection: vi.fn(),
}));

vi.mock('../../lib/scanner-route-api.js', () => ({
  addScannedCardToOwned: routeApiMocks.addScannedCardToOwned,
  addScannedCardToCollection: routeApiMocks.addScannedCardToCollection,
  addScannedCardToDeck: routeApiMocks.addScannedCardToDeck,
  routeScannedCardToDestination: routeApiMocks.routeScannedCardToDestination,
  fetchScannerCollections: routeApiMocks.fetchScannerCollections,
  fetchScannerDecks: routeApiMocks.fetchScannerDecks,
  fetchBatchOwnership: routeApiMocks.fetchBatchOwnership,
  createScannerCollection: routeApiMocks.createScannerCollection,
}));

import { useScannerQueue } from '../../lib/use-scanner-queue.js';
import { clearScannerCartStorage } from '../../lib/scanner-session.js';

const {
  addScannedCardToOwned,
  routeScannedCardToDestination,
} = routeApiMocks;

const scanDefaults = { condition: 'NM', isFoil: false };
const user = { userId: 1, email: 'test@example.com', role: 'user' };

describe('useScannerQueue — cart model', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    sessionStorage.clear();
  });

  afterEach(() => {
    clearScannerCartStorage();
  });

  it('handleCardScanned enqueues locally without route helpers', async () => {
    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: { type: 'owned', id: null, label: 'My Collection' },
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Pikachu',
        set: 'Base',
        databaseId: 101,
      });
    });

    expect(routeScannedCardToDestination).not.toHaveBeenCalled();
    expect(addScannedCardToOwned).not.toHaveBeenCalled();
    expect(result.current.scannedCards).toHaveLength(1);
    const card = result.current.scannedCards[0];
    expect(card).toMatchObject({
      name: 'Pikachu',
      set: 'Base',
      processed: false,
    });
    expect(card.id).toBeTruthy();
    expect(result.current.selectedCards.has(card.id)).toBe(true);
  });

  it('handleBulkAction owned commits each selected card and removes them from the cart', async () => {
    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: null,
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Bolt',
        set: 'Alpha',
        databaseId: 201,
      });
    });

    const cardId = result.current.scannedCards[0].id;

    await act(async () => {
      await result.current.handleBulkAction('owned');
    });

    expect(addScannedCardToOwned).toHaveBeenCalledTimes(1);
    expect(addScannedCardToOwned).toHaveBeenCalledWith(
      expect.objectContaining({ id: cardId, name: 'Bolt', set: 'Alpha' })
    );
    expect(result.current.scannedCards).toHaveLength(0);
    expect(result.current.selectedCards.size).toBe(0);
  });

  it('commitSelectedToOwned returns successful ids instead of reading stale queue state', async () => {
    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: null,
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Jace',
        set: 'Origins',
        databaseId: 301,
      });
    });

    const cardId = result.current.scannedCards[0].id;
    let successfulIds;
    await act(async () => {
      successfulIds = await result.current.commitSelectedToOwned();
    });

    expect(successfulIds).toEqual([cardId]);
    expect(addScannedCardToOwned).toHaveBeenCalledTimes(1);
    expect(result.current.scannedCards).toHaveLength(0);
  });

  it('commitSelectedToOwned returns an empty array when the API fails', async () => {
    addScannedCardToOwned.mockRejectedValueOnce(new Error('network'));

    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: null,
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Jace',
        set: 'Origins',
        databaseId: 301,
      });
    });

    let successfulIds;
    await act(async () => {
      successfulIds = await result.current.commitSelectedToOwned();
    });

    expect(successfulIds).toEqual([]);
    expect(result.current.scannedCards).toHaveLength(1);
  });

  it('commitSelectedToOwned routes through handleBulkAction owned', async () => {
    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: null,
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Jace',
        set: 'Origins',
        databaseId: 301,
      });
    });

    await act(async () => {
      await result.current.commitSelectedToOwned();
    });

    expect(addScannedCardToOwned).toHaveBeenCalledTimes(1);
    expect(result.current.scannedCards).toHaveLength(0);
  });

  it('persists cart state to sessionStorage on scan', async () => {
    const { result } = renderHook(() =>
      useScannerQueue({
        user,
        sessionDestination: null,
        scanDefaults,
        deckMode: null,
      })
    );

    await act(async () => {
      result.current.handleCardScanned({
        name: 'Liliana',
        set: 'Midnight',
        databaseId: 401,
      });
    });

    const cardId = result.current.scannedCards[0].id;

    await waitFor(() => {
      const raw = sessionStorage.getItem('deckhearth:scanner-cart');
      expect(raw).toBeTruthy();
      const parsed = JSON.parse(raw);
      expect(parsed.scannedCards).toHaveLength(1);
      expect(parsed.selectedCardIds).toEqual([cardId]);
    });

    expect(result.current.unprocessedCount).toBe(1);
  });
});
