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

import { useFlashToasts } from './useFlashToasts';

// Mock the Inertia usePage hook
const mockFlash = vi.fn(() => ({}));
vi.mock('@inertiajs/react', () => ({
  usePage: () => ({ props: { flash: mockFlash() } }),
  router: {
    on: vi.fn(() => vi.fn()),
  },
}));

// Mock fireToast
const mockFireToast = vi.fn();
vi.mock('@/lib/toast-config', () => ({
  fireToast: (...args: unknown[]) => mockFireToast(...args),
}));

describe('useFlashToasts', () => {
  beforeEach(() => {
    mockFireToast.mockClear();
    mockFlash.mockReset();
  });

  it('fires a success toast when flash.success is present', () => {
    mockFlash.mockReturnValue({ success: 'Saved!' });
    renderHook(() => useFlashToasts());
    expect(mockFireToast).toHaveBeenCalledWith('success', 'Saved!');
  });

  it('fires an error toast when flash.error is present', () => {
    mockFlash.mockReturnValue({ error: 'Something went wrong' });
    renderHook(() => useFlashToasts());
    // no recovery_action → fireToast called with exactly 2 args (additive contract)
    expect(mockFireToast).toHaveBeenCalledWith('error', 'Something went wrong');
  });

  it('does not fire a toast when flash is empty', () => {
    mockFlash.mockReturnValue({});
    renderHook(() => useFlashToasts());
    expect(mockFireToast).not.toHaveBeenCalled();
  });

  it('dedupes identical flash messages on re-render (partial reload suppression)', () => {
    mockFlash.mockReturnValue({ error: 'Connection failed' });
    const { rerender } = renderHook(() => useFlashToasts());

    expect(mockFireToast).toHaveBeenCalledTimes(1);

    // Same flash — simulates a partial reload re-delivering the same flash
    rerender();
    expect(mockFireToast).toHaveBeenCalledTimes(1); // no second toast
  });

  // FE-011: a second IDENTICAL flash from a NEW full navigation must still fire
  it('FE-011: fires a second toast for the same message after flash_id changes', () => {
    // Simulate first navigation: error X with flash_id A
    mockFlash.mockReturnValue({ error: 'Connection failed', flash_id: 'uuid-aaa' });
    const { rerender } = renderHook(() => useFlashToasts());
    expect(mockFireToast).toHaveBeenCalledTimes(1);

    // Simulate second navigation: same error X, but new flash_id B (new action)
    mockFlash.mockReturnValue({ error: 'Connection failed', flash_id: 'uuid-bbb' });
    rerender();
    expect(mockFireToast).toHaveBeenCalledTimes(2);
  });

  // R6FE-009: content-fallback dedupe must not swallow a legitimately-repeated
  // identical flash when flash_id is absent and a new response object arrives.
  it('R6FE-009: fires a second toast for the same message when flash_id is absent and a new response object arrives', () => {
    // First navigation: error with no flash_id (object reference A)
    mockFlash.mockReturnValue({ error: 'Connection failed' });
    const { rerender } = renderHook(() => useFlashToasts());
    expect(mockFireToast).toHaveBeenCalledTimes(1);

    // Same content re-render (partial reload) — must NOT fire again
    rerender();
    expect(mockFireToast).toHaveBeenCalledTimes(1);

    // Second full navigation: new object reference, same content, no flash_id — MUST fire
    mockFlash.mockReturnValue({ error: 'Connection failed' });
    rerender();
    expect(mockFireToast).toHaveBeenCalledTimes(2);
  });

  // AI-KEY-RECOVERY-CTA: flash.recovery_action is passed through to fireToast
  it('passes recovery_action to fireToast when present alongside error', () => {
    const recoveryAction = { label: 'Open AI settings', href: '/settings/ai' };
    mockFlash.mockReturnValue({
      error: 'Your AI provider API key is no longer valid. Re-check it in Settings > AI.',
      recovery_action: recoveryAction,
      flash_id: 'uuid-recovery',
    });
    renderHook(() => useFlashToasts());
    expect(mockFireToast).toHaveBeenCalledWith(
      'error',
      'Your AI provider API key is no longer valid. Re-check it in Settings > AI.',
      recoveryAction,
    );
  });

  it('calls fireToast with no third arg when recovery_action is absent (additive contract)', () => {
    mockFlash.mockReturnValue({ error: 'Generic error', flash_id: 'uuid-generic' });
    renderHook(() => useFlashToasts());
    // recovery_action is absent → fireToast receives exactly 2 args, keeping
    // call-signature parity for all existing callers (e.g. Notifications.test.tsx)
    expect(mockFireToast).toHaveBeenCalledWith('error', 'Generic error');
  });
});
