import React, { useMemo } from 'react';
import {
  CrmActivityTimeline,
  type CrmActivityTimelineAction,
} from './activity-timeline';
import {
  useCrmActivities,
  type CrmActivityItem,
  type CrmActivityQueryApi,
} from './activity-timeline-data';
import {
  buildCRMActivityTimelineRecordContext,
  crmActivityTimelineSurface,
  isCrmEmailTaskActivity,
} from './activity-timeline-surface';

export interface CrmActivityTimelineStandardAction {
  name: string;
  label: string;
  category?: string;
  rendererId?: string;
  targetObjects?: readonly string[];
}

export interface CrmActivityTimelineActionConfig {
  name?: string;
  actionName?: string;
  [key: string]: unknown;
}

export interface CrmActivityTimelineObjectMetadata {
  name: string;
  [key: string]: unknown;
}

export interface CrmActivityTimelineLayout<TActionConfig extends CrmActivityTimelineActionConfig> {
  quickActions?: readonly TActionConfig[] | null;
  quickActionsAuthored?: boolean;
}

export interface CrmActivityTimelinePanelProps<
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
  TActionConfig extends CrmActivityTimelineActionConfig = CrmActivityTimelineActionConfig,
> {
  recordId: string;
  record: Record<string, unknown> | null | undefined;
  objectMetadata: TObjectMetadata;
  recordName?: string;
  layout?: CrmActivityTimelineLayout<TActionConfig> | null;
  /** The record-page layout entry that placed this panel. The host passes it to
   *  every record panel (RecordChildBarChartPanel reads panel.config the same
   *  way); declared here so CRM panels can read their own authored config. */
  panel?: { config?: Record<string, unknown> | null } | null;
}

export interface CrmActivityTimelineRendererHost<
  TStandardAction extends CrmActivityTimelineStandardAction,
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
> {
  navigateToRecord: (targetObjectApiName: string, targetRecordId: string) => void;
  navigateToNewRecord: (targetObjectApiName: string) => void;
  refreshRecord: () => void;
}

export interface CrmActivityTimelineNavigation {
  navigateToRecord: (objectApiName: string, recordId: string) => void;
  navigateToNewRecord: (objectApiName: string) => void;
}

export type CrmActivityTimelineActionRenderer<
  TStandardAction extends CrmActivityTimelineStandardAction,
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
> = React.ComponentType<{
  isOpen: boolean;
  onClose: () => void;
  action: TStandardAction;
  record: Record<string, unknown>;
  objectMetadata: TObjectMetadata;
  host: CrmActivityTimelineRendererHost<TStandardAction, TObjectMetadata>;
}>;

export interface CrmActivityTimelineAdapterOptions<
  TStandardAction extends CrmActivityTimelineStandardAction,
  TActionConfig extends CrmActivityTimelineActionConfig,
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
> {
  activityQueryApi: CrmActivityQueryApi;
  useObjectMetadata: (objectApiName: string) => {
    metadata?: TObjectMetadata | null;
  };
  useStandardActions: () => readonly TStandardAction[];
  getActionRenderer: (
    rendererId: string | undefined,
  ) => CrmActivityTimelineActionRenderer<TStandardAction, TObjectMetadata> | null | undefined;
  resolvePlacedStandardActions: (
    quickActions: readonly TActionConfig[] | undefined,
    standardActions: readonly TStandardAction[],
    objectApiName: string,
  ) => readonly TStandardAction[];
  filterVisibleActionsForRecord: (
    actions: readonly TStandardAction[],
    hostRecord: Record<string, unknown> | null | undefined,
  ) => readonly TStandardAction[];
  useNavigation: () => CrmActivityTimelineNavigation;
}

export interface CrmActivityTimelineProps<
  TActionConfig extends CrmActivityTimelineActionConfig,
> {
  recordId: string;
  objectApiName: string;
  recordName?: string;
  quickActions?: readonly TActionConfig[] | null;
  quickActionsAuthored?: boolean;
  hostRecord?: Record<string, unknown> | null;
}

export interface CrmActivityTimelineAdapters<
  TPanelProps,
  TActionConfig extends CrmActivityTimelineActionConfig,
> {
  ActivityTimeline: React.FC<CrmActivityTimelineProps<TActionConfig>>;
  CRMActivityTimelinePanel: React.FC<TPanelProps>;
}

function actionWithRendererState<
  TStandardAction extends CrmActivityTimelineStandardAction,
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
>(
  action: TStandardAction,
  getActionRenderer: (
    rendererId: string | undefined,
  ) => CrmActivityTimelineActionRenderer<TStandardAction, TObjectMetadata> | null | undefined,
): CrmActivityTimelineAction & TStandardAction {
  const renderer = getActionRenderer(action.rendererId);
  return {
    ...action,
    disabled: !renderer,
    disabledReason: !renderer
      ? `No renderer registered for ${action.rendererId || action.name}`
      : undefined,
  };
}

function escapeCrmActivitySoqlValue(value: string): string {
  return value.replace(/'/g, "''");
}

function schemaResultIsAvailable(result: unknown): boolean {
  if (result == null) {
    return false;
  }
  if (typeof result === 'object' && 'schema' in result) {
    return !!(result as { schema?: unknown | null }).schema;
  }
  return true;
}

async function canOpenActivityDetailObject(
  api: CrmActivityQueryApi,
  objectApiName: string,
): Promise<boolean> {
  if (!api.getSchema) {
    return true;
  }
  try {
    return schemaResultIsAvailable(await api.getSchema(objectApiName));
  } catch {
    return false;
  }
}

export async function resolveEmailMessageDetailTarget(
  api: CrmActivityQueryApi,
  item: CrmActivityItem,
): Promise<{ objectApiName: string; recordId: string } | null> {
  const detail = crmActivityTimelineSurface.emailDetail;
  if (!item.id || !isCrmEmailTaskActivity(item)) {
    return null;
  }
  if (item.detailObjectApiName === detail.objectApiName && item.detailRecordId) {
    return { objectApiName: detail.objectApiName, recordId: item.detailRecordId };
  }
  if (!(await canOpenActivityDetailObject(api, detail.objectApiName))) {
    return null;
  }

  try {
    const records = await api.query({
      objectApiName: detail.objectApiName,
      selectFields: [...detail.selectFields],
      filterExpr: `${detail.activityLookupField} = '${escapeCrmActivitySoqlValue(item.id)}'`,
      sortField: 'Id',
      sortDirection: 'ASC',
      limit: 1,
    });
    const emailMessageId = records[0]?.Id != null ? String(records[0].Id) : '';
    return emailMessageId
      ? { objectApiName: detail.objectApiName, recordId: emailMessageId }
      : null;
  } catch {
    return null;
  }
}

export function createCrmActivityTimelineAdapters<
  TStandardAction extends CrmActivityTimelineStandardAction,
  TActionConfig extends CrmActivityTimelineActionConfig,
  TObjectMetadata extends CrmActivityTimelineObjectMetadata,
  TPanelProps extends CrmActivityTimelinePanelProps<TObjectMetadata, TActionConfig>,
>({
  activityQueryApi,
  useObjectMetadata,
  useStandardActions,
  getActionRenderer,
  resolvePlacedStandardActions,
  filterVisibleActionsForRecord,
  useNavigation,
}: CrmActivityTimelineAdapterOptions<TStandardAction, TActionConfig, TObjectMetadata>): CrmActivityTimelineAdapters<TPanelProps, TActionConfig> {
  const ActivityTimeline: React.FC<CrmActivityTimelineProps<TActionConfig>> = ({
    recordId,
    objectApiName,
    recordName,
    quickActions,
    quickActionsAuthored,
    hostRecord,
  }) => {
    const { metadata: objectMetadata } = useObjectMetadata(objectApiName);
    const activitiesResult = useCrmActivities(recordId, objectApiName, activityQueryApi, crmActivityTimelineSurface);
    const standardActions = useStandardActions();
    const { navigateToRecord, navigateToNewRecord } = useNavigation();

    const legacyActivityActions = useMemo(() => {
      const normalizedObject = objectApiName.toLowerCase();
      return standardActions.filter(action => {
        if (action.category !== crmActivityTimelineSurface.actionCategory) {
          return false;
        }
        const targets = action.targetObjects || [];
        if (targets.length === 0) {
          return false;
        }
        return targets.some(target => target.toLowerCase() === normalizedObject);
      });
    }, [objectApiName, standardActions]);

    const resolvedActivityActions = useMemo(() => {
      const hasAuthoredQuickActions = quickActionsAuthored === true || (quickActions?.length ?? 0) > 0;
      if (hasAuthoredQuickActions) {
        return resolvePlacedStandardActions(
          quickActions ?? undefined,
          standardActions,
          objectApiName,
        );
      }
      return legacyActivityActions;
    }, [
      legacyActivityActions,
      objectApiName,
      quickActions,
      quickActionsAuthored,
      resolvePlacedStandardActions,
      standardActions,
    ]);

    const activityActions = useMemo(
      () => filterVisibleActionsForRecord(resolvedActivityActions, hostRecord)
        .map(action => actionWithRendererState(action, getActionRenderer)),
      [filterVisibleActionsForRecord, getActionRenderer, hostRecord, resolvedActivityActions],
    );

    const host = useMemo<CrmActivityTimelineRendererHost<TStandardAction, TObjectMetadata>>(() => ({
      navigateToRecord,
      navigateToNewRecord,
      refreshRecord: activitiesResult.refresh,
    }), [activitiesResult.refresh, navigateToNewRecord, navigateToRecord]);

    const currentRecord = useMemo(() => (
      buildCRMActivityTimelineRecordContext(
        crmActivityTimelineSurface,
        recordId,
        objectApiName,
        recordName,
      ).record
    ), [objectApiName, recordId, recordName]);

    return (
      <CrmActivityTimeline
        activitiesResult={activitiesResult}
        actions={activityActions}
        onOpenActivityDetail={async (item) => {
          const emailMessageTarget = await resolveEmailMessageDetailTarget(activityQueryApi, item);
          if (emailMessageTarget) {
            navigateToRecord(emailMessageTarget.objectApiName, emailMessageTarget.recordId);
            return;
          }
          if (item.detailObjectApiName && item.id) {
            navigateToRecord(item.detailObjectApiName, item.id);
          }
        }}
        renderAction={(action, onClose) => {
          const standardAction = action as TStandardAction;
          const Renderer = getActionRenderer(standardAction.rendererId);
          if (!Renderer || !objectMetadata) {
            return null;
          }
          return React.createElement(Renderer, {
            isOpen: true,
            onClose,
            action: standardAction,
            record: currentRecord,
            objectMetadata,
            host,
          });
        }}
      />
    );
  };

  const CRMActivityTimelinePanel: React.FC<TPanelProps> = ({
    recordId,
    record,
    objectMetadata,
    recordName,
    layout,
  }) => (
    <ActivityTimeline
      recordId={recordId}
      objectApiName={objectMetadata.name}
      recordName={recordName}
      quickActions={layout?.quickActions}
      quickActionsAuthored={layout?.quickActionsAuthored}
      hostRecord={record}
    />
  );

  return {
    ActivityTimeline,
    CRMActivityTimelinePanel,
  };
}
