import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { ApprovalProcessSetup } from '../src/platform/features/approvals/ApprovalProcessSetup';
import { ApprovalProcessSetupWizard } from '../src/platform/features/approvals/ApprovalProcessSetupWizard';
import { OBJECT_APPROVAL_PROCESS_KEYS } from '../src/platform/features/approvals/useObjectHasApprovalProcess';

const apiMocks = vi.hoisted(() => ({
  activateProcess: vi.fn(),
  deactivateProcess: vi.fn(),
  deleteApprovalProcess: vi.fn(),
  getApprovalProcesses: vi.fn(),
  getPendingCountsByProcess: vi.fn(),
}));

const deployMetadataMock = vi.hoisted(() => vi.fn());

vi.mock('../src/platform/infrastructure/api/approvals', () => ({
  approvalsAPI: apiMocks,
}));

vi.mock('../src/platform/infrastructure/api/metadataDeploy', () => ({
  deployMetadata: deployMetadataMock,
}));

vi.mock('../src/platform/infrastructure/api/objectQueries', () => ({
  useSchema: () => ({ data: undefined }),
  useSchemas: () => ({ data: [] }),
}));

vi.mock('../src/platform/features/approvals/components/ProcessActionsSection', () => ({
  ProcessActionsSection: () => null,
}));

vi.mock('../src/platform/features/approvals/components/wizard-steps', () => ({
  ConfigurationStep: ({
    updateState,
  }: {
    updateState: (update: Record<string, unknown>) => void;
  }) => (
    <button
      type="button"
      onClick={() => updateState({
        targetObject: 'Opportunity',
        label: 'Opportunity Approval',
        fullName: 'Opportunity_Approval',
        nextAutomatedApprover: {
          userHierarchyField: 'ManagerId',
          useApproverFieldOfRecordOwner: false,
        },
        approvalSteps: [],
      })}
    >
      Make deployable
    </button>
  ),
  AutomatedApproverStep: () => null,
  ApprovalStepsEditor: () => null,
  ReviewAndDeploy: () => null,
}));

vi.mock('@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui')>();
  return {
    ...actual,
    PowerList: ({
      actions,
      ariaLabel,
      data,
      leadingColumn,
      loading,
      rowKey,
    }: {
      actions?: (row: Record<string, unknown>) => React.ReactNode;
      ariaLabel?: string;
      data: Array<Record<string, unknown>>;
      leadingColumn?: { render: (row: Record<string, unknown>) => React.ReactNode };
      loading?: boolean;
      rowKey: (row: Record<string, unknown>) => string;
    }) => {
      if (loading) return <div role="status">Loading...</div>;
      return (
        <div aria-label={ariaLabel}>
          {data.map((row) => {
            const key = rowKey(row);
            return (
              <div key={key}>
                <div data-testid={`leading-${key}`}>{leadingColumn?.render(row)}</div>
                <div data-testid={`actions-${key}`}>{actions?.(row)}</div>
              </div>
            );
          })}
        </div>
      );
    },
  };
});

const process = {
  Id: 'pd-1',
  Name: 'Opportunity Approval',
  DeveloperName: 'Opportunity_Approval',
  State: 'Inactive',
  TableEnumOrId: 'Opportunity',
};

function renderWithClient(component: React.ReactElement) {
  const client = new QueryClient();
  const invalidateQueries = vi.spyOn(client, 'invalidateQueries');
  render(
    <QueryClientProvider client={client}>
      {component}
    </QueryClientProvider>,
  );
  return { invalidateQueries };
}

describe('approval-process admin mutations invalidate object approval gates', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    apiMocks.getApprovalProcesses.mockResolvedValue([process]);
    apiMocks.getPendingCountsByProcess.mockResolvedValue([]);
    apiMocks.activateProcess.mockResolvedValue(undefined);
    apiMocks.deactivateProcess.mockResolvedValue(undefined);
    apiMocks.deleteApprovalProcess.mockResolvedValue(undefined);
    deployMetadataMock.mockResolvedValue(undefined);
  });

  it('invalidates after list-page toggle and delete mutations', async () => {
    const { invalidateQueries } = renderWithClient(<ApprovalProcessSetup />);

    const toggle = await screen.findByTestId('leading-pd-1');
    fireEvent.click(within(toggle).getByRole('button'));

    await waitFor(() => expect(apiMocks.activateProcess).toHaveBeenCalledWith('pd-1'));
    await waitFor(() => expect(invalidateQueries).toHaveBeenCalledWith({
      queryKey: OBJECT_APPROVAL_PROCESS_KEYS.root,
    }));

    const actions = await screen.findByTestId('actions-pd-1');
    fireEvent.click(within(actions).getAllByRole('button')[1]);
    const confirmation = await screen.findByRole('dialog', { name: 'Delete Approval Process' });
    fireEvent.click(within(confirmation).getByRole('button', { name: 'Delete' }));

    await waitFor(() => expect(apiMocks.deleteApprovalProcess).toHaveBeenCalledWith('pd-1'));
    await waitFor(() => expect(invalidateQueries).toHaveBeenCalledTimes(2));
    expect(invalidateQueries).toHaveBeenNthCalledWith(2, {
      queryKey: OBJECT_APPROVAL_PROCESS_KEYS.root,
    });
  });

  it('invalidates after the wizard deploys an approval process', async () => {
    const { invalidateQueries } = renderWithClient(<ApprovalProcessSetupWizard />);

    fireEvent.click(screen.getByRole('button', { name: 'Make deployable' }));
    fireEvent.click(screen.getByRole('button', { name: 'Save & Deploy' }));

    await waitFor(() => expect(deployMetadataMock).toHaveBeenCalledTimes(1));
    expect(deployMetadataMock).toHaveBeenCalledWith(
      expect.objectContaining({
        'approvalProcesses/Opportunity.Opportunity_Approval.approvalProcess': expect.any(String),
      }),
      ['Opportunity.Opportunity_Approval'],
      'ApprovalProcess',
    );
    await waitFor(() => expect(invalidateQueries).toHaveBeenCalledWith({
      queryKey: OBJECT_APPROVAL_PROCESS_KEYS.root,
    }));
  });
});
