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

const hoisted = vi.hoisted(() => ({
  userId: 'viewer-1',
  hasModifyAllData: false,
  approve: vi.fn(),
  reject: vi.fn(),
  recall: vi.fn(),
  reassign: vi.fn(),
}));

vi.mock('@nexuscrm/host-sdk', async importOriginal => {
  const actual = await importOriginal<Record<string, unknown>>();
  return {
    ...actual,
    useRuntime: () => ({ user: { Id: hoisted.userId } }),
    useSystemPermission: (permissionName: string) =>
      permissionName === 'ModifyAllData' && hoisted.hasModifyAllData,
  };
});

vi.mock('../src/platform/infrastructure/api/approvals', () => ({
  approvalsAPI: {
    approve: hoisted.approve,
    reject: hoisted.reject,
    recall: hoisted.recall,
    reassign: hoisted.reassign,
  },
}));

import { ApprovalBanner } from '../src/platform/features/approvals/ApprovalBanner';
import { APPROVAL_STATUS } from '../src/platform/features/approvals/approvalConstants';
import type { RecordApprovalStatus } from '../src/platform/features/approvals/approvalTypes';

const ON_BEHALF_NOTICE = /as an administrator/i;

function makePendingItem(overrides: Partial<RecordApprovalStatus> = {}): RecordApprovalStatus {
  return {
    status: APPROVAL_STATUS.PENDING,
    submittedById: 'submitter-1',
    submitterName: 'Sam Submitter',
    pendingWorkItems: [
      { workItemId: 'wi-1', actorId: 'approver-1', actorName: 'Alice Approver' },
      { workItemId: 'wi-2', actorId: 'approver-2', actorName: 'Bob Approver' },
    ],
    ...overrides,
  };
}

function renderBanner(pendingItem: RecordApprovalStatus) {
  const onActionComplete = vi.fn();
  render(<ApprovalBanner pendingItem={pendingItem} onActionComplete={onActionComplete} />);
  return { onActionComplete };
}

describe('ApprovalBanner admin act-on-behalf', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    hoisted.userId = 'viewer-1';
    hoisted.hasModifyAllData = false;
    hoisted.approve.mockResolvedValue({});
    hoisted.reject.mockResolvedValue({});
    hoisted.recall.mockResolvedValue({});
  });

  it('shows approve/reject/reassign and the on-behalf notice to a ModifyAllData holder who is not the assigned approver', () => {
    hoisted.userId = 'admin-1';
    hoisted.hasModifyAllData = true;

    renderBanner(makePendingItem());

    expect(screen.getByText('Approve')).toBeTruthy();
    expect(screen.getByText('Reject')).toBeTruthy();
    expect(screen.getByText('Reassign')).toBeTruthy();
    expect(screen.getByText(ON_BEHALF_NOTICE)).toBeTruthy();
    // The notice names whose work item the decision will land on.
    expect(screen.getByText('Alice Approver')).toBeTruthy();
    // Still framed as someone else's request, not the admin's own.
    expect(screen.getByText(/Pending Approval/)).toBeTruthy();
  });

  it('prefers the admin own work item and shows no notice when the admin is also an assigned approver', async () => {
    hoisted.userId = 'approver-2';
    hoisted.hasModifyAllData = true;

    renderBanner(makePendingItem());

    expect(screen.queryByText(ON_BEHALF_NOTICE)).toBeNull();
    fireEvent.click(screen.getByText('Approve'));
    fireEvent.click(screen.getByText('Confirm Approval'));

    await waitFor(() => expect(hoisted.approve).toHaveBeenCalledTimes(1));
    expect(hoisted.approve).toHaveBeenCalledWith('wi-2', '');
  });

  it('skips assignments without a work item id when picking the on-behalf target', async () => {
    hoisted.userId = 'admin-1';
    hoisted.hasModifyAllData = true;

    renderBanner(makePendingItem({
      pendingWorkItems: [
        { actorId: 'approver-1', actorName: 'Alice Approver' },
        { workItemId: 'wi-2', actorId: 'approver-2', actorName: 'Bob Approver' },
      ],
    }));

    expect(screen.getByText('Bob Approver')).toBeTruthy();
    fireEvent.click(screen.getByText('Approve'));
    fireEvent.click(screen.getByText('Confirm Approval'));

    await waitFor(() => expect(hoisted.approve).toHaveBeenCalledTimes(1));
    expect(hoisted.approve).toHaveBeenCalledWith('wi-2', '');
  });

  it('approves the first pending work item when an admin acts on behalf', async () => {
    hoisted.userId = 'admin-1';
    hoisted.hasModifyAllData = true;

    const { onActionComplete } = renderBanner(makePendingItem());

    fireEvent.click(screen.getByText('Approve'));
    fireEvent.change(screen.getByRole('textbox'), { target: { value: 'approved on behalf' } });
    fireEvent.click(screen.getByText('Confirm Approval'));

    await waitFor(() => expect(hoisted.approve).toHaveBeenCalledTimes(1));
    expect(hoisted.approve).toHaveBeenCalledWith('wi-1', 'approved on behalf');
    expect(onActionComplete).toHaveBeenCalledTimes(1);
  });

  it('shows no action buttons and no notice to a non-admin who is not the assigned approver', () => {
    hoisted.userId = 'viewer-1';
    hoisted.hasModifyAllData = false;

    renderBanner(makePendingItem());

    expect(screen.queryByText('Approve')).toBeNull();
    expect(screen.queryByText('Reject')).toBeNull();
    expect(screen.queryByText('Reassign')).toBeNull();
    expect(screen.queryByText('Recall')).toBeNull();
    expect(screen.queryByText(ON_BEHALF_NOTICE)).toBeNull();
    expect(screen.getByText('Alice Approver, Bob Approver')).toBeTruthy();
  });

  it('lets an admin who is neither submitter nor approver recall on behalf of the submitter', async () => {
    hoisted.userId = 'admin-1';
    hoisted.hasModifyAllData = true;

    const { onActionComplete } = renderBanner(makePendingItem());

    fireEvent.click(screen.getByText('Recall'));
    fireEvent.change(screen.getByRole('textbox'), { target: { value: 'recalled on behalf' } });
    fireEvent.click(screen.getByText('Confirm Recall'));

    await waitFor(() => expect(hoisted.recall).toHaveBeenCalledTimes(1));
    expect(hoisted.recall).toHaveBeenCalledWith('wi-1', 'recalled on behalf');
    expect(onActionComplete).toHaveBeenCalledTimes(1);
  });

  it('still targets the assigned approver own work item, without ModifyAllData', async () => {
    hoisted.userId = 'approver-2';
    hoisted.hasModifyAllData = false;

    renderBanner(makePendingItem());

    expect(screen.queryByText(ON_BEHALF_NOTICE)).toBeNull();
    fireEvent.click(screen.getByText('Approve'));
    fireEvent.click(screen.getByText('Confirm Approval'));

    await waitFor(() => expect(hoisted.approve).toHaveBeenCalledTimes(1));
    expect(hoisted.approve).toHaveBeenCalledWith('wi-2', '');
  });

  it('renders no actions at all when no pending assignment carries a work item id', () => {
    hoisted.userId = 'admin-1';
    hoisted.hasModifyAllData = true;

    renderBanner(makePendingItem({
      pendingWorkItems: [
        { actorId: 'approver-1', actorName: 'Alice Approver' },
        { actorId: 'approver-2', actorName: 'Bob Approver' },
      ],
    }));

    expect(screen.queryByText('Approve')).toBeNull();
    expect(screen.queryByText('Reassign')).toBeNull();
    expect(screen.queryByText('Recall')).toBeNull();
  });

  it('renders a single Recall button when the admin acting on behalf is also the submitter', () => {
    hoisted.userId = 'submitter-1';
    hoisted.hasModifyAllData = true;

    renderBanner(makePendingItem());

    expect(screen.getByText('Approve')).toBeTruthy();
    expect(screen.getAllByText('Recall')).toHaveLength(1);
  });
});
