/**
 * Chroniques de l'Étrange — Système FoundryVTT
 *
 * Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
 * Ce système FoundryVTT est une implémentation indépendante et n'est pas
 * affilié à Antre-Monde Éditions,
 * mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
 *
 * @author    LeRatierBretonnien
 * @copyright 2024–2026 LeRatierBretonnien
 * @license   CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
 */

/**
 * Migrates actor JSON from the legacy CDE system to the current system format.
 *
 * This module is pure logic — it does not interact with Foundry APIs directly.
 * All transformation is done in-memory; the caller is responsible for creating
 * Foundry documents from the returned data.
 */

// ── Element label → key ──────────────────────────────────────────────────────

const ELEMENT_LABEL_TO_KEY = {
  "métal":  "metal",
  "metal":  "metal",
  "eau":    "eau",
  "terre":  "terre",
  "feu":    "feu",
  "bois":   "bois",
}

/** Normalise a French element label to its system key (e.g. "Métal" → "metal"). */
function elementKey(label = "") {
  return ELEMENT_LABEL_TO_KEY[label.toLowerCase().trim()] ?? "metal"
}

// ── Hei polarity label → key ──────────────────────────────────────────────────

function heiKey(label = "") {
  const l = label.toLowerCase().trim()
  if (l === "yin/yang" || l === "yinyang") return "yinyang"
  if (l === "yang") return "yang"
  return "yin"
}

// ── Spell discipline inference from speciality name ───────────────────────────

/** Maps French speciality labels (lowercase, accents stripped) → school key. */
const SPECIALITY_TO_DISCIPLINE = {
  // internalcinnabar
  "essence":              "internalcinnabar",
  "esprit":               "internalcinnabar",
  "mind":                 "internalcinnabar",
  "purification":         "internalcinnabar",
  "manipulation":         "internalcinnabar",
  "aura":                 "internalcinnabar",
  // alchemy
  "acupuncture":          "alchemy",
  "élixirs":              "alchemy",
  "elixirs":              "alchemy",
  "poisons":              "alchemy",
  "arsenal":              "alchemy",
  "potions":              "alchemy",
  // masteryoftheway
  "malédiction":          "masteryoftheway",
  "malediction":          "masteryoftheway",
  "transfiguration":      "masteryoftheway",
  "nécromancie":          "masteryoftheway",
  "necromancie":          "masteryoftheway",
  "contrôle climatique":  "masteryoftheway",
  "controle climatique":  "masteryoftheway",
  "magie d'or":           "masteryoftheway",
  "magie dor":            "masteryoftheway",
  // exorcism
  "invocation":           "exorcism",
  "pistage":              "exorcism",
  "traçage":              "exorcism",
  "tracage":              "exorcism",
  "protection":           "exorcism",
  "châtiment":            "exorcism",
  "chatiment":            "exorcism",
  "domination":           "exorcism",
  // geomancy
  "neutralisation":       "geomancy",
  "divination":           "geomancy",
  "prière terrestre":     "geomancy",
  "priere terrestre":     "geomancy",
  "prière céleste":       "geomancy",
  "priere celeste":       "geomancy",
  "géomancie":            "geomancy",
  "geomancie":            "geomancy",
  "feng shui":            "geomancy",
  "fungseoi":             "geomancy",
}

/**
 * Attempt to infer the magic school (discipline) from a spell's speciality name.
 * Falls back to scanning the item name for school keywords if needed.
 */
function inferDiscipline(specialityName = "", itemName = "") {
  const key = specialityName.toLowerCase().trim()
  if (SPECIALITY_TO_DISCIPLINE[key]) return SPECIALITY_TO_DISCIPLINE[key]

  // Fuzzy fallback: check item name for school markers
  const name = itemName.toLowerCase()
  if (name.includes("exorcis"))           return "exorcism"
  if (name.includes("géomanci") || name.includes("geomanci")) return "geomancy"
  if (name.includes("alchimi"))           return "alchemy"
  if (name.includes("cinnabre") || name.includes("interne")) return "internalcinnabar"
  if (name.includes("maîtrise") || name.includes("maitrise") || name.includes("tao")) return "masteryoftheway"

  return "internalcinnabar"
}

// ── KungFu activation mapping ─────────────────────────────────────────────────

function mapActivation(oldActivation = "") {
  const s = oldActivation.toLowerCase()
  if (s.includes("infligés") || s.includes("infliges"))    return "damage-inflicted"
  if (s.includes("reçus") || s.includes("recus"))          return "damage-received"
  if (s.includes("réaction") || s.includes("reaction"))    return "reaction"
  if (s.includes("dés-fastes") || s.includes("des-fastes") || s.includes("fastes")) return "dice"
  if (s.includes("aide"))                                   return "action-aid"
  if (s.includes("attaque") && s.includes("défense"))      return "action-attack-defense"
  if (s.includes("attaque") && s.includes("defense"))      return "action-attack-defense"
  if (s.includes("attaque"))                               return "action-attack"
  if (s.includes("défense") || s.includes("defense"))      return "action-defense"
  return "action-attack"
}

// ── Helpers ───────────────────────────────────────────────────────────────────

const DEFAULT_ACTOR_IMG = "icons/svg/mystery-man.svg"
const DEFAULT_ITEM_IMG  = "icons/svg/item-bag.svg"

// ── Item migration ────────────────────────────────────────────────────────────

function migrateEquipmentItem(oldItem) {
  const s = oldItem.system ?? {}
  return {
    name: oldItem.name,
    type: "item",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:   s.reference ?? "",
      description: s.description ?? "",
      quantity:    Number(s.quantity ?? 1),
      weight:      Number(s.weight ?? 0),
      notes:       s.notes ?? "",
    },
  }
}

function migrateKungfuItem(oldItem) {
  const s = oldItem.system ?? {}
  const techs = s.techniques ?? {}

  const migratedTechs = {}
  for (const key of ["technique1", "technique2", "technique3"]) {
    const t = techs[key] ?? {}
    migratedTechs[key] = {
      check:      Boolean(t.check),
      name:       t.name ?? "",
      activation: mapActivation(t.activation ?? ""),
      technique:  t.technique ?? "",
    }
  }

  return {
    name: oldItem.name,
    type: "kungfu",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:   s.reference ?? "",
      description: s.description ?? "",
      orientation: s.orientation || "yin",
      aspect:      s.aspect || "metal",
      skill:       s.skill || "kungfu",
      speciality:  s.speciality ?? "",
      style:       s.style ?? "",
      techniques:  migratedTechs,
      notes:       s.notes ?? "",
    },
  }
}

function migrateSpellItem(oldItem) {
  const s = oldItem.system ?? {}
  return {
    name: oldItem.name,
    type: "spell",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:                  s.reference ?? "",
      description:                s.description ?? "",
      specialityname:             s.specialityname ?? "",
      associatedelement:          elementKey(s.associatedelement ?? ""),
      heiType:                    heiKey(s.hei ?? ""),
      heiCost:                    Number(s.heiCost ?? 0),
      difficulty:                 Number(s.difficulty ?? 0),
      realizationtimeritual:      s.realizationtimeritual ?? "",
      realizationtimeaccelerated: s.realizationtimeaccelerated ?? "",
      flashback:                  s.flashback ?? "",
      components:                 s.components ?? "",
      effects:                    s.effects ?? "",
      examples:                   s.examples ?? "",
      notes:                      s.notes ?? "",
      discipline:                 inferDiscipline(s.specialityname ?? "", oldItem.name ?? ""),
    },
  }
}

function migrateSupernaturalItem(oldItem) {
  const s = oldItem.system ?? {}
  // Old system stored a nested `supernatural: { reference }` — prefer that reference if top-level is empty
  const nestedRef = s.supernatural?.reference ?? ""
  return {
    name: oldItem.name,
    type: "supernatural",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:   s.reference || nestedRef,
      description: s.description ?? "",
      notes:       s.notes ?? "",
      heiType:     "yin",
      heiCost:     0,
      trigger:     "",
      effects:     "",
    },
  }
}

function migrateWeaponItem(oldItem) {
  const s = oldItem.system ?? {}
  return {
    name: oldItem.name,
    type: "weapon",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:         s.reference ?? "",
      description:       s.description ?? "",
      hasSpeciality:     Boolean(s.hasSpeciality),
      weaponType:        s.weaponType || "melee",
      material:          s.material ?? "",
      damageAspect:      elementKey(s.damageAspect ?? ""),
      damageBase:        Number(s.damageBase ?? 0),
      range:             s.range || "contact",
      obtainLevel:       Number(s.obtainLevel ?? 0),
      obtainDifficulty:  Number(s.obtainDifficulty ?? 0),
      quantity:          Number(s.quantity ?? 1),
      notes:             s.notes ?? "",
    },
  }
}

function migrateArmorItem(oldItem) {
  const s = oldItem.system ?? {}
  return {
    name: oldItem.name,
    type: "armor",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:         s.reference ?? "",
      description:       s.description ?? "",
      protectionValue:   Number(s.protectionValue ?? 0),
      domain:            s.domain ?? "",
      obtainLevel:       Number(s.obtainLevel ?? 0),
      obtainDifficulty:  Number(s.obtainDifficulty ?? 0),
      quantity:          Number(s.quantity ?? 1),
      notes:             s.notes ?? "",
    },
  }
}

function migrateSanheiItem(oldItem) {
  const s = oldItem.system ?? {}
  const props = s.properties ?? {}
  const propSchema = (p) => ({
    name:        p?.name ?? "",
    heiCost:     Number(p?.heiCost ?? 0),
    heiType:     heiKey(p?.heiType ?? ""),
    description: p?.description ?? "",
  })
  return {
    name: oldItem.name,
    type: "sanhei",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:   s.reference ?? "",
      description: s.description ?? "",
      heiType:     heiKey(s.heiType ?? ""),
      properties: {
        prop1: propSchema(props.prop1),
        prop2: propSchema(props.prop2),
        prop3: propSchema(props.prop3),
      },
      notes: s.notes ?? "",
    },
  }
}

function migrateIngredientItem(oldItem) {
  const s = oldItem.system ?? {}
  return {
    name: oldItem.name,
    type: "ingredient",
    img:  oldItem.img || DEFAULT_ITEM_IMG,
    system: {
      reference:         s.reference ?? "",
      description:       s.description ?? "",
      school:            s.school ?? "all",
      obtainLevel:       Number(s.obtainLevel ?? 0),
      obtainDifficulty:  Number(s.obtainDifficulty ?? 0),
      quantity:          Number(s.quantity ?? 1),
      notes:             s.notes ?? "",
    },
  }
}

function migrateItem(oldItem) {
  switch (oldItem.type) {
    case "item":          return migrateEquipmentItem(oldItem)
    case "kungfu":        return migrateKungfuItem(oldItem)
    case "spell":         return migrateSpellItem(oldItem)
    case "supernatural":  return migrateSupernaturalItem(oldItem)
    case "weapon":        return migrateWeaponItem(oldItem)
    case "armor":         return migrateArmorItem(oldItem)
    case "sanhei":        return migrateSanheiItem(oldItem)
    case "ingredient":    return migrateIngredientItem(oldItem)
    default:
      // Unknown item type: keep as generic equipment
      return migrateEquipmentItem({ ...oldItem, type: "item" })
  }
}

// ── Actor migration ───────────────────────────────────────────────────────────

function migrateCharacter(old) {
  const s = old.system ?? {}

  // aspects: keep only { chinese, label, value }
  const aspect = {}
  for (const [k, v] of Object.entries(s.aspect ?? {})) {
    aspect[k] = { chinese: v.chinese ?? "", label: v.label ?? "", value: Number(v.value ?? 0) }
  }

  // skills: keep { label, specialities, value }
  const skills = {}
  for (const [k, v] of Object.entries(s.skills ?? {})) {
    skills[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0) }
  }

  // resources: keep { label, specialities, value, debt }
  const resources = {}
  for (const [k, v] of Object.entries(s.resources ?? {})) {
    resources[k] = { label: v.label ?? "", specialities: v.specialities ?? "", value: Number(v.value ?? 0), debt: Boolean(v.debt) }
  }

  // components: keep { value }
  const component = {}
  for (const [k, v] of Object.entries(s.component ?? {})) {
    component[k] = { value: v.value ?? "" }
  }

  // magics: keep { visible, value, speciality: { [key]: { check } } }
  const MAGIC_SPECIALITIES = {
    internalcinnabar: ["essence", "mind", "purification", "manipulation", "aura"],
    alchemy:          ["acupuncture", "elixirs", "poisons", "arsenal", "potions"],
    masteryoftheway:  ["curse", "transfiguration", "necromancy", "climatecontrol", "goldenmagic"],
    exorcism:         ["invocation", "tracking", "protection", "punishment", "domination"],
    geomancy:         ["neutralization", "divination", "earthlyprayer", "heavenlyprayer", "fungseoi"],
  }
  const magics = {}
  for (const [school, specs] of Object.entries(MAGIC_SPECIALITIES)) {
    const om = s.magics?.[school] ?? {}
    const speciality = {}
    for (const spec of specs) {
      speciality[spec] = { check: Boolean(om.speciality?.[spec]?.check) }
    }
    magics[school] = { visible: Boolean(om.visible), value: Number(om.value ?? 0), speciality }
  }

  // threetreasures: strip `min` from heiyang/heiyin; keep dicelevel as-is
  const tt = s.threetreasures ?? {}
  const threetreasures = {
    heiyang: { value: Number(tt.heiyang?.value ?? 0), max: Number(tt.heiyang?.max ?? 0) },
    heiyin:  { value: Number(tt.heiyin?.value ?? 0),  max: Number(tt.heiyin?.max ?? 0)  },
    dicelevel: {
      level0d: {
        san:  { value: Number(tt.dicelevel?.level0d?.san?.value ?? 0),  max: Number(tt.dicelevel?.level0d?.san?.max ?? 0) },
        zing: { value: Number(tt.dicelevel?.level0d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level0d?.zing?.max ?? 0) },
      },
      level1d: {
        san:  { value: Number(tt.dicelevel?.level1d?.san?.value ?? 0),  max: Number(tt.dicelevel?.level1d?.san?.max ?? 0) },
        zing: { value: Number(tt.dicelevel?.level1d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level1d?.zing?.max ?? 0) },
      },
      level2d: {
        san:  { value: Number(tt.dicelevel?.level2d?.san?.value ?? 0),  max: Number(tt.dicelevel?.level2d?.san?.max ?? 0) },
        zing: { value: Number(tt.dicelevel?.level2d?.zing?.value ?? 0), max: Number(tt.dicelevel?.level2d?.zing?.max ?? 0) },
      },
    },
  }

  // biography (old separate field) merged into description
  const description = s.description || s.biography || ""

  return {
    name: old.name,
    type: "character",
    img:  old.img || DEFAULT_ACTOR_IMG,
    system: {
      concept:         s.concept ?? "",
      guardian:        parseInt(s.guardian ?? "0") || 0,
      initiative:      Number(s.initiative ?? 1),
      anti_initiative: Number(s.anti_initiative ?? 24),
      description,
      aspect,
      skills,
      resources,
      component,
      magics,
      magicOrder: [],
      threetreasures,
      experience: {
        value: Number(s.experience?.value ?? 0),
        max:   Number(s.experience?.max ?? 0),
        min:   Number(s.experience?.min ?? 0),
      },
    },
    items: (old.items ?? []).map(migrateItem),
  }
}

function migrateNpc(old) {
  const s = old.system ?? {}

  const aptitudes = {}
  for (const [k, v] of Object.entries(s.aptitudes ?? {})) {
    aptitudes[k] = { value: Number(v.value ?? 0), speciality: v.speciality ?? "" }
  }

  return {
    name: old.name,
    type: "npc",
    img:  old.img || DEFAULT_ACTOR_IMG,
    system: {
      type:            s.type ?? "",
      // Old system had separate `levelofthreat`/`powerofnuisance` as numbers
      // and string copies `threat`/`nuisance` — use the numeric fields
      threat:          Number(s.levelofthreat ?? s.threat ?? 0),
      nuisance:        Number(s.powerofnuisance ?? s.nuisance ?? 0),
      initiative:      Number(s.initiative ?? 1),
      anti_initiative: Number(s.anti_initiative ?? 24),
      aptitudes,
      vitality: {
        value:  Number(s.vitality?.value ?? 0),
        calcul: Number(s.vitality?.calcul ?? 0),
        note:   s.vitality?.note ?? "",
      },
      hei: {
        value:  Number(s.hei?.value ?? 0),
        calcul: Number(s.hei?.calcul ?? 0),
        note:   s.hei?.note ?? "",
      },
      description: s.description ?? "",
    },
    items: (old.items ?? []).map(migrateItem),
  }
}

// ── Public API ────────────────────────────────────────────────────────────────

/**
 * Migrate a single legacy actor JSON to the current system format.
 *
 * @param {object} oldJson  Parsed JSON from the old system export
 * @returns {{ name: string, type: string, img: string, system: object, items: object[] }}
 * @throws {Error} if the actor type is unrecognised
 */
export function migrateActor(oldJson) {
  switch (oldJson.type) {
    case "character": return migrateCharacter(oldJson)
    case "npc":       return migrateNpc(oldJson)
    default:
      throw new Error(`Unknown actor type "${oldJson.type}" in "${oldJson.name}"`)
  }
}

/**
 * Parse one or more legacy JSON strings and return migrated actor data.
 * Accepts a single actor object or an array of actor objects in one file.
 *
 * @param {string} jsonText  Raw JSON text from a file
 * @returns {Array<{ name, type, img, system, items }>}
 */
export function parseLegacyJson(jsonText) {
  const parsed = JSON.parse(jsonText)
  if (typeof parsed !== "object" || parsed === null) {
    throw new Error("Le fichier JSON doit contenir un objet acteur ou un tableau d'acteurs")
  }
  const actors = Array.isArray(parsed) ? parsed : [parsed]
  return actors.map(migrateActor)
}
