import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { TagPicker } from '../../../../assets/admin/editor/TagPicker';
import { EditorCore } from '../../../../assets/admin/editor/EditorCore';

const ALL_TAGS = [
    { id: 'tag-1', name: 'Culture', slug: 'culture' },
    { id: 'tag-2', name: 'Sport', slug: 'sport' },
    { id: 'tag-3', name: 'Technologie', slug: 'technologie' },
];

function mockFetchOnce(body: unknown, ok = true): void {
    (global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
        ok,
        json: () => Promise.resolve(body),
    } as Response);
}

describe('TagPicker', () => {
    let container: HTMLElement;
    let editorCore: EditorCore;

    beforeEach(() => {
        document.body.innerHTML = `
            <div id="tag-picker" data-page-id="page-abc" data-selected-tag-ids="tag-1"></div>
        `;
        container = document.getElementById('tag-picker')!;
        editorCore = new EditorCore();

        global.fetch = vi.fn();
    });

    afterEach(() => {
        document.body.innerHTML = '';
        vi.restoreAllMocks();
    });

    it('fetches GET /api/admin/tags and renders the page\'s current tags as chips', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        expect(global.fetch).toHaveBeenCalledWith('/api/admin/tags', expect.objectContaining({ method: 'GET' }));

        const chips = container.querySelectorAll('.tag-picker__chip');
        expect(chips).toHaveLength(1);
        expect(chips[0]!.textContent).toContain('Culture');
    });

    it('adds a tag as a chip when selected from the add-select, and marks the page dirty', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        const markChangedSpy = vi.spyOn(editorCore.getChangeTracker(), 'markChanged');

        const addSelect = container.querySelector<HTMLSelectElement>('.tag-picker__add-select')!;
        addSelect.value = 'tag-2';
        addSelect.dispatchEvent(new Event('change'));

        const chips = container.querySelectorAll('.tag-picker__chip');
        expect(chips).toHaveLength(2);
        expect(markChangedSpy).toHaveBeenCalled();
    });

    it('removes a tag chip when its remove button is clicked, and marks the page dirty', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        const markChangedSpy = vi.spyOn(editorCore.getChangeTracker(), 'markChanged');

        const removeButton = container.querySelector<HTMLButtonElement>('.tag-picker__chip-remove')!;
        removeButton.click();

        expect(container.querySelectorAll('.tag-picker__chip')).toHaveLength(0);
        expect(markChangedSpy).toHaveBeenCalled();
    });

    it('does not offer already-selected tags in the add-select', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        const options = Array.from(
            container.querySelectorAll<HTMLOptionElement>('.tag-picker__add-select option')
        ).map(o => o.value);

        expect(options).not.toContain('tag-1');
        expect(options).toContain('tag-2');
        expect(options).toContain('tag-3');
    });

    it('registers a save handler on EditorCore that PATCHes the selected tag set', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });
        const registerSpy = vi.spyOn(editorCore, 'registerAdditionalSaveHandler');

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        expect(registerSpy).toHaveBeenCalledTimes(1);

        const addSelect = container.querySelector<HTMLSelectElement>('.tag-picker__add-select')!;
        addSelect.value = 'tag-3';
        addSelect.dispatchEvent(new Event('change'));

        mockFetchOnce({ success: true, data: { tagIds: ['tag-1', 'tag-3'] } });

        const savedHandler = registerSpy.mock.calls[0]![0];
        const result = await savedHandler();

        expect(result).toBe(true);
        expect(global.fetch).toHaveBeenLastCalledWith(
            '/api/admin/pages/page-abc/tags',
            expect.objectContaining({
                method: 'PATCH',
                body: JSON.stringify({ tagIds: ['tag-1', 'tag-3'] }),
            })
        );
    });

    it('never includes an inline tag-creation control (v1 scope: pre-created tags only)', async () => {
        mockFetchOnce({ success: true, data: ALL_TAGS });

        const picker = new TagPicker(container, editorCore);
        await picker.initialize();

        expect(container.querySelector('input[type="text"]')).toBeNull();
        expect(container.textContent).not.toMatch(/créer|nouveau tag/i);
    });
});
