import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, renderHook, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { stored, workspace } from './helpers/layoutAuthoringFixture';
const mocks = vi.hoisted(() => ({ workspace: vi.fn(), stored: vi.fn(), mutate: vi.fn() }));
vi.mock('../src/platform/infrastructure/api/layoutAuthoring', async importOriginal => ({
    ...await importOriginal<typeof import('../src/platform/infrastructure/api/layoutAuthoring')>(),
    layoutAuthoringAPI: mocks,
}));
import { useEditableLayout } from '../src/platform/infrastructure/api/objectMetadataHooks';
const wrapper = ({ children }: { children: React.ReactNode }) => {
    const [client] = React.useState(() => new QueryClient({ defaultOptions: { queries: { retry: false } } }));
    return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
};
beforeEach(() => {
    mocks.workspace.mockReset().mockResolvedValue(workspace);
    mocks.stored.mockReset().mockResolvedValue(stored);
    mocks.mutate.mockReset();
});
describe('one authoring workspace binds editor reads', () => {
    it('resolves the default and reads its exact published document', async () => {
        const { result } = renderHook(() => useEditableLayout('Account'), { wrapper });
        await waitFor(() => expect(result.current.layout?.id).toBe('layout-a'));
        expect(mocks.stored).toHaveBeenCalledWith('Account', 'layout-a', expect.any(AbortSignal));
        expect(result.current.stored?.orderedListFields).toEqual([]);
    });
    it('never treats a failed workspace or stored read as an empty layout', async () => {
        mocks.workspace.mockRejectedValue(new Error('unavailable'));
        const first = renderHook(() => useEditableLayout('Account'), { wrapper });
        await waitFor(() => expect(first.result.current.error).toBeTruthy());
        expect(first.result.current.layout).toBeNull(); expect(mocks.stored).not.toHaveBeenCalled(); first.unmount();
        mocks.workspace.mockResolvedValue(workspace); mocks.stored.mockRejectedValue(new Error('forbidden'));
        const second = renderHook(() => useEditableLayout('Account', 'layout-a'), { wrapper });
        await waitFor(() => expect(second.result.current.error).toBeTruthy());
        expect(second.result.current.layout).toBeNull();
    });
    it('does not invent an ID or cross an object binding', async () => {
        const { result } = renderHook(() => useEditableLayout('Account', 'another-layout'), { wrapper });
        await waitFor(() => expect(result.current.error).toBeTruthy());
        expect(mocks.stored).not.toHaveBeenCalled();
    });
    it('permits creation only after a successful workspace read', async () => {
        mocks.workspace.mockResolvedValue({ ...workspace, layouts: [] });
        const { result } = renderHook(() => useEditableLayout('Account', 'new'), { wrapper });
        await waitFor(() => expect(result.current.workspace).toBeTruthy());
        expect(result.current.layout).toBeNull(); expect(result.current.error).toBeFalsy();
    });
    it('keeps loading through the stored read', async () => {
        mocks.stored.mockReturnValue(new Promise(() => {}));
        const { result } = renderHook(() => useEditableLayout('Account'), { wrapper });
        await waitFor(() => expect(mocks.stored).toHaveBeenCalled());
        expect(result.current.loading).toBe(true); expect(result.current.layout).toBeNull();
    });
    it('does not accept a later concurrent revision as this save readback', async () => {
        const savedRevision = `sha256:${'b'.repeat(64)}`;
        const laterRevision = `sha256:${'c'.repeat(64)}`;
        mocks.mutate.mockResolvedValue({ changed: true, layoutRevision: savedRevision, layoutId: 'layout-a' });
        mocks.workspace
            .mockResolvedValueOnce(workspace)
            .mockResolvedValueOnce({ ...workspace, layoutRevision: laterRevision });
        mocks.stored
            .mockResolvedValueOnce(stored)
            .mockResolvedValueOnce({ ...stored, layoutRevision: laterRevision });
        const { result } = renderHook(() => useEditableLayout('Account', 'layout-a'), { wrapper });
        await waitFor(() => expect(result.current.layout?.id).toBe('layout-a'));
        const draft = { ...result.current.layout!, layoutName: 'Saved name' };
        await act(async () => {
            await expect(result.current.save(draft)).rejects.toThrow('changed again after this save');
        });
    });
});
