import { fireEvent, render } from '@testing-library/react';
import { act } from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
    RightSidebarProvider,
    useRightSidebar,
} from '../../src/contexts/RightSidebarContext';

type RightSidebarHarnessOptions = {
    pathname?: string;
    onMobileOpenRequest?: () => void;
};

function setViewportWidth(width: number) {
    Object.defineProperty(window, 'innerWidth', {
        configurable: true,
        writable: true,
        value: width,
    });
    Object.defineProperty(window, 'matchMedia', {
        configurable: true,
        writable: true,
        value: undefined,
    });
}

function renderRightSidebarHarness(options: RightSidebarHarnessOptions = {}) {
    const { pathname = '/users', onMobileOpenRequest } = options;
    let currentPathname = pathname;
    let currentValue: ReturnType<typeof useRightSidebar> | null = null;

    function Probe() {
        currentValue = useRightSidebar();
        return null;
    }

    function Wrapper({ pathname }: Readonly<{ pathname: string }>) {
        return (
            <RightSidebarProvider
                closeOnPathname={pathname}
                onMobileOpenRequest={onMobileOpenRequest}
            >
                <Probe />
            </RightSidebarProvider>
        );
    }

    const rendered = render(<Wrapper pathname={currentPathname} />);

    return {
        getCurrent() {
            if (!currentValue) {
                throw new Error('Right sidebar context value not initialized');
            }
            return currentValue;
        },
        reroute(nextPathname: string) {
            currentPathname = nextPathname;
            rendered.rerender(<Wrapper pathname={currentPathname} />);
        },
        unmount: rendered.unmount,
    };
}

describe('RightSidebarContext', () => {
    beforeEach(() => {
        localStorage.removeItem('authRightSidebarWidth');
        setViewportWidth(1024);
    });

    it('throws when useRightSidebar is used outside provider', () => {
        function Invalid() {
            useRightSidebar();
            return null;
        }

        expect(() => render(<Invalid />)).toThrow('useRightSidebar must be used within RightSidebarProvider');
    });

    it('opens and closes with content', () => {
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        act(() => {
            harness.getCurrent().openSidebar({
                title: 'Meta',
                content: <div>Body</div>,
            });
        });

        expect(harness.getCurrent().isOpen).toBe(true);
        expect(harness.getCurrent().content?.title).toBe('Meta');

        act(() => {
            harness.getCurrent().closeSidebar();
        });

        expect(harness.getCurrent().isOpen).toBe(false);
        expect(harness.getCurrent().content).toBeNull();
    });

    it('does not open without current or next content', () => {
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        act(() => {
            harness.getCurrent().openSidebar();
        });

        expect(harness.getCurrent().isOpen).toBe(false);
        expect(harness.getCurrent().content).toBeNull();
    });

    it('updates content live and supports toggle semantics', () => {
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        act(() => {
            harness.getCurrent().openSidebar({
                title: 'Meta',
                content: <div>Body</div>,
            });
        });

        act(() => {
            harness.getCurrent().setSidebarContent({
                title: 'Meta Updated',
                content: <div>Updated</div>,
            });
        });

        expect(harness.getCurrent().isOpen).toBe(true);
        expect(harness.getCurrent().content?.title).toBe('Meta Updated');

        act(() => {
            harness.getCurrent().toggleSidebar();
        });
        expect(harness.getCurrent().isOpen).toBe(false);
        expect(harness.getCurrent().content).toBeNull();
    });

    it('toggleSidebar opens with provided content when closed', () => {
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        act(() => {
            harness.getCurrent().toggleSidebar({
                title: 'Toggle open',
                content: <div>Toggle body</div>,
            });
        });

        expect(harness.getCurrent().isOpen).toBe(true);
        expect(harness.getCurrent().content?.title).toBe('Toggle open');
    });

    it('closes on pathname changes', () => {
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        act(() => {
            harness.getCurrent().openSidebar({
                title: 'Meta',
                content: <div>Body</div>,
            });
        });
        expect(harness.getCurrent().isOpen).toBe(true);

        harness.reroute('/posts');
        expect(harness.getCurrent().isOpen).toBe(false);
        expect(harness.getCurrent().content).toBeNull();
    });

    it('reads/clamps persisted width, persists resized widths, and reacts to escape', async () => {
        localStorage.setItem('authRightSidebarWidth', '999');
        const harness = renderRightSidebarHarness({ pathname: '/users' });

        expect(harness.getCurrent().desktopSidebarStyle['--auth-right-sidebar-width']).toBe(
            '480px',
        );

        act(() => {
            harness.getCurrent().openSidebar({
                title: 'Meta',
                content: <div>Body</div>,
            });
        });

        const preventDefault = vi.fn();
        act(() => {
            harness.getCurrent().startResize({
                clientX: 480,
                preventDefault,
            } as never);
        });

        act(() => {
            fireEvent.pointerMove(window, { clientX: -400 });
            fireEvent.pointerUp(window);
        });

        await act(async () => {
            await Promise.resolve();
        });

        expect(preventDefault).toHaveBeenCalledTimes(1);
        expect(localStorage.getItem('authRightSidebarWidth')).toBe('480');

        act(() => {
            fireEvent.keyDown(window, { key: 'Escape' });
        });
        expect(harness.getCurrent().isOpen).toBe(false);
    });

    it('notifies mobile open requests when opening on mobile', () => {
        setViewportWidth(768);
        const onMobileOpenRequest = vi.fn();
        const harness = renderRightSidebarHarness({
            pathname: '/users',
            onMobileOpenRequest,
        });

        act(() => {
            harness.getCurrent().openSidebar({
                title: 'Meta',
                content: <div>Body</div>,
            });
        });

        expect(harness.getCurrent().isOpen).toBe(true);
        expect(onMobileOpenRequest).toHaveBeenCalledTimes(1);
    });

    it('normalizes custom sizing values', () => {
        function Probe() {
            const sidebar = useRightSidebar();
            return <div data-testid="width">{sidebar.desktopSidebarStyle['--auth-right-sidebar-width']}</div>;
        }

        const { getByTestId } = render(
            <RightSidebarProvider
                sizing={{
                    defaultWidth: 1000,
                    minWidth: 500,
                    maxWidth: 300,
                }}
            >
                <Probe />
            </RightSidebarProvider>,
        );

        expect(getByTestId('width').textContent).toBe('500px');
    });
});
