import React, { ComponentProps } from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { CoverArt, CoverArtShape } from '../coverArt';

describe('<CoverArt>', () => {
    const url = 'TESTURL';
    const defaultProps = { url };

    const renderComponent = (props?: Partial<ComponentProps<typeof CoverArt>>) =>
        render(<CoverArt {...defaultProps} {...props} />);

    testComponent(CoverArt);

    test('applies style merged with computed width', () => {
        const { getByTestId } = renderComponent({ style: { marginTop: 10 } });
        const root = getByTestId('CoverArt');
        expect(root).toHaveStyle({ marginTop: '10px', width: '80px' });
    });

    test('renders default component', () => {
        const { getByAltText, container } = renderComponent();

        expect(container.querySelector('.CoverArt-image')).toBeVisible();
        expect(container.querySelector('.CoverArt')).toBeVisible();
        expect(getByAltText('cover art')).toBeVisible();
    });

    test('renders empty component', () => {
        const { queryByAltText, container } = renderComponent({ url: null });

        expect(container.querySelector('.CoverArt-empty')).toBeVisible();
        expect(queryByAltText('cover art')).toBeNull();
    });

    test('renders with unknown className', () => {
        const { queryByAltText, container } = renderComponent({ unknown: true, url: null });
        expect(container.querySelector('.CoverArt-unknown')).toBeVisible();
        expect(queryByAltText('cover')).toBeNull();
    });

    test('sets correct shape classnames', () => {
        const shapes: CoverArtShape[] = ['round', 'square', 'letterbox', 'landscape'];
        shapes.forEach((shape) => {
            const { container } = renderComponent({ shape });
            expect(container.querySelector(`.CoverArt.${shape}`)).toBeVisible();
        });
    });

    describe('lazy loading', () => {
        test('is not enabled by default', () => {
            const { queryByAltText } = renderComponent();
            expect(queryByAltText('cover art')).not.toHaveAttribute('loading');
        });

        test('sets lazy loading param', () => {
            const { queryByAltText } = renderComponent({ lazy: true });
            expect(queryByAltText('cover art')).toHaveAttribute('loading', 'lazy');
        });
    });

    test('accepts custom alt text', () => {
        const { getByAltText } = renderComponent({ alt: 'test-alt' });
        expect(getByAltText('test-alt')).toBeVisible();
    });

    test('renders image when url is provided after initial render', async () => {
        const { container, rerender } = render(<CoverArt url={null} />);

        expect(container.querySelector('.CoverArt-empty')).toBeVisible();
        expect(container.querySelector('img')).toBeNull();

        rerender(<CoverArt url="TESTURL" />);

        await waitFor(() => {
            expect(container.querySelector('.CoverArt-image')).toBeVisible();
        });

        const img = container.querySelector('img');
        expect(img).toHaveAttribute('src', 'TESTURL');
    });

    describe('loading state', () => {
        test('does not render SkeletonLoader by default', () => {
            const r = renderComponent();

            expect(r.queryByTestId('SkeletonLoader')).toBeNull();
        });

        test('does not render SkeletonLoader when loading is false', () => {
            const r = renderComponent({ loading: false });

            expect(r.queryByTestId('SkeletonLoader')).toBeNull();
            expect(r.getByAltText('cover art')).toBeVisible();
        });

        test('renders SkeletonLoader when loading is true', () => {
            const r = renderComponent({ loading: true });

            const loader = r.getByTestId('SkeletonLoader');

            expect(loader).toBeVisible();
            expect(loader.querySelector('.SkeletonLoader-square')).toBeVisible();
            expect(r.getByAltText('cover art')).toBeInTheDocument();
        });

        test.each([
            { shape: 'square' as CoverArtShape, expectedShape: 'square' },
            { shape: 'round' as CoverArtShape, expectedShape: 'circle' },
            { shape: 'landscape' as CoverArtShape, expectedShape: 'square' },
            { shape: 'letterbox' as CoverArtShape, expectedShape: 'square' },
        ])(
            'renders SkeletonLoader with $shape shape as $expectedShape',
            ({ shape, expectedShape }) => {
                const r = renderComponent({ loading: true, shape });

                const loader = r.getByTestId('SkeletonLoader');

                expect(loader).toBeVisible();
                expect(loader.querySelector(`.SkeletonLoader-${expectedShape}`)).toBeVisible();
            }
        );

        test('renders image with overlay skeleton when loading is true and url is provided', () => {
            const r = renderComponent({ loading: true, url: 'test-url' });

            const img = r.container.querySelector('img');

            expect(img).toBeInTheDocument();
            expect(img).toHaveAttribute('src', 'test-url');
            expect(r.getByTestId('SkeletonLoader')).toBeVisible();
        });
    });

    describe('image error handling', () => {
        test('shows placeholder when image fails to load', async () => {
            const { container } = renderComponent({ url: 'invalid-url' });

            // Initially shows img element
            const img = container.querySelector('img');
            expect(img).toBeInTheDocument();
            expect(container.querySelector('.CoverArt-empty')).toBeNull();

            // Simulate image load error
            fireEvent.error(img!);

            // Should show placeholder after error
            await waitFor(() => {
                expect(container.querySelector('.CoverArt-empty')).toBeVisible();
            });
        });

        test('calls onError callback when image fails to load', async () => {
            const onError = vi.fn();
            const { container } = renderComponent({ url: 'invalid-url', onError });

            fireEvent.error(container.querySelector('img')!);

            await waitFor(() => {
                expect(container.querySelector('.CoverArt-empty')).toBeVisible();
            });

            expect(onError).toHaveBeenCalledTimes(1);
        });

        test('still shows placeholder when onError is provided and image fails', async () => {
            const { container } = renderComponent({ url: 'invalid-url', onError: vi.fn() });

            fireEvent.error(container.querySelector('img')!);

            await waitFor(() => {
                expect(container.querySelector('.CoverArt-empty')).toBeVisible();
            });
        });

        test('calls onLoad callback when image fails to load', async () => {
            const onLoad = vi.fn();
            const { container } = renderComponent({ url: 'invalid-url', onLoad });

            fireEvent.load(container.querySelector('img')!);

            await waitFor(() => {
                expect(container.querySelector('.CoverArt-empty')).not.toBeInTheDocument();
            });

            expect(onLoad).toHaveBeenCalledTimes(1);
        });

        test('resets error state when URL changes', async () => {
            const { container, rerender } = render(<CoverArt url="invalid-url" />);

            // Trigger error
            const img = container.querySelector('img');
            fireEvent.error(img!);

            await waitFor(() => {
                expect(container.querySelector('.CoverArt-empty')).toBeVisible();
            });

            // Change URL - should reset error state and show new image
            rerender(<CoverArt url="new-valid-url" />);

            await waitFor(() => {
                expect(container.querySelector('img')).toBeInTheDocument();
            });

            const newImg = container.querySelector('img');
            expect(newImg).toHaveAttribute('src', 'new-valid-url');
        });
    });
});
