import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock sonner BEFORE importing toast-config
const mockToastError = vi.fn();
const mockToastSuccess = vi.fn();
const mockToastWarning = vi.fn();
const mockToastInfo = vi.fn();
vi.mock('sonner', () => ({
    toast: {
        error: (...args: unknown[]) => mockToastError(...args),
        success: (...args: unknown[]) => mockToastSuccess(...args),
        warning: (...args: unknown[]) => mockToastWarning(...args),
        info: (...args: unknown[]) => mockToastInfo(...args),
    },
}));

// Mock router — router.visit is fire-and-forget
const mockRouterVisit = vi.fn();
vi.mock('@inertiajs/react', () => ({
    router: { visit: (...args: unknown[]) => mockRouterVisit(...args) },
}));

describe('fireToast', () => {
    beforeEach(() => {
        mockToastError.mockClear();
        mockToastSuccess.mockClear();
        mockToastWarning.mockClear();
        mockToastInfo.mockClear();
        mockRouterVisit.mockClear();
    });

    // ── additive contract (no regression for callers without action) ─────────

    it('calls toast.error with only message and duration when no action is provided', async () => {
        const { fireToast } = await import('@/lib/toast-config');
        fireToast('error', 'Something failed');
        expect(mockToastError).toHaveBeenCalledOnce();
        const [msg, opts] = mockToastError.mock.calls[0] as [string, Record<string, unknown>];
        expect(msg).toBe('Something failed');
        expect(opts).not.toHaveProperty('action');
        expect(opts).toHaveProperty('duration', 7000);
    });

    it('calls toast.success with no action when success severity is used (no regression)', async () => {
        const { fireToast } = await import('@/lib/toast-config');
        fireToast('success', 'All good!');
        expect(mockToastSuccess).toHaveBeenCalledOnce();
        const [, opts] = mockToastSuccess.mock.calls[0] as [string, Record<string, unknown>];
        expect(opts).not.toHaveProperty('action');
    });

    // ── action rendering ────────────────────────────────────────────────────

    it('passes sonner action option when action is provided on error toast', async () => {
        const { fireToast } = await import('@/lib/toast-config');
        fireToast('error', 'Key invalid', { label: 'Open AI settings', href: '/settings/ai' });
        expect(mockToastError).toHaveBeenCalledOnce();
        const [msg, opts] = mockToastError.mock.calls[0] as [
            string,
            { duration: number; action: { label: string; onClick: () => void } },
        ];
        expect(msg).toBe('Key invalid');
        expect(opts.action).toBeDefined();
        expect(opts.action.label).toBe('Open AI settings');
    });

    it('the action onClick calls router.visit with the provided href', async () => {
        const { fireToast } = await import('@/lib/toast-config');
        fireToast('error', 'Key invalid', { label: 'Open AI settings', href: '/settings/ai' });
        const [, opts] = mockToastError.mock.calls[0] as [
            string,
            { action: { onClick: () => void } },
        ];
        opts.action.onClick();
        expect(mockRouterVisit).toHaveBeenCalledWith('/settings/ai');
    });

    it('does not await router.visit (fire-and-forget — gotcha #11)', async () => {
        // router.visit returns void-like; no Promise. The onClick should not fail
        // if router.visit returns undefined.
        const { fireToast } = await import('@/lib/toast-config');
        mockRouterVisit.mockReturnValue(undefined);
        fireToast('error', 'Key invalid', { label: 'Open AI settings', href: '/settings/ai' });
        const [, opts] = mockToastError.mock.calls[0] as [
            string,
            { action: { onClick: () => void } },
        ];
        // Must not throw
        expect(() => opts.action.onClick()).not.toThrow();
    });
});
