/* eslint-disable react-refresh/only-export-components */
import React, { useMemo } from 'react';
import { Navigate, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import DOMPurify from 'dompurify';
import {
    registerCrmFrontendHostAdapter,
    type CrmFrontendHostAdapterHost,
} from './host-adapter';
import {
    type CrmActionDataApi,
    type CrmActionHandler,
    type CrmActionHandlerHost,
    type CrmActionRecord,
} from './action-handlers';
import type {
    CrmActivityTimelineActionRenderer,
    CrmActivityTimelineObjectMetadata,
    CrmActivityTimelinePanelProps,
} from './activity-timeline-adapter';
import type { CrmActivityQueryApi } from './activity-timeline-data';
import type { CrmConvertLeadApi } from './convert-lead-modal';
import type { CrmCreateRecordFromSourceApi } from './create-record-from-source-modal';
import type { CrmSendRecordEmailApi } from './send-record-email-modal';
import { filesAPI } from './platform/infrastructure/api/files';
import type { CrmMetadataFieldTypeId } from './metadata-field-type-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_TASK_ACTIVITY_QUICK_CREATE_RENDERER_ID,
} from './renderer-ids';
import {
    CRM_FRONTEND_APP_ID,
} from './route-surface-policy';
import {
    createCrmRuntimeRouteBindings,
} from './runtime-route-bindings';
import type {
    CrmRuntimeActionRendererProps,
    CrmRuntimeRecordPanelProps,
} from './runtime-renderer-adapters';
import type { CrmTaskActivityApi } from './task-activity-quick-create-modal';
import type {
    CrmRecordLinkProps,
    CrmSearchableLookupProps,
    CrmUiFieldInputProps,
    CrmUiFieldRendererProps,
} from './ui-field-components';
import {
    ChartWidget,
    FunnelWidget,
    GaugeWidget,
    IcicleWidget,
    MetricWidget,
} from './dashboard-widgets';
import { RecordChildBarChartPanel } from './platform/features/record/panels/RecordChildBarChartPanel';
import {
    crmDashboardWidgets,
} from './dashboard-route';
import {
    crmShellComponents,
} from './shell-components';
import {
    CrmChangeOwnerModal,
    CrmFileUploadActionModal,
    createCrmActionRendererHostAdapter,
    createCrmRecordPanelHostAdapter,
    crmRuntimeActionRegistry,
    filterCrmVisibleActionsForRecord as filterCrmVisibleActionsForRecordSource,
    getCrmRecordDeleteConfirmationMessage,
    getCrmRegisteredActionRenderer,
    registerCrmActionRendererHostAdapter,
    registerCrmEmailAlertRecipientTypes,
    registerCrmRecordPanelHostAdapter,
    resolveCrmPlacedStandardActions as resolveCrmPlacedStandardActionsSource,
    type CrmAppActionHostRenderer,
    type CrmAppActionRendererProps,
    type CrmAppRecordPanelHostRenderer,
    type CrmRecordPanelRendererProps,
    type CrmRuntimeActionHandler,
} from './runtime-registry-bindings';
import {
    registerHostApp,
    registerHostAuthPresentation,
    registerHostExternalIdentityProvider,
    registerHostFrontendRuntimeServices,
    registerHostSessionLifecycleListener,
    registerHostThemeContribution,
    type HostExternalIdentityProvider,
    type Theme,
} from '@nexuscrm/host-sdk';
import { BootScreen } from './platform/components/auth/BootScreen';
import { LoginScreen } from './platform/components/auth/LoginScreen';
import { OAuthCallback } from './platform/components/auth/OAuthCallback';
import { ResetPasswordPage } from './platform/components/auth/ResetPasswordPage';
import { SearchableLookup } from './platform/components/form/SearchableLookup';
import { RecordLink } from './platform/components/RecordLink';
import { ROUTES } from './platform/core/constants/Routes';
import {
    FIELD_TYPES,
    type FieldType,
} from './platform/core/constants/SchemaDefinitions';
import { registerAppConfigFrontendOwner } from '@nexuscrm/host-sdk';
import {
    registerAppNavigationCapabilities,
} from './platform/core/navigation/appNavigationCapabilities';
import {
    registerSetupPageCapabilities,
    registerSetupPageChildRoutes,
    registerSetupSurface,
} from '@nexuscrm/host-sdk';
import { Logger } from './platform/core/services/Logger';
import {
    formatCurrency,
    formatDate,
    formatDateTime,
    registerCurrencyDisplayPolicy,
} from './platform/core/utils/formatting';
import { blockRemoteContent, decodeHtmlEntities } from './platform/core/utils/htmlUtils';
import { getRecordValue } from './platform/core/utils/recordValue';
import { SYSTEM_TABLE_NAMES } from '@nexuscrm/generated-schema';
import { actionAPI, type StandardAction } from './platform/infrastructure/api/actions';
import { authAPI } from './platform/infrastructure/api/auth';
import { apiClient } from './platform/infrastructure/api/client';
import { clearCurrentUserId, seedCurrentUserId } from './platform/infrastructure/api/approvals';
import { dataAPI } from './platform/infrastructure/api/data';
import { effectivePermissionsAPI } from './platform/infrastructure/api/effectivePermissions';
import { metadataAPI } from './platform/infrastructure/api/metadata';
import { getInternalObjectMetadata, getSchema } from './platform/infrastructure/api/schemas';
import { uiAPI } from './platform/infrastructure/api/ui';
import { registerStandardPageTabs } from './platform/core/navigation/standardPageTabs';
import {
    UIRegistry,
    type FieldInputProps,
    type FieldRendererProps,
} from './platform/registries/FieldUiRegistry';
import { CrmLayout as CrmShell } from './platform/components/shell/CrmLayout';
import {
    joinMultipicklistSelections,
    parseMultipicklistSelections,
    registerFieldSurfacePolicy,
} from './platform/core/metadata/fieldSurfacePolicy';
import { useObjectMetadata } from './platform/infrastructure/api/objectMetadataHooks';
import { MetadataRegistry, type FieldTypeDefinition, type FilterOperator } from './platform/registries/MetadataRegistry';
import { useOptionalRuntime } from '@nexuscrm/host-sdk';
import { ComponentRegistry, type RegisteredComponent } from './platform/registries/ComponentRegistry';
import { SetupRegistry, type SetupPageDefinition } from './platform/registries/SetupRegistry';
import type { ActionConfig, SObject, SObjectDescribe } from './platform/types';

export { crmDashboardWidgets };

const Setup = React.lazy(() => import('./platform/components/setup/Setup').then(m => ({ default: m.Setup })));
const crmRuntimeRouteBindings = createCrmRuntimeRouteBindings({
    setupComponent: Setup as RegisteredComponent,
});

export {
    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_TASK_ACTIVITY_QUICK_CREATE_RENDERER_ID,
};

function base64UrlEncode(value: string): string {
    return btoa(value)
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');
}

function getStringProperty(payload: Record<string, unknown>, key: string): string | undefined {
    const value = payload[key];
    return typeof value === 'string' && value.trim() ? value : undefined;
}

const SALESFORCE_EXTERNAL_IDENTITY_PROVIDER = {
    id: 'salesforce',
    label: 'Salesforce',
    description: 'Connect directly to Salesforce org',
    buttonLabel: 'Salesforce',
    defaultEnvironmentId: 'production',
    storageKeyPrefix: 'sf_connected_app',
    environments: [
        {
            id: 'production',
            label: 'Production',
            authorizationBaseUrl: 'https://login.salesforce.com',
            oauthProxyBasePath: '/sf-oauth/production',
            badgeTone: 'success',
        },
        {
            id: 'sandbox',
            label: 'Sandbox',
            authorizationBaseUrl: 'https://test.salesforce.com',
            oauthProxyBasePath: '/sf-oauth/sandbox',
            badgeTone: 'warning',
        },
    ],
    customDomain: {
        label: 'Custom Domain',
        placeholder: 'my-domain',
        inputPrefix: 'https://',
        inputSuffix: '.my.salesforce.com',
        proxyEnvironmentId: 'production',
        buildAuthorizationBaseUrl: (domain: string) => `https://${domain}.my.salesforce.com`,
    },
    defaultTokenInstanceUrl: 'https://your-org.my.salesforce.com',
    tokenInstanceUrlPlaceholder: 'https://yourcompany.my.salesforce.com',
    defaultClientSecret: process.env.REACT_APP_SALESFORCE_CLIENT_SECRET || '',
    legacyStorageKeys: {
        clientId: ['sf_connected_app_client_id'],
        clientSecret: ['sf_connected_app_client_secret'],
        environment: ['sf_environment'],
    },
    instanceProxyBasePath: '/sf-instance/',
    isExternalRequestUrl: (url: string) => {
        if (url.startsWith('/sf-instance/')) {
            return true;
        }

        try {
            const requestURL = new URL(url);
            if (requestURL.protocol !== 'https:') {
                return false;
            }
            const hostname = requestURL.hostname.toLowerCase();
            return hostname === 'salesforce.com'
                || hostname.endsWith('.salesforce.com')
                || hostname === 'force.com'
                || hostname.endsWith('.force.com');
        } catch {
            return false;
        }
    },
    buildAuthorizeUrl: (request) => {
        const params = new URLSearchParams({
            client_id: request.clientId,
            redirect_uri: request.redirectUri,
            response_type: request.responseType,
            code_challenge: request.codeChallenge,
            code_challenge_method: 'S256',
            state: request.state,
        });
        return `${request.authorizationBaseUrl}/services/oauth2/authorize?${params.toString()}`;
    },
    buildTokenRequest: (request) => {
        const params = new URLSearchParams({
            grant_type: 'authorization_code',
            client_id: request.clientId,
            redirect_uri: request.redirectUri,
            code: request.code,
            code_verifier: request.codeVerifier,
        });
        if (request.clientSecret) {
            params.set('client_secret', request.clientSecret);
        }
        return {
            url: `${request.oauthProxyBasePath}/services/oauth2/token`,
            init: {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: params.toString(),
            },
        };
    },
    parseTokenResponse: (payload: unknown) => {
        const token = payload && typeof payload === 'object'
            ? payload as Record<string, unknown>
            : {};
        return {
            accessToken: getStringProperty(token, 'access_token') ?? '',
            instanceUrl: getStringProperty(token, 'instance_url') ?? '',
        };
    },
    buildUserInfoRequest: (request) => ({
        url: `${request.oauthProxyBasePath}/services/oauth2/userinfo`,
        init: {
            headers: {
                Authorization: `Bearer ${request.accessToken}`,
            },
        },
    }),
    buildInstanceProxyBaseUrl: (instanceUrl: string) => `/sf-instance/${base64UrlEncode(instanceUrl)}`,
    parseUserInfo: (payload: unknown) => {
        const info = payload && typeof payload === 'object'
            ? payload as Record<string, unknown>
            : {};
        return {
            id: getStringProperty(info, 'user_id') ?? getStringProperty(info, 'sub') ?? '',
            name: getStringProperty(info, 'name') ?? getStringProperty(info, 'nickname') ?? '',
            email: getStringProperty(info, 'email') ?? getStringProperty(info, 'preferred_username') ?? '',
            profileId: getStringProperty(info, 'profile_id') ?? '',
            roleId: getStringProperty(info, 'role_id'),
            permissions: info.permissions && typeof info.permissions === 'object'
                ? info.permissions as Record<string, boolean>
                : undefined,
        };
    },
} satisfies HostExternalIdentityProvider;

export function registerSalesforceExternalIdentityProvider(): void {
    registerHostExternalIdentityProvider(SALESFORCE_EXTERNAL_IDENTITY_PROVIDER);
}

const CRM_THEME: Theme = {
    id: 'salesforce-crm-standard',
    name: 'Salesforce CRM Standard',
    colors: {
        brand: '#2563EB',
        brandLight: '#DBEAFE',
        brandDark: '#1E40AF',
        secondary: '#64748B',
        success: '#10B981',
        warning: '#F59E0B',
        danger: '#EF4444',
        background: '#F8FAFC',
        surface: '#FFFFFF',
        text: '#0F172A',
        textSecondary: '#64748B',
        border: '#E2E8F0',
    },
    density: 'comfortable',
};

export function registerCrmPresentationContributions(): void {
    registerHostAuthPresentation({
        id: 'salesforce-crm-standard',
        boot: BootScreen,
        login: LoginScreen,
        oauthCallback: OAuthCallback,
        resetPassword: ResetPasswordPage,
        oauthProtocols: [{
            providerId: 'google',
            initialize: request => authAPI.initializeGoogleOAuth(
                request.clientId,
                request.authorizeContinue,
            ),
            exchange: (code, state, context) => authAPI.exchangeGoogleToken(
                code,
                state ?? '',
                context.sessionControl,
            ),
        }],
    });
    registerHostThemeContribution({
        id: 'salesforce-crm-standard',
        theme: CRM_THEME,
    });
}

function createRedirectRoute(to: string): React.FC {
    return function CRMRedirectRoute() {
        return <Navigate to={to} replace />;
    };
}

const crmActionRegistryAdapter = {
    register: (type: string, handler: CrmActionHandler) => {
        crmRuntimeActionRegistry.register(type, handler as CrmRuntimeActionHandler);
    },
    get: (type: string) => {
        return crmRuntimeActionRegistry.get(type) as CrmActionHandler | undefined;
    },
} satisfies CrmActionHandlerHost['actionRegistry'];

const crmActionDataApi: CrmActionDataApi = {
    executeAction: (actionId, payload) => dataAPI.executeAction(actionId, payload),
    executeStandardCreateRecord: (actionId, sourceRecordId) =>
        dataAPI.executeStandardCreateRecord(actionId, sourceRecordId),
    deleteRecord: (objectApiName, recordId) => dataAPI.deleteRecord(objectApiName, recordId),
    updateRecord: (objectApiName, recordId, updates) => dataAPI.updateRecord(objectApiName, recordId, updates),
    getRecord: <T extends CrmActionRecord = CrmActionRecord>(
        objectApiName: string,
        recordId: string,
    ): Promise<T | null | undefined> => dataAPI.getRecord<T>(objectApiName, recordId),
    getInternalObjectMetadata,
};

const convertLeadApi: CrmConvertLeadApi = {
    query: request => dataAPI.query(request),
    previewStandardAction: (actionPreviewEndpoint, input) => (
        dataAPI.previewStandardAction(actionPreviewEndpoint, input)
    ),
    executeStandardAction: (actionEndpoint, inputs) => (
        dataAPI.executeStandardAction(actionEndpoint, inputs)
    ),
    getSchema: objectApiName => getSchema(objectApiName),
    warn: (message, context, error) => Logger.warn(message, context, error),
};

const createRecordFromSourceApi: CrmCreateRecordFromSourceApi = {
    executeStandardAction: (...args) => dataAPI.executeStandardAction(...args),
    previewStandardAction: (...args) => dataAPI.previewStandardAction(...args),
};

const taskActivityApi: CrmTaskActivityApi = {
    executeStandardAction: (...args) => dataAPI.executeStandardAction(...args),
};

function requireAbsoluteHttpsUrl(value: unknown, description: string): string {
    if (typeof value !== 'string' || value.trim() === '') {
        throw new Error(`${description} must be an absolute HTTPS URL.`);
    }
    let parsed: URL;
    try {
        parsed = new URL(value);
    } catch {
        throw new Error(`${description} must be an absolute HTTPS URL.`);
    }
    if (parsed.protocol !== 'https:') {
        throw new Error(`${description} must be an absolute HTTPS URL.`);
    }
    return parsed.href;
}

const sendRecordEmailApi: CrmSendRecordEmailApi = {
    getConnectedGmail: () => apiClient.request('/api/connected-mailboxes/gmail', {
        method: 'GET',
        useConfiguredBackendOrigin: true,
    }),
    getRecordEmailRecipient: async (objectApiName, recordId) => (
        await dataAPI.getRecord(objectApiName, recordId) ?? {}
    ),
    uploadAttachment: async (file, recordId, objectApiName, idempotencyKey) => {
        const uploaded = await filesAPI.uploadRecordFile(file, {
            parentId: recordId,
            parentObjectApiName: objectApiName,
            attachmentKind: 'email',
            idempotencyKey,
        });
        return {
            Id: uploaded.Id,
            Name: String(uploaded.Name ?? file.name),
            Size: uploaded.Size,
            Mime: typeof uploaded.Mime === 'string' && uploaded.Mime
                ? uploaded.Mime
                : file.type || 'application/octet-stream',
        };
    },
    beginGmailConnection: async () => {
        const returnTo = `${window.location.pathname}${window.location.search}`;
        const start = await apiClient.request<{ authorizationUrl: string }>(
            `/api/connected-mailboxes/gmail/authorize?returnTo=${encodeURIComponent(returnTo)}`,
            {
                method: 'GET',
                useConfiguredBackendOrigin: true,
            },
        );
        window.location.assign(requireAbsoluteHttpsUrl(
            start.authorizationUrl,
            'Gmail authorization URL',
        ));
    },
    executeStandardAction: (actionEndpoint, inputs, signal) => (
        dataAPI.executeStandardAction(actionEndpoint, inputs, signal, 'runtime')
    ),
};

const activityQueryApi: CrmActivityQueryApi = {
    query: request => dataAPI.query(request),
    queryAll: (soql: string) => dataAPI.queryAll(soql) as Promise<Record<string, unknown>[]>,
    getSchema: objectApiName => getSchema(objectApiName),
};

function useCrmStandardActions(): readonly StandardAction[] {
    const { data } = useQuery({
        queryKey: ['actions', 'standard'],
        queryFn: () => actionAPI.getStandardActions(),
        staleTime: 0,
    });
    return data?.actions ?? [];
}

function useCrmActivityTimelineNavigation() {
    const navigate = useNavigate();
    return useMemo(() => ({
        navigateToRecord: (objectApiName: string, recordId: string) => (
            navigate(ROUTES.OBJECT.DETAIL(objectApiName, recordId))
        ),
        navigateToNewRecord: (objectApiName: string) => (
            navigate(ROUTES.OBJECT.NEW(objectApiName))
        ),
    }), [navigate]);
}

const useCurrentUserId = (): string | null | undefined => {
    const runtime = useOptionalRuntime();
    return runtime?.user?.Id;
};

type CrmHostActivityObjectMetadata = SObjectDescribe & CrmActivityTimelineObjectMetadata;
type CrmHostActionRendererProps = CrmAppActionRendererProps & CrmRuntimeActionRendererProps;
type CrmHostRecordPanelProps = CrmRecordPanelRendererProps &
    CrmRuntimeRecordPanelProps &
    CrmActivityTimelinePanelProps<CrmHostActivityObjectMetadata, ActionConfig>;

type CrmHostRenderer = CrmAppActionHostRenderer | CrmAppRecordPanelHostRenderer;
type CrmDashboardWidgetComponent = React.FC<unknown>;

const getCrmActionRenderer = (
    rendererId: string | undefined,
): CrmActivityTimelineActionRenderer<StandardAction, CrmHostActivityObjectMetadata> | undefined => (
    getCrmRegisteredActionRenderer(rendererId) as CrmActivityTimelineActionRenderer<StandardAction, CrmHostActivityObjectMetadata> | undefined
);

const useCrmObjectMetadata = (objectApiName: string): {
    metadata?: CrmHostActivityObjectMetadata | null;
} => (
    useObjectMetadata(objectApiName) as { metadata?: CrmHostActivityObjectMetadata | null }
);

const resolveCrmPlacedStandardActions = (
    quickActions: readonly ActionConfig[] | undefined,
    standardActions: readonly StandardAction[],
    objectApiName: string,
): readonly StandardAction[] => (
    resolveCrmPlacedStandardActionsSource(
        quickActions ? [...quickActions] : undefined,
        [...standardActions],
        objectApiName,
    )
);

const filterCrmVisibleActionsForRecord = (
    actions: readonly StandardAction[],
    hostRecord: Record<string, unknown> | null | undefined,
): readonly StandardAction[] => (
    filterCrmVisibleActionsForRecordSource([...actions], hostRecord)
);

const crmFrontendHostAdapter = {
    routeComponents: crmRuntimeRouteBindings.routeComponents,
    createRedirectRoute,
    surfaces: {
        registerAppConfigFrontendOwner: (source, appId, options) => (
            registerAppConfigFrontendOwner(source, appId, options)
        ),
        registerCurrencyDisplayPolicy,
        registerSetupSurface,
        registerSetupPageCapabilities,
        registerAppNavigationCapabilities: (appId, capabilities) => (
            registerAppNavigationCapabilities(
                appId,
                capabilities as Parameters<typeof registerAppNavigationCapabilities>[1],
            )
        ),
        registerEmailAlertRecipientTypes: registerCrmEmailAlertRecipientTypes,
        registerHostApp: (contribution) => registerHostApp({
            ...contribution,
            shell: CrmShell,
            shellComponents: crmShellComponents,
        } as Parameters<typeof registerHostApp>[0]),
        registerStandardPageTabs,
    },
    rendererAdapters: {
        createActionRendererHostAdapter: (component) => (
            createCrmActionRendererHostAdapter(component as React.FC<CrmAppActionRendererProps>) as CrmHostRenderer
        ),
        createRecordPanelHostAdapter: (component) => (
            createCrmRecordPanelHostAdapter(component as React.FC<CrmRecordPanelRendererProps>) as CrmHostRenderer
        ),
        registerActionRendererHostAdapter: (rendererId, renderer) => (
            registerCrmActionRendererHostAdapter(rendererId, renderer as CrmAppActionHostRenderer)
        ),
        registerRecordPanelHostAdapter: (rendererId, renderer) => (
            registerCrmRecordPanelHostAdapter(rendererId, renderer as CrmAppRecordPanelHostRenderer)
        ),
    },
    actionHandlers: {
        actionRegistry: crmActionRegistryAdapter,
        dataApi: crmActionDataApi,
        routes: {
            objectDetail: ROUTES.OBJECT.DETAIL,
            objectEdit: ROUTES.OBJECT.EDIT,
            objectList: ROUTES.OBJECT.LIST,
            objectNew: ROUTES.OBJECT.NEW,
        },
        components: {
            ChangeOwnerModal: CrmChangeOwnerModal as unknown as React.ComponentType<unknown>,
            FileUploadActionModal: CrmFileUploadActionModal as unknown as React.ComponentType<unknown>,
        },
        getDeleteConfirmationMessage: getCrmRecordDeleteConfirmationMessage,
        joinMultipicklistSelections: (values) => joinMultipicklistSelections(values, CRM_FRONTEND_APP_ID),
        logger: {
            warn: (message) => Logger.warn(message),
        },
        systemFileObjectName: SYSTEM_TABLE_NAMES.SYSTEM_FILE,
    },
    runtimeRenderers: {
        convertLeadApi,
        createRecordFromSourceApi,
        taskActivityApi,
        sendRecordEmailApi,
        lookupComponent: SearchableLookup,
        useTaskObjectMetadata: useObjectMetadata,
        useCurrentUserId,
    },
    activityTimeline: {
        activityQueryApi,
        useObjectMetadata: useCrmObjectMetadata,
        useStandardActions: useCrmStandardActions,
        getActionRenderer: getCrmActionRenderer,
        resolvePlacedStandardActions: resolveCrmPlacedStandardActions,
        filterVisibleActionsForRecord: filterCrmVisibleActionsForRecord,
        useNavigation: useCrmActivityTimelineNavigation,
    },
    recordActivityFeed: {
        activityQueryApi,
        useNavigation: useCrmActivityTimelineNavigation,
    },
    recordChildBarChart: RecordChildBarChartPanel as unknown as React.FC<CrmHostRecordPanelProps>,
    dashboardWidgets: {
        registry: crmDashboardWidgets,
        widgets: {
            MetricWidget: MetricWidget as CrmDashboardWidgetComponent,
            ChartWidget: ChartWidget as CrmDashboardWidgetComponent,
            FunnelWidget: FunnelWidget as CrmDashboardWidgetComponent,
            GaugeWidget: GaugeWidget as CrmDashboardWidgetComponent,
            IcicleWidget: IcicleWidget as CrmDashboardWidgetComponent,
        },
    },
    metadata: {
        platformFieldTypes: FIELD_TYPES as Partial<Record<CrmMetadataFieldTypeId, { sqlType?: string }>>,
        registerFieldTypes: definitions => MetadataRegistry.registerFieldTypes(definitions as FieldTypeDefinition[]),
    },
    setup: {
        components: crmRuntimeRouteBindings.setupComponents,
        registerComponent: (name, component) => ComponentRegistry.registerAsNonEmbeddable(name, component),
        registerPages: (appId, pages) => SetupRegistry.registerPages(appId, pages as SetupPageDefinition[]),
        registerChildRoutes: registerSetupPageChildRoutes,
    },
    uiFields: {
        SearchableLookup: SearchableLookup as React.FC<CrmSearchableLookupProps>,
        RecordLink: RecordLink as React.FC<CrmRecordLinkProps>,
        decodeHtmlEntities,
        // DOMPurify handles XSS; blockRemoteContent handles the separate problem
        // of sanitized-but-still-fetching markup (tracking pixels).
        sanitizeHtml: (value: string, options?: { blockRemoteContent?: boolean }) => (
            options?.blockRemoteContent
                ? blockRemoteContent(DOMPurify.sanitize(value))
                : DOMPurify.sanitize(value)
        ),
        htmlToText: (value: string) => decodeHtmlEntities(
            // Insert a separator at block boundaries first, else stripping tags
            // glues adjacent blocks/list items together ("<li>A</li><li>B</li>"
            // -> "AB"). Then drop all remaining markup and collapse whitespace.
            DOMPurify.sanitize(
                value.replace(/<\/(?:p|div|li|h[1-6]|blockquote|tr|ul|ol)>|<br\s*\/?>/gi, ' '),
                { ALLOWED_TAGS: [], ALLOWED_ATTR: [] },
            ),
        ).replace(/\s+/g, ' ').trim(),
        formatCurrency,
        formatDate,
        formatDateTime,
        getRecordValue: (record, fieldName) => getRecordValue(record as SObject | undefined, fieldName),
        parseMultipicklistSelections: (value) => parseMultipicklistSelections(value, CRM_FRONTEND_APP_ID),
        joinMultipicklistSelections: (values) => joinMultipicklistSelections(values, CRM_FRONTEND_APP_ID),
        registerFieldSurfacePolicy: (appId, policy) => registerFieldSurfacePolicy(appId, policy),
        registerFieldRenderer: (type, renderer) => UIRegistry.registerFieldRenderer(
            type,
            renderer as React.FC<FieldRendererProps>,
        ),
        registerFieldRendererByName: (objectApiName, fieldApiName, renderer) => UIRegistry.registerFieldRendererByName(
            objectApiName,
            fieldApiName,
            renderer as React.FC<FieldRendererProps>,
        ),
        registerFieldInput: (type, input) => UIRegistry.registerFieldInput(type, input as React.FC<FieldInputProps>),
    },
} satisfies CrmFrontendHostAdapterHost<
    CrmHostActionRendererProps,
    CrmHostRecordPanelProps,
    CrmHostRenderer,
    RegisteredComponent,
    CrmDashboardWidgetComponent,
    FieldType,
    FilterOperator,
    React.FC<CrmUiFieldRendererProps>,
    React.FC<CrmUiFieldInputProps>,
    StandardAction,
    ActionConfig,
    CrmHostActivityObjectMetadata
>;

let crmFrontendAdapterRegistered = false;
let unregisterCrmSessionLifecycle: (() => void) | null = null;

function registerCrmRuntimeServices(): void {
    registerHostFrontendRuntimeServices(CRM_FRONTEND_APP_ID, {
        appConfig: {
            getApps: (options) => metadataAPI.getApps({
                includeToolingSupplement: options?.includeAdministrativeApps,
            }),
        },
        permissions: {
            getCurrentUserEffectivePermissions: async () => {
                const permissions = await effectivePermissionsAPI.getCurrentUserEffectivePermissions();
                return {
                    system: permissions.system,
                    resources: Object.fromEntries(Object.entries(permissions.objects).map(([key, permission]) => [key, {
                        read: permission.read,
                        create: permission.create,
                        edit: permission.edit,
                        delete: permission.delete,
                        viewAny: permission.viewAll,
                        manageAny: permission.modifyAll,
                    }])),
                };
            },
            normalizeResourceKey: (resourceName) => effectivePermissionsAPI.normalizeObjectKey(resourceName),
            getResourceInfoPermission: (resourceName) => uiAPI.getObjectInfo(resourceName),
        },
    });
}

function registerCrmSessionLifecycle(): void {
    if (unregisterCrmSessionLifecycle) return;
    unregisterCrmSessionLifecycle = registerHostSessionLifecycleListener({
        onAuthenticated: ({ userId }) => { seedCurrentUserId(userId); },
        onCleared: () => { clearCurrentUserId(); },
    });
}

export function registerCRMAppPack(): void {
    if (crmFrontendAdapterRegistered) return;
    registerSalesforceExternalIdentityProvider();
    registerCrmPresentationContributions();
    registerCrmRuntimeServices();
    registerCrmSessionLifecycle();
    registerCrmFrontendHostAdapter(crmFrontendHostAdapter);
    crmFrontendAdapterRegistered = true;
}

export function resetCRMFrontendAdapterRegistrationForTest(): void {
    crmFrontendAdapterRegistered = false;
    unregisterCrmSessionLifecycle?.();
    unregisterCrmSessionLifecycle = null;
}
