import { describe, it, expect, vi, afterEach } from 'vitest';
import { render, screen, cleanup, fireEvent } from '@testing-library/svelte';

const setReaction = vi.fn();
const clearReaction = vi.fn();
vi.mock('$lib/api/reactions', () => ({
    setReaction: (...a: unknown[]) => setReaction(...a),
    clearReaction: (...a: unknown[]) => clearReaction(...a)
}));

import ReactionButtons from './ReactionButtons.svelte';

afterEach(() => {
    cleanup();
    setReaction.mockReset();
    clearReaction.mockReset();
});

const pressed = (testid: string) =>
    screen.getByTestId(testid).getAttribute('aria-pressed');

describe('ReactionButtons', () => {
    it('reflects the initial reaction', () => {
        render(ReactionButtons, { props: { mangaId: 'm1', initial: 'like' } });
        expect(pressed('reaction-like')).toBe('true');
        expect(pressed('reaction-dislike')).toBe('false');
    });

    it('likes when nothing is set', async () => {
        setReaction.mockResolvedValue({ manga_id: 'm1', reaction: 'like' });
        render(ReactionButtons, { props: { mangaId: 'm1', initial: null } });
        await fireEvent.click(screen.getByTestId('reaction-like'));
        expect(setReaction).toHaveBeenCalledWith('m1', 'like');
        expect(pressed('reaction-like')).toBe('true');
    });

    it('clears when clicking the active reaction again', async () => {
        clearReaction.mockResolvedValue(undefined);
        render(ReactionButtons, { props: { mangaId: 'm1', initial: 'like' } });
        await fireEvent.click(screen.getByTestId('reaction-like'));
        expect(clearReaction).toHaveBeenCalledWith('m1');
        expect(pressed('reaction-like')).toBe('false');
    });

    it('switches from like to dislike', async () => {
        setReaction.mockResolvedValue({ manga_id: 'm1', reaction: 'dislike' });
        render(ReactionButtons, { props: { mangaId: 'm1', initial: 'like' } });
        await fireEvent.click(screen.getByTestId('reaction-dislike'));
        expect(setReaction).toHaveBeenCalledWith('m1', 'dislike');
        expect(pressed('reaction-dislike')).toBe('true');
        expect(pressed('reaction-like')).toBe('false');
    });

    it('resyncs when navigated to a different manga', async () => {
        // The detail page component is reused across /manga/A -> /manga/B
        // navigations (e.g. clicking a similar or recommendation card), so
        // new props must overwrite the locally-held reaction state.
        const { rerender } = render(ReactionButtons, {
            props: { mangaId: 'm1', initial: 'like' }
        });
        expect(pressed('reaction-like')).toBe('true');

        await rerender({ mangaId: 'm2', initial: 'dislike' });
        expect(pressed('reaction-dislike')).toBe('true');
        expect(pressed('reaction-like')).toBe('false');

        await rerender({ mangaId: 'm3', initial: null });
        expect(pressed('reaction-like')).toBe('false');
        expect(pressed('reaction-dislike')).toBe('false');
    });

    it('rolls back on failure', async () => {
        setReaction.mockRejectedValue(new Error('boom'));
        render(ReactionButtons, { props: { mangaId: 'm1', initial: null } });
        await fireEvent.click(screen.getByTestId('reaction-like'));
        // Optimistic update reverted after the rejection.
        await Promise.resolve();
        expect(pressed('reaction-like')).toBe('false');
    });
});
