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

const pageTagsApi = vi.hoisted(() => ({
    getMyTagsForPage: vi.fn(),
    addTagToPage: vi.fn(),
    removeTagFromPage: vi.fn(),
    listMyDistinctPageTags: vi.fn()
}));

vi.mock('$lib/api/page_tags', () => pageTagsApi);

afterEach(() => {
    cleanup();
    vi.clearAllMocks();
});

async function settle() {
    await tick();
    await Promise.resolve();
    await tick();
}

describe('AddTagsSheet', () => {
    it('renders current tags as chips after load', async () => {
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce(['funny', 'fight']);
        pageTagsApi.listMyDistinctPageTags.mockResolvedValueOnce([]);

        render(AddTagsSheet, { props: { pageId: 'p1' } });
        await settle();

        expect(screen.getByTestId('add-tags-remove-funny')).toBeTruthy();
        expect(screen.getByTestId('add-tags-remove-fight')).toBeTruthy();
    });

    it('renders suggestions excluding already-applied tags', async () => {
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce(['funny']);
        pageTagsApi.listMyDistinctPageTags.mockResolvedValueOnce([
            { tag: 'funny', count: 5 },
            { tag: 'fight', count: 3 }
        ]);

        render(AddTagsSheet, { props: { pageId: 'p1' } });
        await settle();

        expect(screen.queryByTestId('add-tags-suggest-funny')).toBeNull();
        expect(screen.getByTestId('add-tags-suggest-fight')).toBeTruthy();
    });

    it('add posts then refetches normalized tag list', async () => {
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce([]);
        // Two calls: initial load + post-add refreshSuggestions.
        pageTagsApi.listMyDistinctPageTags.mockResolvedValue([]);
        pageTagsApi.addTagToPage.mockResolvedValueOnce(undefined);
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce(['funny']);

        const onChange = vi.fn();
        render(AddTagsSheet, { props: { pageId: 'p1', onChange } });
        await settle();

        const input = screen.getByTestId('add-tags-input') as HTMLInputElement;
        await fireEvent.input(input, { target: { value: 'Funny' } });
        await fireEvent.click(screen.getByTestId('add-tags-submit'));
        await settle();

        expect(pageTagsApi.addTagToPage).toHaveBeenCalledWith('p1', 'Funny');
        expect(pageTagsApi.getMyTagsForPage).toHaveBeenCalledTimes(2);
        expect(onChange).toHaveBeenCalledWith(['funny']);
        // Stored as normalized — chip shows lowercase.
        expect(screen.getByTestId('add-tags-remove-funny')).toBeTruthy();
    });

    it('restores focus to the input after a successful add', async () => {
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce([]);
        pageTagsApi.listMyDistinctPageTags.mockResolvedValue([]);
        pageTagsApi.addTagToPage.mockResolvedValueOnce(undefined);
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce(['funny']);

        render(AddTagsSheet, { props: { pageId: 'p1' } });
        await settle();

        const input = screen.getByTestId('add-tags-input') as HTMLInputElement;
        input.focus();
        await fireEvent.input(input, { target: { value: 'funny' } });
        await fireEvent.click(screen.getByTestId('add-tags-submit'));
        await settle();
        // Drain the queueMicrotask scheduled in `add()`'s finally so
        // the focus-restore call has actually run.
        await Promise.resolve();
        await Promise.resolve();

        expect(document.activeElement).toBe(input);
    });

    it('remove deletes and updates chip list', async () => {
        pageTagsApi.getMyTagsForPage.mockResolvedValueOnce(['funny', 'fight']);
        pageTagsApi.listMyDistinctPageTags.mockResolvedValueOnce([]);
        pageTagsApi.removeTagFromPage.mockResolvedValueOnce(undefined);

        const onChange = vi.fn();
        render(AddTagsSheet, { props: { pageId: 'p1', onChange } });
        await settle();

        await fireEvent.click(screen.getByTestId('add-tags-remove-funny'));
        await settle();

        expect(pageTagsApi.removeTagFromPage).toHaveBeenCalledWith('p1', 'funny');
        expect(screen.queryByTestId('add-tags-remove-funny')).toBeNull();
        expect(onChange).toHaveBeenCalledWith(['fight']);
    });
});
