import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import {
  type CrmActionHandlerHost,
} from '../src/action-handlers';
import { CRM_ACTION_HANDLER_TYPES } from '../src/action-surface-policy';
import {
  type CrmActivityTimelineActionConfig,
  type CrmActivityTimelineObjectMetadata,
  type CrmActivityTimelinePanelProps,
  type CrmActivityTimelineStandardAction,
} from '../src/activity-timeline-adapter';
import { CRM_FRONTEND_APP_ID, CRM_FRONTEND_ROUTE_COMPONENT_KEYS } from '../src/route-surface-policy';
import {
  CRM_ACTIVITY_TIMELINE_RENDERER_ID,
  CRM_CONVERTED_LEAD_BANNER_RENDERER_ID,
  CRM_CREATE_RECORD_FROM_SOURCE_RENDERER_ID,
  CRM_EMAIL_COMPOSE_RENDERER_ID,
  CRM_LEAD_CONVERT_RENDERER_ID,
  CRM_RECORD_ACTIVITY_FEED_RENDERER_ID,
  CRM_RECORD_CHILD_BAR_CHART_RENDERER_ID,
  CRM_TASK_ACTIVITY_QUICK_CREATE_RENDERER_ID,
} from '../src/renderer-ids';
import {
  type CrmRuntimeActionRendererProps,
  type CrmRuntimeRecordPanelProps,
} from '../src/runtime-renderer-adapters';
import { CRM_SETUP_COMPONENTS } from '../src/setup-navigation-policy';
import type {
  CrmUiFieldInputProps,
  CrmUiFieldRendererProps,
} from '../src/ui-field-components';
import {
  CRM_APP_CONFIG_FRONTEND_OWNER_SOURCE,
  createCrmFrontendHostAdapterRegistration,
  registerCrmFrontendHostAdapter,
  type CrmFrontendHostAdapterHost,
} from '../src/host-adapter';

const DummyComponent: React.FC = () => null;
const DummyWidget: React.FC = () => null;
const DummyLookup: React.FC = () => null;

interface HostObjectMetadata extends CrmActivityTimelineObjectMetadata {
  fields?: unknown[];
}

type HostActionConfig = CrmActivityTimelineActionConfig;
type HostAction = CrmActivityTimelineStandardAction;
type HostActionProps = CrmRuntimeActionRendererProps;
type HostPanelProps = CrmRuntimeRecordPanelProps & CrmActivityTimelinePanelProps<HostObjectMetadata, HostActionConfig>;
type HostRenderer = {
  kind: 'action' | 'panel';
  component: React.FC<HostActionProps> | React.FC<HostPanelProps>;
};
type FieldRenderer = React.FC<CrmUiFieldRendererProps>;
type FieldInput = React.FC<CrmUiFieldInputProps>;

function createHost() {
  const surfaceSpies = {
    registerAppConfigFrontendOwner: vi.fn(),
    registerCurrencyDisplayPolicy: vi.fn(),
    registerSetupSurface: vi.fn(),
    registerSetupPageCapabilities: vi.fn(),
    registerAppNavigationCapabilities: vi.fn(),
    registerEmailAlertRecipientTypes: vi.fn(),
    registerHostApp: vi.fn(),
    registerStandardPageTabs: vi.fn(),
  };
  const rendererSpies = {
    createActionRendererHostAdapter: vi.fn((component: React.FC<HostActionProps>): HostRenderer => ({
      kind: 'action',
      component,
    })),
    createRecordPanelHostAdapter: vi.fn((component: React.FC<HostPanelProps>): HostRenderer => ({
      kind: 'panel',
      component,
    })),
    registerActionRendererHostAdapter: vi.fn(),
    registerRecordPanelHostAdapter: vi.fn(),
  };
  const actionRegistry = {
    register: vi.fn(),
    get: vi.fn(),
  } satisfies CrmActionHandlerHost['actionRegistry'];
  const routeComponents = Object.fromEntries(
    CRM_FRONTEND_ROUTE_COMPONENT_KEYS.map(key => [key, DummyComponent]),
  );
  const setupComponents = Object.fromEntries(
    CRM_SETUP_COMPONENTS.map(({ name }) => [name, DummyComponent]),
  );

  const host: CrmFrontendHostAdapterHost<
    HostActionProps,
    HostPanelProps,
    HostRenderer,
    typeof DummyComponent,
    typeof DummyWidget,
    string,
    string,
    FieldRenderer,
    FieldInput,
    HostAction,
    HostActionConfig,
    HostObjectMetadata
  > = {
    routeComponents,
    createRedirectRoute: () => DummyComponent,
    surfaces: surfaceSpies,
    rendererAdapters: rendererSpies,
    actionHandlers: {
      actionRegistry,
      dataApi: {
        executeAction: async () => undefined,
        executeStandardCreateRecord: async () => ({}),
        deleteRecord: async () => undefined,
        updateRecord: async () => undefined,
        getRecord: async () => undefined,
        getInternalObjectMetadata: async () => ({}),
      },
      routes: {
        objectDetail: (objectApiName, recordId) => `/${objectApiName}/${recordId}`,
        objectEdit: (objectApiName, recordId) => `/${objectApiName}/${recordId}/edit`,
        objectList: objectApiName => `/${objectApiName}`,
        objectNew: objectApiName => `/${objectApiName}/new`,
      },
      components: {
        ChangeOwnerModal: DummyComponent,
        FileUploadActionModal: DummyComponent,
      },
      getDeleteConfirmationMessage: () => 'Delete?',
      joinMultipicklistSelections: values => values.join(';'),
      logger: {
        warn: vi.fn(),
      },
      systemFileObjectName: '_System_File',
    },
    runtimeRenderers: {
      convertLeadApi: {
        query: async () => ({ records: [] }),
        previewStandardAction: async () => ({}),
        executeStandardAction: async () => ({}),
        getSchema: async () => ({ fields: [] }),
        warn: vi.fn(),
      },
      createRecordFromSourceApi: {
        executeStandardAction: async () => ({}),
        previewStandardAction: async () => ({}),
      },
      taskActivityApi: {
        executeStandardAction: async () => ({}),
      },
      sendRecordEmailApi: {
        getConnectedGmail: async () => ({ connected: false }),
        getRecordEmailRecipient: async () => ({}),
        beginGmailConnection: vi.fn(),
        uploadAttachment: async () => ({ Id: 'file-version', Name: 'file.txt', Size: 1, Mime: 'text/plain' }),
        executeStandardAction: async () => ({}),
      },
      lookupComponent: DummyLookup,
      useTaskObjectMetadata: () => ({ metadata: { name: 'Task', fields: [] } }),
      useCurrentUserId: () => '005-test',
    },
    activityTimeline: {
      activityQueryApi: {
        query: async () => ({ records: [] }),
      },
      useObjectMetadata: () => ({ metadata: { name: 'Account', fields: [] } }),
      useStandardActions: () => [],
      getActionRenderer: () => undefined,
      resolvePlacedStandardActions: () => [],
      filterVisibleActionsForRecord: actions => actions,
      useNavigation: () => ({
        navigateToRecord: vi.fn(),
        navigateToNewRecord: vi.fn(),
      }),
    },
    recordActivityFeed: {
      activityQueryApi: {
        query: async () => ({ records: [] }),
      },
      useNavigation: () => ({
        navigateToRecord: vi.fn(),
        navigateToNewRecord: vi.fn(),
      }),
    },
    recordChildBarChart: DummyComponent as React.FC<HostPanelProps>,
    dashboardWidgets: {
      registry: {
        registerPolicy: vi.fn(),
        registerWidget: vi.fn(),
      },
      widgets: {
        MetricWidget: DummyWidget,
        ChartWidget: DummyWidget,
        FunnelWidget: DummyWidget,
        GaugeWidget: DummyWidget,
        IcicleWidget: DummyWidget,
      },
    },
    metadata: {
      platformFieldTypes: {},
      registerFieldTypes: vi.fn(),
    },
    setup: {
      components: setupComponents,
      registerComponent: vi.fn(),
      registerPages: vi.fn(),
      registerChildRoutes: vi.fn(),
    },
    uiFields: {
      SearchableLookup: DummyLookup,
      RecordLink: () => null,
      decodeHtmlEntities: value => value,
      formatCurrency: value => String(value),
      formatDate: value => value,
      formatDateTime: value => value,
      getRecordValue: (record, fieldName) => record?.[fieldName],
      parseMultipicklistSelections: value => (Array.isArray(value) ? value.map(String) : []),
      joinMultipicklistSelections: values => values.join(';'),
      registerFieldSurfacePolicy: vi.fn(),
      registerFieldRenderer: vi.fn(),
      registerFieldRendererByName: vi.fn(),
      registerFieldInput: vi.fn(),
    },
  };

  return {
    host,
    surfaceSpies,
    rendererSpies,
    actionRegistry,
  };
}

describe('CRM frontend host adapter', () => {
  it('creates one CRM host contribution from source routes and host renderer adapters', () => {
    const { host, rendererSpies } = createHost();

    const registration = createCrmFrontendHostAdapterRegistration(host);

    expect(registration.contribution.id).toBe(CRM_FRONTEND_APP_ID);
    expect(registration.contribution.routes.length).toBeGreaterThan(5);
    expect(registration.contribution.actionRenderers.map(([id]) => id)).toEqual([
      CRM_LEAD_CONVERT_RENDERER_ID,
      CRM_CREATE_RECORD_FROM_SOURCE_RENDERER_ID,
      CRM_TASK_ACTIVITY_QUICK_CREATE_RENDERER_ID,
      CRM_EMAIL_COMPOSE_RENDERER_ID,
    ]);
    expect(registration.contribution.panelRenderers.map(([id]) => id)).toEqual([
      CRM_ACTIVITY_TIMELINE_RENDERER_ID,
      CRM_RECORD_ACTIVITY_FEED_RENDERER_ID,
      CRM_RECORD_CHILD_BAR_CHART_RENDERER_ID,
      CRM_CONVERTED_LEAD_BANNER_RENDERER_ID,
    ]);
    expect(rendererSpies.createActionRendererHostAdapter).toHaveBeenCalledTimes(4);
    expect(rendererSpies.createRecordPanelHostAdapter).toHaveBeenCalledTimes(4);
  });

  it('registers CRM frontend surfaces, registries, renderers, and contribution in one sequence', () => {
    const { host, surfaceSpies, rendererSpies, actionRegistry } = createHost();

    const registration = registerCrmFrontendHostAdapter(host);

    expect(surfaceSpies.registerAppConfigFrontendOwner)
      .toHaveBeenCalledWith(CRM_APP_CONFIG_FRONTEND_OWNER_SOURCE, CRM_FRONTEND_APP_ID, { fallback: true });
    expect(actionRegistry.register).toHaveBeenCalledTimes(CRM_ACTION_HANDLER_TYPES.length);
    expect(host.metadata.registerFieldTypes).toHaveBeenCalled();
    expect(host.uiFields.registerFieldSurfacePolicy).toHaveBeenCalled();
    expect(host.dashboardWidgets.registry.registerPolicy).toHaveBeenCalled();
    expect(host.setup.registerPages).toHaveBeenCalledWith(CRM_FRONTEND_APP_ID, expect.any(Array));
    expect(surfaceSpies.registerHostApp).toHaveBeenCalledWith(registration.contribution);
    expect(rendererSpies.registerActionRendererHostAdapter).toHaveBeenCalledWith(
      CRM_LEAD_CONVERT_RENDERER_ID,
      registration.actionRenderers.leadConvert,
    );
    expect(rendererSpies.registerActionRendererHostAdapter).toHaveBeenCalledWith(
      CRM_CREATE_RECORD_FROM_SOURCE_RENDERER_ID,
      registration.actionRenderers.createRecordFromSource,
    );
    expect(rendererSpies.registerActionRendererHostAdapter).toHaveBeenCalledWith(
      CRM_TASK_ACTIVITY_QUICK_CREATE_RENDERER_ID,
      registration.actionRenderers.taskActivityQuickCreate,
    );
    expect(rendererSpies.registerActionRendererHostAdapter).toHaveBeenCalledWith(
      CRM_EMAIL_COMPOSE_RENDERER_ID,
      registration.actionRenderers.emailCompose,
    );
    expect(rendererSpies.registerRecordPanelHostAdapter).toHaveBeenCalledWith(
      CRM_ACTIVITY_TIMELINE_RENDERER_ID,
      registration.panelRenderers.activityTimeline,
    );
    expect(rendererSpies.registerRecordPanelHostAdapter).toHaveBeenCalledWith(
      CRM_RECORD_ACTIVITY_FEED_RENDERER_ID,
      registration.panelRenderers.recordActivityFeed,
    );
    expect(rendererSpies.registerRecordPanelHostAdapter).toHaveBeenCalledWith(
      CRM_RECORD_CHILD_BAR_CHART_RENDERER_ID,
      registration.panelRenderers.recordChildBarChart,
    );
    expect(rendererSpies.registerRecordPanelHostAdapter).toHaveBeenCalledWith(
      CRM_CONVERTED_LEAD_BANNER_RENDERER_ID,
      registration.panelRenderers.convertedLeadBanner,
    );
  });
});
