import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, expect, it, vi, beforeEach } from 'vitest';

// Real queries + real UI; only the service client and the host toast/error
// helpers are mocked so we exercise the mutate → invalidate → toast wiring.
const apiMocks = vi.hoisted(() => ({
    getQuarantine: vi.fn(),
    resolveQuarantine: vi.fn(),
}));

vi.mock('../src/platform/infrastructure/api/identitySync', () => ({
    identitySyncAPI: {
        getQuarantine: apiMocks.getQuarantine,
        resolveQuarantine: apiMocks.resolveQuarantine,
    },
}));

const toastMocks = vi.hoisted(() => ({
    success: vi.fn(),
    error: vi.fn(),
    info: vi.fn(),
    warning: vi.fn(),
}));

vi.mock('@nexuscrm/host-sdk', async (importOriginal) => {
    const actual = await importOriginal<typeof import('@nexuscrm/host-sdk')>();
    return {
        ...actual,
        useHostNotifications: () => toastMocks,
        formatApiError: (err: unknown) => ({ message: err instanceof Error ? err.message : 'error' }),
    };
});

import { QuarantineTab } from '../src/platform/features/identity-sync/components/QuarantineTab';

const PENDING_ITEM = {
    id: 'q-1',
    reason: 'ambiguous-match',
    email: 'jordan@example.com',
    userName: 'Jordan Vega',
    principal: 'jordan.vega',
    department: 'Sales',
    requestedProfile: 'Sales User',
    detail: 'Two active users share this email prefix.',
    status: 'pending',
    createdAt: '2026-07-15T10:00:00.000Z',
};

function makeClient(): QueryClient {
    return new QueryClient({ defaultOptions: { queries: { retry: false, retryDelay: 0 } } });
}

function renderTab(canManageUsers: boolean) {
    const client = makeClient();
    return render(
        <QueryClientProvider client={client}>
            <QuarantineTab canManageUsers={canManageUsers} />
        </QueryClientProvider>,
    );
}

describe('QuarantineTab resolve interaction', () => {
    beforeEach(() => {
        vi.clearAllMocks();
        apiMocks.getQuarantine.mockResolvedValue({ items: [PENDING_ITEM], total: 1 });
        apiMocks.resolveQuarantine.mockResolvedValue({ id: 'q-1', status: 'approved' });
    });

    it('opens the review modal and resolves an approval with a note, then invalidates and toasts', async () => {
        renderTab(true);

        await waitFor(() => expect(screen.getByText('jordan@example.com')).toBeInTheDocument());
        expect(apiMocks.getQuarantine).toHaveBeenCalledTimes(1);

        fireEvent.click(screen.getByRole('button', { name: 'Review' }));

        const note = await screen.findByLabelText('Resolution note');
        fireEvent.change(note, { target: { value: 'Verified with manager' } });

        fireEvent.click(screen.getByRole('button', { name: 'Approve' }));

        await waitFor(() => expect(apiMocks.resolveQuarantine).toHaveBeenCalledWith(
            'q-1',
            { approve: true, note: 'Verified with manager' },
        ));

        // Success toast names the affected identity.
        await waitFor(() => expect(toastMocks.success).toHaveBeenCalledWith(
            expect.stringContaining('jordan@example.com'),
        ));

        // onSuccess invalidates the root key → the mounted list refetches.
        await waitFor(() => expect(apiMocks.getQuarantine).toHaveBeenCalledTimes(2));

        // Modal closed on success.
        await waitFor(() => expect(screen.queryByLabelText('Resolution note')).not.toBeInTheDocument());
    });

    it('submits a rejection with the chosen approve=false flag', async () => {
        renderTab(true);
        await waitFor(() => expect(screen.getByText('jordan@example.com')).toBeInTheDocument());

        fireEvent.click(screen.getByRole('button', { name: 'Review' }));
        await screen.findByLabelText('Resolution note');
        fireEvent.click(screen.getByRole('button', { name: 'Reject' }));

        await waitFor(() => expect(apiMocks.resolveQuarantine).toHaveBeenCalledWith(
            'q-1',
            { approve: false, note: '' },
        ));
    });

    it('surfaces an error toast and keeps the modal open when the resolve fails', async () => {
        apiMocks.resolveQuarantine.mockRejectedValueOnce(new Error('breaker open'));
        renderTab(true);
        await waitFor(() => expect(screen.getByText('jordan@example.com')).toBeInTheDocument());

        fireEvent.click(screen.getByRole('button', { name: 'Review' }));
        await screen.findByLabelText('Resolution note');
        fireEvent.click(screen.getByRole('button', { name: 'Approve' }));

        await waitFor(() => expect(toastMocks.error).toHaveBeenCalledWith(
            expect.stringContaining('breaker open'),
        ));
        // Still open so the admin can retry.
        expect(screen.getByLabelText('Resolution note')).toBeInTheDocument();
    });

    it('disables approve and reject for users without Manage Users permission', async () => {
        renderTab(false);
        await waitFor(() => expect(screen.getByText('jordan@example.com')).toBeInTheDocument());

        fireEvent.click(screen.getByRole('button', { name: 'Review' }));
        await screen.findByLabelText('Resolution note');

        expect(screen.getByRole('button', { name: 'Approve' })).toBeDisabled();
        expect(screen.getByRole('button', { name: 'Reject' })).toBeDisabled();
        expect(screen.getByText(/need the Manage Users permission/i)).toBeInTheDocument();
    });

    it('shows the empty state when nothing is quarantined', async () => {
        apiMocks.getQuarantine.mockResolvedValue({ items: [], total: 0 });
        renderTab(true);

        await waitFor(() => expect(screen.getByText('Nothing in quarantine')).toBeInTheDocument());
    });
});
