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

import type { ActionMetadata, SObject } from '../src/platform/types';
import { actionRegistry, type ActionContext } from '../src/platform/features/record/actions/actionRegistry';
import { useMetadataActionInvoker } from '../src/platform/features/record/components/useMetadataActionInvoker';

const record = { Id: '001AC0000000001' } as unknown as SObject;

function metadataAction(type: string, overrides: Partial<ActionMetadata> = {}): ActionMetadata {
    return {
        id: `act-${type}`,
        name: type,
        label: type,
        type,
        objectApiName: 'Account',
        ...overrides,
    } as ActionMetadata;
}

// Minimal consumer: one trigger button per action + the modal host, mirroring
// how MetadataRecordDetail wires header descriptors to the invoker.
function Harness({ actions, onActionComplete, suppressed, isActionSuppressed }: {
    actions: ActionMetadata[];
    onActionComplete?: () => void;
    suppressed?: boolean;
    isActionSuppressed?: (action: ActionMetadata) => boolean;
}) {
    const { invoke, modals } = useMetadataActionInvoker({ record, onActionComplete, suppressed, isActionSuppressed });
    return (
        <div>
            {actions.map(a => (
                <button key={a.name} type="button" onClick={() => void invoke(a)}>
                    {a.label}
                </button>
            ))}
            {modals}
        </div>
    );
}

afterEach(() => {
    actionRegistry.clear();
});

describe('useMetadataActionInvoker', () => {
    it('dispatches invoke() through the action registry with the record and a context', async () => {
        const execute = vi.fn(async () => { });
        actionRegistry.register('TestDirect', { execute });
        render(<Harness actions={[metadataAction('TestDirect')]} />);

        fireEvent.click(screen.getByRole('button', { name: 'TestDirect' }));

        await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
        const [action, passedRecord, context] = execute.mock.calls[0] as unknown as [ActionMetadata, SObject, ActionContext];
        expect(action.type).toBe('TestDirect');
        expect(passedRecord).toBe(record);
        expect(typeof context.confirm).toBe('function');
        expect(typeof context.openModal).toBe('function');
    });

    it('hosts the confirmation dialog for confirm-gated handlers and runs onConfirm', async () => {
        const onConfirm = vi.fn(async () => { });
        actionRegistry.register('TestConfirm', {
            execute: async (_action, _record, context) => {
                context.confirm({ title: 'Confirm Action', message: 'Proceed?', onConfirm });
            },
        });
        render(<Harness actions={[metadataAction('TestConfirm')]} />);

        fireEvent.click(screen.getByRole('button', { name: 'TestConfirm' }));
        const dialog = await screen.findByRole('dialog', { name: 'Confirm Action' });
        expect(dialog).toHaveTextContent('Proceed?');

        fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
        await waitFor(() => expect(onConfirm).toHaveBeenCalledTimes(1));
        // dialog closes once the confirmed work settles
        await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
    });

    it('hosts a handler-opened modal component and closes it via its onClose', async () => {
        const Modal = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) =>
            isOpen ? (
                <div data-testid="plugin-modal">
                    <button type="button" onClick={onClose}>close-plugin</button>
                </div>
            ) : null;
        actionRegistry.register('TestModal', {
            execute: async (_action, _record, context) => {
                context.openModal(Modal as React.ComponentType<unknown>, {});
            },
        });
        render(<Harness actions={[metadataAction('TestModal')]} />);

        fireEvent.click(screen.getByRole('button', { name: 'TestModal' }));
        expect(await screen.findByTestId('plugin-modal')).toBeInTheDocument();

        fireEvent.click(screen.getByRole('button', { name: 'close-plugin' }));
        await waitFor(() => expect(screen.queryByTestId('plugin-modal')).toBeNull());
    });

    it('forwards context.refreshRecord to onActionComplete', async () => {
        const onActionComplete = vi.fn();
        actionRegistry.register('TestRefresh', {
            execute: async (_action, _record, context) => {
                context.refreshRecord();
            },
        });
        render(<Harness actions={[metadataAction('TestRefresh')]} onActionComplete={onActionComplete} />);

        fireEvent.click(screen.getByRole('button', { name: 'TestRefresh' }));
        await waitFor(() => expect(onActionComplete).toHaveBeenCalledTimes(1));
    });

    it('tears down an open confirmation when the record becomes suppressed (locked) and does not execute or resurface it', async () => {
        const onConfirm = vi.fn(async () => { });
        actionRegistry.register('TestLockConfirm', {
            execute: async (_action, _record, context) => {
                context.confirm({ title: 'Activate', message: 'Proceed?', onConfirm });
            },
        });
        const actions = [metadataAction('TestLockConfirm')];
        const { rerender } = render(<Harness actions={actions} suppressed={false} />);

        fireEvent.click(screen.getByRole('button', { name: 'TestLockConfirm' }));
        await screen.findByRole('dialog', { name: 'Activate' });

        // the record becomes locked while the confirmation is open
        rerender(<Harness actions={actions} suppressed={true} />);
        await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
        expect(onConfirm).not.toHaveBeenCalled();

        // unlocking must not resurface the torn-down dialog
        rerender(<Harness actions={actions} suppressed={false} />);
        await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
    });

    it('tears down a handler-opened modal when the record becomes suppressed', async () => {
        const Modal = ({ isOpen }: { isOpen: boolean; onClose: () => void }) =>
            isOpen ? <div data-testid="plugin-modal" /> : null;
        actionRegistry.register('TestLockModal', {
            execute: async (_action, _record, context) => {
                context.openModal(Modal as React.ComponentType<unknown>, {});
            },
        });
        const actions = [metadataAction('TestLockModal')];
        const { rerender } = render(<Harness actions={actions} suppressed={false} />);
        fireEvent.click(screen.getByRole('button', { name: 'TestLockModal' }));
        expect(await screen.findByTestId('plugin-modal')).toBeInTheDocument();

        rerender(<Harness actions={actions} suppressed={true} />);
        await waitFor(() => expect(screen.queryByTestId('plugin-modal')).toBeNull());
    });

    it('ignores invoke() while suppressed', async () => {
        const execute = vi.fn(async () => { });
        actionRegistry.register('TestSuppressedInvoke', { execute });
        render(<Harness actions={[metadataAction('TestSuppressedInvoke')]} suppressed={true} />);
        fireEvent.click(screen.getByRole('button', { name: 'TestSuppressedInvoke' }));
        await waitFor(() => expect(execute).not.toHaveBeenCalled());
    });

    it('tears down the open interaction when its own action becomes suppressed (record-state hide override)', async () => {
        const onConfirm = vi.fn(async () => { });
        actionRegistry.register('TestHideConfirm', {
            execute: async (_action, _record, context) => {
                context.confirm({ title: 'Hide Me', message: 'Proceed?', onConfirm });
            },
        });
        const actions = [metadataAction('TestHideConfirm')];
        const { rerender } = render(<Harness actions={actions} isActionSuppressed={() => false} />);
        fireEvent.click(screen.getByRole('button', { name: 'TestHideConfirm' }));
        await screen.findByRole('dialog', { name: 'Hide Me' });

        rerender(<Harness actions={actions} isActionSuppressed={(a) => a.name === 'TestHideConfirm'} />);
        await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
        expect(onConfirm).not.toHaveBeenCalled();
    });

    it('does not throw when invoking an action whose type has no registered handler', async () => {
        render(<Harness actions={[metadataAction('TestUnregistered')]} />);
        fireEvent.click(screen.getByRole('button', { name: 'TestUnregistered' }));
        // no handler → error toast (no-op in the stub); nothing rendered, nothing thrown
        await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
    });
});
