type WorkflowConditionKey = string;

export type WorkflowPortalPreviewPrompt = {
  question: string;
  targetType: "client" | "sponsor" | "internal";
};

export type WorkflowPortalPreviewWorkflowInput = {
  workflowTemplateId: string;
  workflowName: string;
  steps: Array<{
    stepName: string;
    appliesWhen: WorkflowConditionKey[];
    responsePrompts: WorkflowPortalPreviewPrompt[];
  }>;
  requiredDocuments: Array<{
    workflowRequiredDocumentId: string;
    documentTypeCode: string;
    displayName: string;
    description: string | null;
    requirementCode: string;
    minimumQuantity: number;
    isRequired: boolean;
    appliesWhen: WorkflowConditionKey[];
  }>;
};

export type WorkflowPortalQuestionPlan = {
  workflowTemplateId: string;
  workflowName: string;
  stepName: string;
  question: string;
  targetType: "client" | "sponsor";
};

export type WorkflowPortalDocumentPlan = {
  workflowTemplateId: string;
  workflowName: string;
  workflowRequiredDocumentId: string;
  documentTypeCode: string;
  displayName: string;
  description: string | null;
  requirementCode: string;
  minimumQuantity: number;
  isRequired: boolean;
  requiredCount: number;
};

export type WorkflowPortalPreviewWorkflowSummary = {
  workflowTemplateId: string;
  workflowName: string;
  questionCount: number;
  documentCount: number;
  matchedStepCount: number;
  matchedDocumentRuleCount: number;
  isActionable: boolean;
  emptyReason: "no_matching_conditions" | "no_client_tasks" | null;
};

export type WorkflowPortalPreview = {
  totals: {
    workflowCount: number;
    questionCount: number;
    documentCount: number;
    actionableWorkflowCount: number;
    emptyWorkflowCount: number;
    isEmpty: boolean;
  };
  workflows: WorkflowPortalPreviewWorkflowSummary[];
  questionPlans: WorkflowPortalQuestionPlan[];
  documentPlans: WorkflowPortalDocumentPlan[];
};

function normalizeWorkflowAppliesWhen(values: WorkflowConditionKey[]) {
  return Array.from(
    new Set(
      values
        .map((item) =>
          String(item ?? "")
            .normalize("NFD")
            .replace(/[\u0300-\u036f]/g, "")
            .trim()
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "_")
            .replace(/^_+|_+$/g, ""),
        )
        .filter((item): item is WorkflowConditionKey => Boolean(item)),
    ),
  );
}

export function normalizeWorkflowConditionValue(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .trim()
    .toLowerCase();
}

export function isTruthyWorkflowConditionValue(value: string) {
  return ["1", "true", "yes", "y", "sim", "s"].includes(
    normalizeWorkflowConditionValue(value),
  );
}

export function matchesWorkflowAppliesWhen(
  appliesWhen: WorkflowConditionKey[],
  factMap: Map<string, string[]>,
) {
  const normalizedConditions = normalizeWorkflowAppliesWhen(appliesWhen);

  if (!normalizedConditions.length) {
    return true;
  }

  const maritalStatuses = (factMap.get("client_marital_status") ?? []).map((item) =>
    normalizeWorkflowConditionValue(item),
  );
  const arrestedValues = (factMap.get("client_has_been_arrested") ?? []).map((item) =>
    normalizeWorkflowConditionValue(item),
  );
  const dependentsValues = (factMap.get("client_has_dependents") ?? []).map((item) =>
    normalizeWorkflowConditionValue(item),
  );

  return normalizedConditions.every((condition) => {
    if (condition === "married") {
      return maritalStatuses.some((item) => ["married", "casado", "casada"].includes(item));
    }

    if (condition === "divorced") {
      return maritalStatuses.some((item) => ["divorced", "divorciado", "divorciada"].includes(item));
    }

    if (condition === "arrested") {
      return arrestedValues.some((item) => isTruthyWorkflowConditionValue(item));
    }

    if (condition === "has_dependents") {
      return dependentsValues.some((item) => isTruthyWorkflowConditionValue(item));
    }

    const genericConditionValues = (factMap.get(condition) ?? []).map((item) =>
      normalizeWorkflowConditionValue(item),
    );

    return genericConditionValues.some((item) => isTruthyWorkflowConditionValue(item));
  });
}

export function resolveWorkflowDependentCount(factMap: Map<string, string[]>) {
  const rawDependentCounts = [
    ...(factMap.get("client_dependents_count") ?? []),
    ...(factMap.get("client_number_of_dependents") ?? []),
  ];
  const numericDependentCounts = rawDependentCounts
    .map((value) => {
      const normalizedValue = String(value ?? "").trim();

      if (!normalizedValue) {
        return null;
      }

      const directNumber = Number(normalizedValue);

      if (Number.isFinite(directNumber) && directNumber > 0) {
        return Math.trunc(directNumber);
      }

      const digitMatch = normalizedValue.match(/\d+/);

      if (!digitMatch) {
        return null;
      }

      const parsedDigits = Number(digitMatch[0]);
      return Number.isFinite(parsedDigits) && parsedDigits > 0 ? Math.trunc(parsedDigits) : null;

    })
    .filter((value): value is number => value !== null);

  if (numericDependentCounts.length > 0) {
    return Math.max(...numericDependentCounts);
  }

  const dependentsValues = (factMap.get("client_has_dependents") ?? []).map((item) =>
    normalizeWorkflowConditionValue(item),
  );

  return dependentsValues.some((item) => isTruthyWorkflowConditionValue(item)) ? 1 : 0;
}

export function buildWorkflowPortalPreview(input: {
  workflows: WorkflowPortalPreviewWorkflowInput[];
  factMap: Map<string, string[]>;
}): WorkflowPortalPreview {
  const dependentCount = resolveWorkflowDependentCount(input.factMap);
  const questionPlans: WorkflowPortalQuestionPlan[] = [];
  const documentPlans: WorkflowPortalDocumentPlan[] = [];
  const workflows: WorkflowPortalPreviewWorkflowSummary[] = input.workflows.map((workflow) => {
    const matchingSteps = workflow.steps.filter((step) =>
      matchesWorkflowAppliesWhen(step.appliesWhen, input.factMap),
    );
    const matchingDocuments = workflow.requiredDocuments.filter((document) =>
      matchesWorkflowAppliesWhen(document.appliesWhen, input.factMap),
    );
    const workflowQuestionPlans = matchingSteps.flatMap((step) =>
      step.responsePrompts
        .filter(
          (prompt): prompt is WorkflowPortalPreviewPrompt & { targetType: "client" | "sponsor" } =>
            prompt.targetType === "client" || prompt.targetType === "sponsor",
        )
        .map((prompt) => ({
          workflowTemplateId: workflow.workflowTemplateId,
          workflowName: workflow.workflowName,
          stepName: step.stepName,
          question: prompt.question,
          targetType: prompt.targetType,
        })),
    );
    const workflowDocumentPlans = matchingDocuments.map((document) => {
      const requiredCount = document.appliesWhen.includes("has_dependents")
        ? Math.max(document.minimumQuantity || 1, dependentCount || 1)
        : Math.max(document.minimumQuantity || 1, 1);

      return {
        workflowTemplateId: workflow.workflowTemplateId,
        workflowName: workflow.workflowName,
        workflowRequiredDocumentId: document.workflowRequiredDocumentId,
        documentTypeCode: document.documentTypeCode,
        displayName: document.displayName,
        description: document.description,
        requirementCode: document.requirementCode,
        minimumQuantity: document.minimumQuantity,
        isRequired: document.isRequired,
        requiredCount,
      };
    });

    questionPlans.push(...workflowQuestionPlans);
    documentPlans.push(...workflowDocumentPlans);

    const questionCount = workflowQuestionPlans.length;
    const documentCount = workflowDocumentPlans.reduce(
      (total, document) => total + document.requiredCount,
      0,
    );
    const isActionable = questionCount > 0 || documentCount > 0;
    const emptyReason = isActionable
      ? null
      : matchingSteps.length === 0 && matchingDocuments.length === 0
        ? "no_matching_conditions"
        : "no_client_tasks";

    return {
      workflowTemplateId: workflow.workflowTemplateId,
      workflowName: workflow.workflowName,
      questionCount,
      documentCount,
      matchedStepCount: matchingSteps.length,
      matchedDocumentRuleCount: matchingDocuments.length,
      isActionable,
      emptyReason,
    };
  });

  const actionableWorkflowCount = workflows.filter((workflow) => workflow.isActionable).length;
  const questionCount = questionPlans.length;
  const documentCount = documentPlans.reduce(
    (total, document) => total + document.requiredCount,
    0,
  );

  return {
    totals: {
      workflowCount: workflows.length,
      questionCount,
      documentCount,
      actionableWorkflowCount,
      emptyWorkflowCount: workflows.length - actionableWorkflowCount,
      isEmpty: questionCount === 0 && documentCount === 0,
    },
    workflows,
    questionPlans,
    documentPlans,
  };
}
