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

import { LayoutModeToolstrip } from '../src/platform/features/admin/components/layout/LayoutModeToolstrip';

// The lens control itself. It was markup inside LayoutEditor until this suite
// needed to drive it — the editor pulls in react-query and the router, so
// rendering it costs seconds and times out often enough that the guard would be
// worth less than the code it guards. A leaf component is affordable, and it
// beats pinning identifiers with a source regex, which passes as long as the
// text is present whether or not the button does anything.

function renderToolstrip(props: Partial<React.ComponentProps<typeof LayoutModeToolstrip>> = {}) {
    const onModeChange = vi.fn();
    const onOnlyShowVisibleChange = vi.fn();
    render(
        <LayoutModeToolstrip
            mode="detail"
            onModeChange={onModeChange}
            createRequiredFields={['Name']}
            counts={{ total: 97, shown: 27, hidden: 70, required: 0 }}
            missingRequiredFields={[]}
            onlyShowVisible={false}
            onOnlyShowVisibleChange={onOnlyShowVisibleChange}
            {...props}
        />,
    );
    return { onModeChange, onOnlyShowVisibleChange };
}

describe('switching the lens', () => {
    it('offers exactly the three modes the projection understands', () => {
        renderToolstrip();

        expect(screen.getByText('Detail page')).toBeTruthy();
        expect(screen.getByText('Create form')).toBeTruthy();
        expect(screen.getByText('Edit form')).toBeTruthy();
    });

    it('reports which lens is active, so the canvas below is never ambiguous', () => {
        renderToolstrip({ mode: 'create' });

        expect(screen.getByText('Create form').getAttribute('aria-pressed')).toBe('true');
        expect(screen.getByText('Detail page').getAttribute('aria-pressed')).toBe('false');
    });

    it('asks for the mode that was clicked', () => {
        const { onModeChange } = renderToolstrip();

        fireEvent.click(screen.getByText('Edit form'));

        expect(onModeChange).toHaveBeenCalledWith('edit');
    });

    it('toggles the filter to the checkbox state, not blindly to true', () => {
        const { onOnlyShowVisibleChange } = renderToolstrip({ onlyShowVisible: true });

        fireEvent.click(screen.getByLabelText('Only show visible'));

        expect(onOnlyShowVisibleChange).toHaveBeenCalledWith(false);
    });
});

describe('a layout the server cannot report on yet', () => {
    it('refuses the two form lenses rather than guessing', () => {
        const { onModeChange } = renderToolstrip({ createRequiredFields: undefined });

        // An unsaved layout or an older snapshot can lack the demanded-field list, so the form
        // lenses would let an admin hide or remove a field the save requires
        // while the canvas said otherwise. Fail closed, and say why.
        // BOTH of them, because the gate is written over the mode vocabulary
        // (`!== 'detail'`) rather than over `create`. Asserting only the create
        // button let a narrowing to `=== 'create'` survive, which leaves the
        // edit lens open on exactly the layouts the gate exists to close.
        for (const label of ['Create form', 'Edit form']) {
            const button = screen.getByText(label);
            expect(button.hasAttribute('disabled')).toBe(true);
            expect(button.getAttribute('title')).toContain('Load the latest layout');

            fireEvent.click(button);
            expect(onModeChange).not.toHaveBeenCalled();
        }
    });

    it('does not tell the admin the create list is why the edit lens is closed', () => {
        renderToolstrip({ createRequiredFields: undefined });

        // Only the create half has a technical reason; the edit half is
        // grouped with it as a product decision. The copy may state the rule
        // and may explain the create half, but it must not present the
        // create-only reason as the cause of both — the previous wording did,
        // and a reviewer read it as a defect rather than a choice.
        const title = screen.getByText('Edit form').getAttribute('title') ?? '';
        expect(title).not.toContain('create and edit forms');
    });

    it('keeps the detail page available, which needs no such list', () => {
        renderToolstrip({ createRequiredFields: undefined });

        const detail = screen.getByText('Detail page');
        expect(detail.hasAttribute('disabled')).toBe(false);
    });

    it('offers all three when the server reports create requirements', () => {
        renderToolstrip();

        for (const label of ['Detail page', 'Create form', 'Edit form']) {
            expect(screen.getByText(label).hasAttribute('disabled')).toBe(false);
        }
    });
});

describe('required create fields missing from the layout', () => {
    it('names the missing fields and asks the admin to check their inputs', () => {
        renderToolstrip({ missingRequiredFields: ['OpportunityId', 'Quantity'] });
        const note = screen.getByText(/2 create fields missing from layout/);
        expect(note.getAttribute('title')).toContain('OpportunityId');
        expect(note.getAttribute('title')).toContain('Quantity');
        expect(note.getAttribute('title')).toContain('Check their related inputs');
        expect(note.getAttribute('title')).not.toMatch(/adds|cannot be saved/);
    });

    it('stays quiet when the layout carries everything', () => {
        renderToolstrip();
        expect(screen.queryByText(/create fields missing from layout/)).toBeNull();
    });
});

describe('the count stands in for seeing the form', () => {
    it('reads shown against total', () => {
        renderToolstrip();

        expect(screen.getByText(/of 97 fields shown/)).toBeTruthy();
        expect(screen.getByText('27')).toBeTruthy();
    });

    it('mentions requirements only when there are some', () => {
        renderToolstrip({ counts: { total: 97, shown: 27, hidden: 70, required: 4 } });
        expect(screen.getByText(/· 4 required/)).toBeTruthy();
    });

    it('says nothing about requirements when there are none', () => {
        renderToolstrip();
        expect(screen.queryByText(/required/)).toBeNull();
    });

    it('does not claim to be the whole truth', () => {
        renderToolstrip();

        // The count covers the authored layout; field permissions narrow the
        // rendered form further and missing create inputs need separate attention.
        const chip = screen.getByText(/of 97 fields shown/);
        expect(chip.getAttribute('title')).toContain('field-level security');
        expect(chip.getAttribute('title')).toContain('including those on other tabs');
    });
});
