import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { NavigationButtons } from '@/pages/fichas-inventario/components/navigation-buttons';
import { useInventarioStore } from '@/pages/fichas-inventario/stores/use-inventario-store';

const { visitMock, editUrlMock } = vi.hoisted(() => ({
    visitMock: vi.fn(),
    editUrlMock: vi.fn(),
}));

vi.mock('@inertiajs/react', () => ({
    router: {
        visit: visitMock,
    },
}));

vi.mock('@/routes/fichas-inventario/index', () => ({
    edit: {
        url: editUrlMock,
    },
}));

describe('NavigationButtons', () => {
    beforeEach(() => {
        visitMock.mockReset();
        editUrlMock.mockReset();
        editUrlMock.mockReturnValue('/fichas-inventario/1/1');
        useInventarioStore.setState({
            saveSectionHandler: null,
            currentSection: '0',
            stepStates: [{ section: '0', status: 'complete' }],
            isSectionDirty: false,
        });
    });

    it('renders three buttons: Anterior, Guardar Sección, Siguiente', () => {
        render(<NavigationButtons currentSection="0" inventarioId={1} />,
        );

        expect(
            screen.getByRole('button', { name: /anterior/i }),
        ).toBeInTheDocument();
        expect(
            screen.getByRole('button', { name: /guardar sección/i }),
        ).toBeInTheDocument();
        expect(
            screen.getByRole('button', { name: /siguiente/i }),
        ).toBeInTheDocument();
    });

    it('disables Guardar Sección when no changes', () => {
        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        const saveButton = screen.getByRole('button', {
            name: /guardar sección/i,
        });
        expect(saveButton).toBeDisabled();
    });

    it('enables Guardar Sección when there are changes', () => {
        useInventarioStore.setState({ isSectionDirty: true });

        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        const saveButton = screen.getByRole('button', {
            name: /guardar sección/i,
        });
        expect(saveButton).not.toBeDisabled();
    });

    it('calls save handler when Guardar Sección is clicked', async () => {
        const user = userEvent.setup();
        const saveHandler = vi.fn().mockResolvedValue(undefined);

        useInventarioStore.setState({
            saveSectionHandler: saveHandler,
            isSectionDirty: true,
        });

        render(<NavigationButtons currentSection="0" inventarioId={15} />);

        await user.click(
            screen.getByRole('button', { name: /guardar sección/i }),
        );

        expect(saveHandler).toHaveBeenCalledTimes(1);
        expect(visitMock).not.toHaveBeenCalled();
    });

    it('shows saving state while saving', async () => {
        const user = userEvent.setup();
        let resolveSave: () => void;
        const saveHandler = vi.fn().mockImplementation(
            () =>
                new Promise<void>((resolve) => {
                    resolveSave = resolve;
                }),
        );

        useInventarioStore.setState({
            saveSectionHandler: saveHandler,
            isSectionDirty: true,
        });

        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        const saveButton = screen.getByRole('button', {
            name: /guardar sección/i,
        });
        await user.click(saveButton);

        expect(saveButton).toHaveTextContent(/guardando/i);
        expect(saveButton).toBeDisabled();

        resolveSave!();
    });

    it('navigates directly when Siguiente clicked with no changes', async () => {
        const user = userEvent.setup();

        render(<NavigationButtons currentSection="0" inventarioId={15} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        expect(editUrlMock).toHaveBeenCalledWith({ inventario: 15, n: '1' });
        expect(visitMock).toHaveBeenCalledWith('/fichas-inventario/1/1');
    });

    it('shows confirmation dialog when navigating with unsaved changes', async () => {
        const user = userEvent.setup();

        useInventarioStore.setState({ isSectionDirty: true });

        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        expect(
            screen.getByRole('heading', { name: /cambios sin guardar/i }),
        ).toBeInTheDocument();
        expect(visitMock).not.toHaveBeenCalled();
    });

    it('saves and continues from confirmation dialog', async () => {
        const user = userEvent.setup();
        const saveHandler = vi.fn().mockResolvedValue(undefined);

        useInventarioStore.setState({
            saveSectionHandler: saveHandler,
            isSectionDirty: true,
        });

        render(<NavigationButtons currentSection="0" inventarioId={15} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        await user.click(
            screen.getByRole('button', { name: /guardar y continuar/i }),
        );

        expect(saveHandler).toHaveBeenCalledTimes(1);
        expect(editUrlMock).toHaveBeenCalledWith({ inventario: 15, n: '1' });
        expect(visitMock).toHaveBeenCalledWith('/fichas-inventario/1/1');
    });

    it('continues without saving from confirmation dialog', async () => {
        const user = userEvent.setup();

        useInventarioStore.setState({ isSectionDirty: true });

        render(<NavigationButtons currentSection="0" inventarioId={15} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        await user.click(
            screen.getByRole('button', {
                name: /continuar sin guardar/i,
            }),
        );

        expect(editUrlMock).toHaveBeenCalledWith({ inventario: 15, n: '1' });
        expect(visitMock).toHaveBeenCalledWith('/fichas-inventario/1/1');
    });

    it('cancels navigation from confirmation dialog', async () => {
        const user = userEvent.setup();

        useInventarioStore.setState({ isSectionDirty: true });

        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        await user.click(screen.getByRole('button', { name: /cancelar/i }));

        expect(visitMock).not.toHaveBeenCalled();
    });

    it('does not navigate when saving fails and keeps dialog open', async () => {
        const user = userEvent.setup();
        const saveHandler = vi.fn().mockRejectedValue({
            denominacion: 'La denominacion es obligatoria.',
        });

        useInventarioStore.setState({
            saveSectionHandler: saveHandler,
            isSectionDirty: true,
        });

        render(<NavigationButtons currentSection="0" inventarioId={15} />);

        await user.click(
            screen.getByRole('button', { name: /siguiente/i }),
        );

        await user.click(
            screen.getByRole('button', { name: /guardar y continuar/i }),
        );

        expect(saveHandler).toHaveBeenCalledTimes(1);
        expect(visitMock).not.toHaveBeenCalled();

        // Dialog should remain open so user can retry or choose another option
        expect(
            screen.getByRole('heading', { name: /cambios sin guardar/i }),
        ).toBeInTheDocument();
    });

    it('shows confirmation dialog for Anterior button with unsaved changes', async () => {
        const user = userEvent.setup();

        useInventarioStore.setState({ isSectionDirty: true });

        render(<NavigationButtons currentSection="1" inventarioId={1} />);

        await user.click(
            screen.getByRole('button', { name: /anterior/i }),
        );

        expect(
            screen.getByRole('heading', { name: /cambios sin guardar/i }),
        ).toBeInTheDocument();
    });

    it('disables Anterior button on first section', () => {
        render(<NavigationButtons currentSection="0" inventarioId={1} />);

        expect(screen.getByRole('button', { name: /anterior/i })).toBeDisabled();
    });

    it('disables Siguiente button on resumen section', () => {
        render(
            <NavigationButtons currentSection="resumen" inventarioId={1} />,
        );

        expect(
            screen.getByRole('button', { name: /siguiente/i }),
        ).toBeDisabled();
    });

    it('hides Guardar Sección button on resumen section', () => {
        render(
            <NavigationButtons currentSection="resumen" inventarioId={1} />,
        );

        expect(
            screen.queryByRole('button', { name: /guardar sección/i }),
        ).not.toBeInTheDocument();
    });
});
