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

import type { StandardAction } from '../src/platform/infrastructure/api/actions';
import type { SObject, SObjectDescribe } from '../src/platform/types';

// The renderer registry routes through the host-sdk, which the vitest stub does
// not wire for registration. Mock just the resolver: a registered renderer is
// modeled by returning a modal component for the 'createQuote' rendererId.
vi.mock('../src/platform/features/record/actions/actionRendererRegistry', async () => {
    const React = await import('react');
    const Modal = (props: { isOpen: boolean; onClose: () => void; action: { name: string } }) =>
        props.isOpen
            ? React.createElement(
                'div',
                { 'data-testid': 'modal' },
                `Modal: ${props.action.name}`,
                React.createElement('button', { onClick: props.onClose }, 'close'),
            )
            : null;
    return {
        getActionRenderer: (rendererId: string | undefined) =>
            rendererId === 'createQuoteRenderer' ? Modal : undefined,
    };
});

import {
    ActionRendererHost,
    buildStandardActionTriggerModels,
} from '../src/platform/features/record/components/ActionRendererHost';

function standardAction(name: string, overrides: Partial<StandardAction> = {}): StandardAction {
    return {
        name,
        label: name,
        type: 'standard',
        targetObjects: ['Opportunity'],
        rendererId: `${name}Renderer`,
        ...overrides,
    } as StandardAction;
}

const record = { Id: '006OPP000000001' } as unknown as SObject;
const objectMetadata = { name: 'Opportunity' } as unknown as SObjectDescribe;

describe('buildStandardActionTriggerModels', () => {
    it('derives an invokable model when the action has a registered renderer', () => {
        const [model] = buildStandardActionTriggerModels([standardAction('createQuote', { label: 'Create Quote' })]);
        expect(model.label).toBe('Create Quote');
        expect(model.disabled).toBe(false);
        expect(model.invokable).toBe(true);
        expect(model.title).toBeUndefined();
        expect(model.Icon).toBeDefined(); // a renderable lucide component (forwardRef object)
    });

    it('marks an action with no registered renderer disabled + non-invokable with an explanatory title', () => {
        const [model] = buildStandardActionTriggerModels([standardAction('missing')]);
        expect(model.disabled).toBe(true);
        expect(model.invokable).toBe(false);
        expect(model.title).toContain('No renderer registered');
    });

    it('honors a disable override (disabled, non-invokable, reason as title)', () => {
        const overrides = new Map([['createQuote', { reason: 'Locked while pending' }]]);
        const [model] = buildStandardActionTriggerModels([standardAction('createQuote')], overrides);
        expect(model.disabled).toBe(true);
        expect(model.invokable).toBe(false);
        expect(model.title).toBe('Locked while pending');
    });
});

describe('ActionRendererHost trigger/modal separation', () => {
    it('default: renders the trigger button and opens the modal on click (self-contained)', () => {
        render(
            <ActionRendererHost
                actions={[standardAction('createQuote', { label: 'Create Quote' })]}
                record={record}
                objectMetadata={objectMetadata}
            />,
        );
        const trigger = screen.getByRole('button', { name: /Create Quote/ });
        expect(screen.queryByTestId('modal')).toBeNull();
        fireEvent.click(trigger);
        expect(screen.getByTestId('modal')).toHaveTextContent('Modal: createQuote');
    });

    it('renderTriggers=false + controlled: renders no trigger, mounts the modal for the controlled openAction, and closes via onOpenActionChange', () => {
        const action = standardAction('createQuote', { label: 'Create Quote' });
        const onOpenActionChange = vi.fn();
        render(
            <ActionRendererHost
                actions={[action]}
                record={record}
                objectMetadata={objectMetadata}
                renderTriggers={false}
                openAction={action}
                onOpenActionChange={onOpenActionChange}
            />,
        );
        expect(screen.queryByRole('button', { name: /Create Quote/ })).toBeNull();
        expect(screen.getByTestId('modal')).toHaveTextContent('Modal: createQuote');
        fireEvent.click(screen.getByRole('button', { name: 'close' }));
        expect(onOpenActionChange).toHaveBeenCalledWith(null);
    });

    it('modal-only host mounts the controlled openAction even when there are no visible trigger actions', () => {
        // Header owns the triggers and passes actions=[] to use this host purely
        // as the modal lifecycle owner. The modal must still mount.
        const action = standardAction('createQuote', { label: 'Create Quote' });
        render(
            <ActionRendererHost
                actions={[]}
                record={record}
                objectMetadata={objectMetadata}
                renderTriggers={false}
                openAction={action}
                onOpenActionChange={vi.fn()}
            />,
        );
        expect(screen.getByTestId('modal')).toHaveTextContent('Modal: createQuote');
    });

    it('controlled: clears the caller openAction state when the host unmounts with a modal open', () => {
        // If a record lock empties the caller's renderable list mid-open, the
        // caller stops rendering the host and the modal vanishes without an
        // onClose. The host must clear the controlled state on unmount, or the
        // modal would spontaneously reopen when the host remounts later.
        const action = standardAction('createQuote', { label: 'Create Quote' });
        const onOpenActionChange = vi.fn();
        const { unmount } = render(
            <ActionRendererHost
                actions={[action]}
                record={record}
                objectMetadata={objectMetadata}
                renderTriggers={false}
                openAction={action}
                onOpenActionChange={onOpenActionChange}
            />,
        );
        expect(screen.getByTestId('modal')).toBeInTheDocument();
        unmount();
        expect(onOpenActionChange).toHaveBeenCalledWith(null);
    });

    it('controlled: unmounting with no action open does not touch the caller state', () => {
        const onOpenActionChange = vi.fn();
        const { unmount } = render(
            <ActionRendererHost
                actions={[standardAction('createQuote')]}
                record={record}
                objectMetadata={objectMetadata}
                renderTriggers={false}
                openAction={null}
                onOpenActionChange={onOpenActionChange}
            />,
        );
        unmount();
        expect(onOpenActionChange).not.toHaveBeenCalled();
    });

    it('renderTriggers=false + controlled openAction=null: renders neither trigger nor modal', () => {
        render(
            <ActionRendererHost
                actions={[standardAction('createQuote', { label: 'Create Quote' })]}
                record={record}
                objectMetadata={objectMetadata}
                renderTriggers={false}
                openAction={null}
                onOpenActionChange={vi.fn()}
            />,
        );
        expect(screen.queryByRole('button', { name: /Create Quote/ })).toBeNull();
        expect(screen.queryByTestId('modal')).toBeNull();
    });
});
