export type ImmigrationCaseType = "EB3" | "EB2" | "FAMILY" | "OTHER";

export type ImmigrationProcessStage =
  | "intake"
  | "petition_stage"
  | "adjustment_package"
  | "review";

export type FormSignal = {
  code: string;
  name?: string | null;
  source?: string | null;
};

export type ImmigrationCaseSignals = {
  forms: FormSignal[];
  relationTypes?: string[];
  factKeys?: string[];
  caseTexts?: string[];
};

export type ImmigrationCaseAnalysis = {
  caseType: ImmigrationCaseType;
  detectedForms: string[];
  missingForms: string[];
  stageOfProcess: ImmigrationProcessStage;
  recommendedNextSteps: string[];
  evidence: {
    formCodes: string[];
    relationTypes: string[];
    factKeys: string[];
  };
};

export type SectionCoverageMetric = {
  sectionName: string;
  filled: number;
  total: number;
  percentFilled: number;
};

export type FormRunMetrics = {
  percentFilled: number;
  percentHighConfidence: number;
  questionsNeeded: number;
  reuseFromOtherForms: number;
  learningGainFromMemory: number;
  conflictRate: number;
  coverageBySection: SectionCoverageMetric[];
};

function normalizeWhitespace(value: string | null | undefined) {
  return String(value ?? "")
    .replace(/\s+/g, " ")
    .trim();
}

function normalizeComparableText(value: string | null | undefined) {
  return normalizeWhitespace(value)
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

export function normalizeFormCode(value: string | null | undefined) {
  const normalizedValue = normalizeWhitespace(value).toUpperCase();
  if (!normalizedValue) {
    return "";
  }

  const matchedCode = normalizedValue.match(/\b([A-Z]{1,3})[\s_-]?(\d{1,4}[A-Z]?)\b/);
  if (matchedCode) {
    return `${matchedCode[1]}-${matchedCode[2]}`;
  }

  return normalizedValue.replace(/\s+/g, " ");
}

export function deriveFormAgentCode(formCodeOrName: string | null | undefined) {
  const normalizedInput = normalizeWhitespace(formCodeOrName);
  if (!normalizedInput) {
    return "form_template";
  }

  if (/^uscis_[a-z0-9_]+$/i.test(normalizedInput)) {
    return normalizedInput.toLowerCase();
  }

  const normalizedCode = normalizeFormCode(normalizedInput)
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "");

  if (!normalizedCode) {
    return "form_template";
  }

  if (/^(i|g|n|ar|ds|eta)_\d/.test(normalizedCode)) {
    return `uscis_${normalizedCode}`;
  }

  return `form_${normalizedCode}`;
}

function normalizeFormKey(value: string | null | undefined) {
  return normalizeFormCode(value)
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "");
}

function uniqueStrings(values: string[]) {
  return Array.from(new Set(values.filter(Boolean)));
}

export function detectImmigrationCaseFromSignals(
  input: ImmigrationCaseSignals,
): ImmigrationCaseAnalysis {
  const formCodes = uniqueStrings(input.forms.map((form) => normalizeFormCode(form.code)));
  const normalizedFormKeys = new Set(formCodes.map((formCode) => normalizeFormKey(formCode)));
  const relationTypes = uniqueStrings(
    (input.relationTypes ?? []).map((value) => normalizeComparableText(value)),
  );
  const factKeys = uniqueStrings((input.factKeys ?? []).map((value) => normalizeComparableText(value)));
  const caseText = normalizeComparableText([...(input.caseTexts ?? []), ...factKeys].join(" "));

  const hasForm = (code: string) => normalizedFormKeys.has(normalizeFormKey(code));
  const hasAnyText = (...needles: string[]) =>
    needles.some((needle) => caseText.includes(normalizeComparableText(needle)));
  const hasRelation = (...needles: string[]) =>
    needles.some((needle) =>
      relationTypes.some((relationType) => relationType.includes(normalizeComparableText(needle))),
    );

  const familyIndicators =
    hasForm("I-130") ||
    hasRelation("spouse", "parent", "child", "petitioner") ||
    hasAnyText("family", "marriage", "petition for alien relative");
  const employmentIndicators =
    hasForm("I-140") ||
    hasRelation("employer", "sponsor") ||
    hasAnyText("employer sponsor", "labor certification", "perm", "job offer", "employment based");
  const eb2Indicators = hasAnyText("eb2", "eb-2", "niw", "advanced degree", "masters", "phd");
  const hasAdjustment = hasForm("I-485");

  let caseType: ImmigrationCaseType = "OTHER";
  if (familyIndicators) {
    caseType = "FAMILY";
  } else if (employmentIndicators) {
    caseType = eb2Indicators ? "EB2" : "EB3";
  }

  let stageOfProcess: ImmigrationProcessStage = "intake";
  if (hasAdjustment) {
    stageOfProcess = "adjustment_package";
  } else if (hasForm("I-130") || hasForm("I-140")) {
    stageOfProcess = "petition_stage";
  } else if (formCodes.length > 0) {
    stageOfProcess = "review";
  }

  const requiredForms =
    caseType === "EB2" || caseType === "EB3" ?
      hasAdjustment ? ["I-140", "I-485", "G-28"] : ["I-140", "G-28"]
    : caseType === "FAMILY" ?
      hasAdjustment ? ["I-130", "I-485", "G-28"] : ["I-130", "G-28"]
    : hasAdjustment ?
      ["I-485", "G-28"]
    : ["G-28"];

  const missingForms = requiredForms.filter((formCode) => !hasForm(formCode));
  const recommendedNextSteps: string[] = [];

  if (missingForms.length > 0) {
    recommendedNextSteps.push(`Prepare or ingest the missing forms: ${missingForms.join(", ")}.`);
  }
  if (caseType === "EB2" || caseType === "EB3") {
    recommendedNextSteps.push(
      "Verify employer sponsorship evidence, work history, and petition support before final filing.",
    );
  }
  if (caseType === "FAMILY") {
    recommendedNextSteps.push(
      "Confirm petitioner relationship evidence and keep family-history answers synchronized across all forms.",
    );
  }
  if (!recommendedNextSteps.length) {
    recommendedNextSteps.push(
      "Review the detected package, validate conflicts, and queue human review for low-confidence fields.",
    );
  }

  return {
    caseType,
    detectedForms: formCodes,
    missingForms,
    stageOfProcess,
    recommendedNextSteps,
    evidence: {
      formCodes,
      relationTypes,
      factKeys,
    },
  };
}

export function computeFormRunMetrics(input: {
  totalFieldCount: number;
  filledCount: number;
  highConfidenceCount: number;
  unresolvedCount: number;
  reuseFromOtherForms: number;
  previousCoverage?: number | null;
  conflictsCount: number;
  coverageBySection?: Array<{
    sectionName: string;
    filled: number;
    total: number;
  }>;
}): FormRunMetrics {
  const totalFieldCount = Math.max(0, Number(input.totalFieldCount ?? 0));
  const filledCount = Math.max(0, Number(input.filledCount ?? 0));
  const highConfidenceCount = Math.max(0, Number(input.highConfidenceCount ?? 0));
  const unresolvedCount = Math.max(0, Number(input.unresolvedCount ?? 0));
  const previousCoverage = Number(input.previousCoverage ?? 0);
  const conflictsCount = Math.max(0, Number(input.conflictsCount ?? 0));

  const percentFilled = totalFieldCount > 0 ? filledCount / totalFieldCount : 0;
  const percentHighConfidence = totalFieldCount > 0 ? highConfidenceCount / totalFieldCount : 0;
  const learningGainFromMemory =
    previousCoverage > 0 ? Math.max(0, percentFilled - previousCoverage) : 0;
  const conflictRate = totalFieldCount > 0 ? conflictsCount / totalFieldCount : 0;
  const coverageBySection = (input.coverageBySection ?? []).map((section) => ({
    sectionName: normalizeWhitespace(section.sectionName) || "Unsectioned",
    filled: Math.max(0, Number(section.filled ?? 0)),
    total: Math.max(0, Number(section.total ?? 0)),
    percentFilled:
      Number(section.total ?? 0) > 0 ?
        Math.max(0, Number(section.filled ?? 0)) / Number(section.total ?? 0)
      : 0,
  }));

  return {
    percentFilled,
    percentHighConfidence,
    questionsNeeded: unresolvedCount,
    reuseFromOtherForms: Math.max(0, Number(input.reuseFromOtherForms ?? 0)),
    learningGainFromMemory,
    conflictRate,
    coverageBySection,
  };
}
