// utils/fhirNameParser.ts
import type { FhirHumanName } from "~/types/fhir/humanName";

// Indonesian academic suffixes
import indonesianPrefixes from "~/data/indonesianPrefixes.json";
import indonesianSuffixes from "~/data/indonesianSuffixes.json";
const INDONESIAN_PREFIXES: readonly string[] = indonesianPrefixes;
const INDONESIAN_SUFFIXES: readonly string[] = indonesianSuffixes;

// Family name patterns
interface FamilyNamePattern {
  pattern: RegExp;
  category: string;
}

const FAMILY_NAME_PATTERNS: readonly FamilyNamePattern[] = [
  // Pola untuk nama keluarga Batak
  {
    pattern:
      /^(Siregar|Sitorus|Simanjuntak|Simatupang|Sinaga|Harahap|Hasibuan|Hutapea|Hutagalung|Hutabarat|Nasution|Lubis|Batubara|Rangkuti|Dalimunthe|Daulay|Matondang|Pulungan|Parinduri)$/i,
    category: "batak"
  },

  // Pola untuk nama keluarga Mandailing
  {
    pattern:
      /^(Nasution|Lubis|Batubara|Rangkuti|Dalimunthe|Daulay|Matondang|Pulungan|Parinduri)$/i,
    category: "mandailing"
  },

  // Pola untuk nama keluarga Tionghoa
  {
    pattern:
      /^(Tan|Lim|Lie|Ong|Tjoa|Oei|Kwee|The|Chong|Huang|Li|Wang|Zhang|Chen|Liu|Yang)$/i,
    category: "chinese"
  },

  // Pola untuk nama keluarga Javanese-Chinese
  {
    pattern:
      /^(Wijaya|Santoso|Gunawan|Susanto|Halim|Tjandra|Suharto|Prabowo|Wibowo|Setiawan)$/i,
    category: "javanese-chinese"
  },

  // Pola untuk nama umum dengan akhiran
  {
    pattern:
      /^(Putra|Putri|Wati|Ningrum|Sari|Dewi|Rahayu|Sukma|Ningsih|Rizki)$/i,
    category: "common-suffix"
  },

  // Pola untuk nama keluarga Melayu-Arab
  {
    pattern: /^(bin|binti)$/i,
    category: "malay-arabic"
  },

  // Pola untuk nama keluarga umum
  {
    pattern: /^(Sukma|Rizki|Dewi|Sari|Ningsih|Rahayu|Wati|Ningrum)$/i,
    category: "common"
  },

  // Pola untuk nama keluarga Arab
  {
    pattern:
      /^(Yusuf|Hassan|Ali|Fatimah|Aisyah|Zain|Husain|Khalid|Amin|Salim)$/i,
    category: "arabic"
  },

  // Pola untuk nama keluarga Javanese
  {
    pattern:
      /^(Suharto|Suharjo|Sukardi|Sukmawati|Kusuma|Prabowo|Wibowo|Setiawan)$/i,
    category: "javanese"
  },

  // Pola untuk nama keluarga perempuan
  {
    pattern: /^(Sari|Wati|Ningrum|Dewi|Rahayu|Sukma|Ningsih)$/i,
    category: "female-suffix"
  },

  // Pola untuk nama keluarga Sunda
  {
    pattern:
      /^(Sukma|Sari|Dewi|Rahayu|Hidayah|Ningsih|Rizki|Suhendi|Sukardi)$/i,
    category: "sundanese"
  },

  // Pola untuk nama keluarga Bali
  {
    pattern: /^(Putra|Putri|Wayan|Made|Nyoman|Ketut|Agung|Sukma|Dewi|Sari)$/i,
    category: "balinese"
  },

  // Pola untuk nama keluarga Bugis
  {
    pattern: /^(Andi|Daeng|Puang|Sultan|Raja|Sitti|Baji|Makkunrai)$/i,
    category: "bugis"
  },

  // Pola untuk nama keluarga Minang
  {
    pattern: /^(Sutan|Datuk|Raja|Pangeran|Haji|Sari|Datu|Raden)$/i,
    category: "minang"
  }
];

export function parseFhirHumanName(
  fullName: string | null | undefined
): FhirHumanName | null {
  if (!fullName || typeof fullName !== "string") {
    return null;
  }

  const fhirName: FhirHumanName = {
    use: "official",
    text: fullName.trim(),
    family: undefined,
    given: [],
    prefix: [],
    suffix: []
  };

  let workingName = fullName.trim();

  // Extract prefixes - accumulate consecutive prefixes into one string
  const prefixesFound: string[] = [];
  let prefixFound = true;
  while (prefixFound) {
    prefixFound = false;
    for (const prefix of INDONESIAN_PREFIXES) {
      const regex = new RegExp(`^${escapeRegExp(prefix)}\\s+`, "i");
      if (regex.test(workingName)) {
        prefixesFound.push(prefix);
        workingName = workingName.replace(regex, "");
        prefixFound = true;
        break; // restart loop after removing one prefix
      }
    }
  }
  if (prefixesFound.length > 0) {
    fhirName.prefix!.push(prefixesFound.join(" "));
  } else {
    // If no known prefix found, treat first word as prefix or given if single word
    const firstSpaceIndex = workingName.indexOf(" ");
    if (firstSpaceIndex !== -1) {
      const firstWord = workingName.substring(0, firstSpaceIndex);
      fhirName.prefix!.push(firstWord);
      workingName = workingName.substring(firstSpaceIndex + 1).trim();
    } else if (workingName.length > 0) {
      // Single word name, treat as given
      fhirName.given!.push(workingName);
      workingName = "";
    }
  }

  // Extract suffixes - improved to handle multiple suffixes separated by commas and spaces
  fhirName.suffix = [];
  let suffixesFound = true;
  while (suffixesFound) {
    suffixesFound = false;
    for (const suffix of INDONESIAN_SUFFIXES) {
      const regex = new RegExp(`(,?\\s+${escapeRegExp(suffix)})$`, "i");
      if (regex.test(workingName)) {
        if (!fhirName.suffix.includes(suffix)) {
          fhirName.suffix.push(suffix);
        }
        workingName = workingName.replace(regex, "").trim();
        suffixesFound = true;
        break; // restart loop after removing one suffix
      }
    }
  }

  // Remove trailing commas after suffix removal
  workingName = workingName.replace(/,\s*$/, "").trim();

  // Remove trailing commas
  workingName = workingName.replace(/,\s*$/, "").trim();

  // Split name parts
  const nameParts = workingName.split(/\s+/).filter((part) => part.length > 0);

  if (nameParts.length === 0) {
    return fhirName;
  }

  // Parse name parts with improved family name detection for multi-word family names
  if (nameParts.length === 1) {
    fhirName.given!.push(nameParts[0]);
  } else if (nameParts.length === 2) {
    if (isLikelyFamilyName(nameParts[1])) {
      fhirName.family = nameParts[1];
      fhirName.given!.push(nameParts[0]);
    } else {
      fhirName.given!.push(nameParts[0]);
      fhirName.given!.push(nameParts[1]);
    }
  } else {
    // Check last two words combined for family name
    const lastTwoParts = nameParts.slice(-2).join(" ");
    if (isLikelyFamilyName(lastTwoParts)) {
      fhirName.family = lastTwoParts;
      for (let i = 0; i < nameParts.length - 2; i++) {
        fhirName.given!.push(nameParts[i]);
      }
    } else if (isLikelyFamilyName(nameParts[nameParts.length - 1])) {
      fhirName.family = nameParts[nameParts.length - 1];
      for (let i = 0; i < nameParts.length - 1; i++) {
        fhirName.given!.push(nameParts[i]);
      }
    } else {
      // Default: treat last two words as family name
      fhirName.family = lastTwoParts;
      for (let i = 0; i < nameParts.length - 2; i++) {
        fhirName.given!.push(nameParts[i]);
      }
    }
  }

  // Clean up empty arrays
  if (fhirName.prefix!.length === 0) delete fhirName.prefix;
  if (fhirName.suffix!.length === 0) delete fhirName.suffix;
  if (fhirName.given!.length === 0) delete fhirName.given;
  if (!fhirName.family) delete fhirName.family;

  return fhirName;
}

function isLikelyFamilyName(namePart: string): boolean {
  return FAMILY_NAME_PATTERNS.some(({ pattern }) => pattern.test(namePart));
}

function escapeRegExp(string: string): string {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

export function formatFhirName(
  fhirName: FhirHumanName | null | undefined
): string {
  if (!fhirName) return "";

  const parts: string[] = [];

  if (fhirName.prefix && fhirName.prefix.length > 0) {
    parts.push(fhirName.prefix.join(" "));
  }

  if (fhirName.given && fhirName.given.length > 0) {
    parts.push(fhirName.given.join(" "));
  }

  if (fhirName.family) {
    parts.push(fhirName.family);
  }

  if (fhirName.suffix && fhirName.suffix.length > 0) {
    parts.push(fhirName.suffix.join(", "));
  }

  return parts.join(" ");
}

export function validateFhirHumanName(
  fhirName: FhirHumanName | null | undefined
): string[] {
  const errors: string[] = [];

  if (!fhirName) {
    errors.push("FHIR HumanName object is required");
    return errors;
  }

  if (!fhirName.text || fhirName.text.trim() === "") {
    errors.push("Text representation of name is required");
  }

  if ((!fhirName.given || fhirName.given.length === 0) && !fhirName.family) {
    errors.push("At least one given name or family name is required");
  }

  const validUseValues: FhirHumanName["use"][] = [
    "usual",
    "official",
    "temp",
    "nickname",
    "anonymous",
    "old",
    "maiden"
  ];
  if (fhirName.use && !validUseValues.includes(fhirName.use)) {
    errors.push(
      `Invalid use value. Must be one of: ${validUseValues.join(", ")}`
    );
  }

  return errors;
}
