/**
 * Traveller NPC Generator - Logique métier
 * Basé sur : https://github.com/carloscasalar/traveller-npc-generator
 * 
 * Ce fichier contient la logique de génération des PNJ Traveller.
 */

import {
  CITIZEN_CATEGORY,
  EXPERIENCE_LEVEL,
  ROLE,
  ROLE_SKILLS,
  CHARACTERISTIC_PRIORITIES,
  GENDER,
  CHARACTERISTIC,
  CHARACTERISTIC_LIST,
  UPP_ORDER,
  toHex,
  calculateDm,
  pickRandomItem,
  shuffleArray,
  getRoleByKey,
  getCitizenCategoryByKey,
  getExperienceLevelByKey,
  getGenderByKey,
  getSkillsForRole,
  getCharacteristicPrioritiesForRole,
  validateOptions,
  DEFAULT_OPTIONS,
  NAME_CATALOGS,
  SKILL_LABELS_FR,
  CHARACTERISTIC_LABELS_FR,
  CITIZEN_CATEGORY_LABELS_FR,
  EXPERIENCE_LEVEL_LABELS_FR,
  ROLE_LABELS_FR,
  GENDER_LABELS_FR
} from './data/travellerNpcGenerator.js';

import { setSkillLevel, localizeSkill } from './mgt2eSkills.js';
import { travellerNpcCache, TravellerNpcError, ERROR_CODES } from './utils/travellerNpcUtils.js';

const MODULE_ID = 'mgt2-compendium-amiral-denisov';

// ============================================================================
// Conversion des compétences
// ============================================================================

/**
 * Mapping des compétences Traveller vers mgt2e
 * @type {Object<string, string>}
 */
// Mapping des compétences Traveller vers MgT2e
// IMPORTANT: Utiliser les clés internes EXACTES du système mgt2e (ex: pilot, spacecraft, smallCraft)
// Basé sur le fichier fr.json du système : /home/morr/work/foundryvtt/traveller-foundryvtt/mgt2e/lang/fr.json
// Format: 'Compétence-Traveller' -> 'competence_mgt2e.specialite_mgt2e' (en camelCase, sans accents)
// Les clés sont en anglais, les libellés français sont gérés par la localisation
const SKILL_MAPPING = {
  // Pilotage
  'Pilot-Spacecraft': 'pilot.spacecraft',
  'Pilot-Small Craft': 'pilot.smallCraft',
  'Pilot': 'pilot',
  'Flyer': 'flyer',
  
  // Astrogation et Navigation
  'Astrogation': 'astrogation',
  'Navigation': 'navigation',
  
  // Électronique
  'Electronics-Sensors': 'electronics.sensors',
  'Electronics-Communications': 'electronics.comms',
  'Electronics-Computers': 'electronics.computers',
  'Electronics': 'electronics',
  'Computers': 'electronics',
  'Communications': 'electronics',
  
  // Artillerie
  'Gunner-Turrets': 'gunner.turret',
  'Gunner-Screens': 'gunner.screen',
  'Gunner': 'gunner',
  
  // Mécanique
  'Mechanic': 'mechanic',
  
  // Ingénierie
  'Engineer-MDrive': 'engineer.mDrive',
  'Engineer-Power': 'engineer.power',
  'Engineer-JDrive': 'engineer.jDrive',
  'Engineer-Life Support': 'engineer.lifeSupport',
  'Engineer': 'engineer',
  
  // Social et Administration
  'Steward': 'steward',
  'Carouse': 'carouse',
  'Persuade': 'persuade',
  'Broker': 'broker',
  'Admin': 'admin',
  'Language': 'language',
  'Advocate': 'advocate',
  'Leadership': 'leadership',
  'Medic': 'medic',
  'Diplomat': 'diplomat',
  
  // Sciences
  'Science-Biology': 'science.biology',
  'Science-Chemistry': 'science.chemistry',
  'Science': 'science',
  
  // Combat
  'Gun Combat': 'guncombat',
  'Heavy Weapons': 'heavyweapons',
  
  // Mêlée
  'Melee-Unarmed': 'melee.unarmed',
  'Melee-Blade': 'melee.blade',
  'Melee': 'melee',
  
  // Athlétisme
  'Athletics-Strength': 'athletics.strength',
  'Athletics-Dexterity': 'athletics.dexterity',
  'Athletics': 'athletics',
  
  // Tactique et Exploration
  'Tactics': 'tactics',
  'Recon': 'recon',
  'Survival': 'survival',
  'Stealth': 'stealth',
  'Explosives': 'explosives',
  'Deception': 'deception',
  'Investigate': 'investigate',
  
  // Conduite
  'Drive-Grav': 'drive.grav',
  'Drive': 'drive',
  
  // Équipement
  'Vacc Suit': 'vaccsuit',
  
  // Art
  'Art-Acting': 'art.performer',
  'Art-Instrument': 'art.instrument',
  'Art': 'art'
};

/**
 * Convertit un nom de compétence du format Traveller vers le format mgt2e
 * @param {string} skillName - Nom de la compétence
 * @returns {string} - Nom au format mgt2e
 */
export function convertSkillToMgt2eFormat(skillName) {
  // Vérifier d'abord dans le mapping explicite
  if (SKILL_MAPPING[skillName]) {
    return SKILL_MAPPING[skillName];
  }
  
  // Si pas dans le mapping, essayer de deviner
  // MgT2e utilise des noms en minuscules (ex: pilot, electronics, guncombat)
  // 1. Remplacer les tirets et espaces par des points
  // 2. Tout mettre en minuscules
  return skillName.toLowerCase().replace(/-| /g, '.');
}

/**
 * Obtient le libellé français d'une compétence Traveller
 * @param {string} skillName - Nom de la compétence (ex: 'Pilot-Spacecraft')
 * @returns {string} - Libellé français
 */
function getSkillLabelFr(skillName) {
  return SKILL_LABELS_FR[skillName] || skillName;
}

/**
 * Obtient le libellé français d'une caractéristique
 * @param {string} charKey - Clé de la caractéristique (ex: 'STR')
 * @returns {string} - Libellé français
 */
function getCharacteristicLabelFr(charKey) {
  return CHARACTERISTIC_LABELS_FR[charKey] || charKey;
}

// ============================================================================
// Génération des caractéristiques
// ============================================================================

/**
 * Génère les caractéristiques d'un PNJ en fonction de sa catégorie et de son rôle
 * 
 * @param {string} citizenCategoryKey - Clé de la catégorie de citoyen
 * @param {string} roleKey - Clé du rôle
 * @returns {Object} - Objet contenant les caractéristiques et l'UPP
 */
export function generateCharacteristics(citizenCategoryKey, roleKey) {
  const category = getCitizenCategoryByKey(citizenCategoryKey);
  const priorities = getCharacteristicPrioritiesForRole(roleKey);
  
  // Le tableau de base de la catégorie est trié par ordre décroissant :
  // on l'attribue tel quel dans l'ordre de priorité (high → medium → low)
  let characteristicArray = [...category.characteristicArray];
  
  const characteristics = {};
  
  // Créer un tableau de priorités avec leur ordre
  const priorityGroups = [
    { keys: priorities.high, count: priorities.high.length },
    { keys: priorities.medium, count: priorities.medium.length },
    { keys: priorities.low, count: priorities.low.length }
  ];
  
  // Attribuer les valeurs dans l'ordre de priorité
  for (const group of priorityGroups) {
    const values = characteristicArray.slice(0, group.count);
    characteristicArray = characteristicArray.slice(group.count);
    
    group.keys.forEach((charKey, i) => {
      characteristics[charKey] = values[i] ?? 7;
    });
  }
  
  // Remplir les valeurs manquantes avec 7
  for (const charKey of UPP_ORDER) {
    if (characteristics[charKey] === undefined) {
      characteristics[charKey] = 7;
    }
  }
  
  // Construire l'UPP
  const upp = UPP_ORDER.map(charKey => toHex(characteristics[charKey])).join('');
  
  return {
    characteristics,
    upp,
    category
  };
}

// ============================================================================
// Génération des compétences
// ============================================================================

/**
 * Distribue les niveaux de compétence sur une liste de compétences
 * 
 * @param {string[]} roleSkills - Liste des compétences du rôle
 * @param {Object} distribution - Distribution des niveaux
 * @returns {Array<{name: string, level: number}>} - Compétences avec niveaux
 */
function distributeSkillLevels(roleSkills, distribution) {
  // Supprimer les doublons EXACTS (même nom complet de compétence)
  // mais conserver les spécialisations comme Pilot-Spacecraft et Pilot-Small Craft
  const uniqueSkills = [];
  const seen = new Set();
  
  for (const skill of roleSkills) {
    if (!seen.has(skill)) {
      seen.add(skill);
      uniqueSkills.push(skill);
    }
  }
  
  // Mélanger les compétences une seule fois
  const shuffledSkills = shuffleArray([...uniqueSkills]);
  
  // Créer un tableau de niveaux, initialisé à 0
  const levels = new Array(shuffledSkills.length).fill(0);
  
  // Distribuer les niveaux de manière séquentielle
  let index = 0;
  
  // Level 3 (les plus rares, en premier)
  const level3Count = Math.min(distribution.level3, shuffledSkills.length);
  for (let i = 0; i < level3Count; i++) {
    if (index < levels.length) levels[index++] = 3;
  }
  
  // Level 2
  const level2Count = Math.min(distribution.level2, shuffledSkills.length - level3Count);
  for (let i = 0; i < level2Count; i++) {
    if (index < levels.length) levels[index++] = 2;
  }
  
  // Level 1
  const level1Count = Math.min(distribution.level1, shuffledSkills.length - level3Count - level2Count);
  for (let i = 0; i < level1Count; i++) {
    if (index < levels.length) levels[index++] = 1;
  }
  
  // Level 0 (déjà initialisé)
  
  // Créer le résultat trié par niveau (descendant) puis par nom
  return shuffledSkills
    .map((skill, i) => ({ name: skill, level: levels[i] }))
    .sort((a, b) => b.level - a.level || a.name.localeCompare(b.name));
}

/**
 * Génère les compétences d'un PNJ en fonction de son rôle et de son expérience
 * 
 * @param {string} roleKey - Clé du rôle
 * @param {string} experienceKey - Clé du niveau d'expérience
 * @returns {Array<{name: string, level: number}>} - Liste des compétences avec niveaux
 */
export function generateSkills(roleKey, experienceKey) {
  const roleSkills = getSkillsForRole(roleKey);
  const experience = getExperienceLevelByKey(experienceKey);
  const distribution = experience.skillDistribution;
  
  return distributeSkillLevels(roleSkills, distribution);
}

// ============================================================================
// Génération complète du PNJ
// ============================================================================

/**
 * Génère un PNJ Traveller complet
 * 
 * @typedef {Object} TravellerNpcOptions
 * @property {string} [citizenCategory] - Catégorie de citoyen
 * @property {string} [experience] - Niveau d'expérience
 * @property {string} [role] - Rôle
 * @property {string} [gender] - Genre
 * @property {string} [firstName] - Prénom forcé
 * @property {string} [surname] - Nom de famille forcé
 *
 * @typedef {Object} TravellerNpcResult
 * @property {boolean} success - Succès de la génération
 * @property {string} type - Type de résultat
 * @property {Object} name - Nom du PNJ
 * @property {Object} role - Rôle
 * @property {Object} citizenCategory - Catégorie
 * @property {Object} experience - Expérience
 * @property {Object} gender - Genre
 * @property {Object} characteristics - Caractéristiques
 * @property {string} upp - Code UPP
 * @property {Array<{name: string, level: number}>} skills - Compétences
 * @property {Array<{name: string, level: number}>} skillsForActor - Compétences pour mgt2e
 * @property {Object} display - Métadonnées pour l'affichage
 *
 * @param {TravellerNpcOptions} [options={}]
 * @returns {TravellerNpcResult}
 */
export function generateTravellerNpc(options = {}) {
  // Valider et fusionner avec les options par défaut
  const opts = validateOptions({
    ...DEFAULT_OPTIONS,
    ...options
  });
  
  // Générer le nom
  let name;
  if (opts.firstName && opts.surname) {
    name = {
      firstName: opts.firstName,
      surname: opts.surname,
      fullName: `${opts.firstName} ${opts.surname}`
    };
  } else {
    const firstName = pickRandomItem(
      opts.gender === 'female' ? NAME_CATALOGS.femaleNames :
      opts.gender === 'male' ? NAME_CATALOGS.maleNames :
      NAME_CATALOGS.nonGenderedNames
    );
    const surname = pickRandomItem(NAME_CATALOGS.surnames);
    name = {
      firstName,
      surname,
      fullName: `${firstName} ${surname}`
    };
  }
  
  // Générer les caractéristiques
  const { characteristics, upp, category } = generateCharacteristics(
    opts.citizenCategory,
    opts.role
  );
  
  // Générer les compétences
  const skills = generateSkills(opts.role, opts.experience);
  
  // Convertir les compétences au format mgt2e pour la création de fiche
  const skillsForActor = skills.map(s => ({
    name: convertSkillToMgt2eFormat(s.name),
    level: s.level
  }));
  
  // Récupérer les objets complets pour les références
  const citizenCategory = getCitizenCategoryByKey(opts.citizenCategory);
  const experience = getExperienceLevelByKey(opts.experience);
  const role = getRoleByKey(opts.role);
  const gender = getGenderByKey(opts.gender);
  
  // Libellés des caractéristiques pour l'affichage (en français)
  const characteristicLabels = {};
  CHARACTERISTIC_LIST.forEach(char => {
    characteristicLabels[char.key] = getCharacteristicLabelFr(char.key);
  });
  
  // Ajouter les libellés français aux compétences pour l'affichage
  const skillsWithLabels = skills.map(skill => ({
    ...skill,
    labelFr: getSkillLabelFr(skill.name)
  }));
  
  return {
    success: true,
    type: 'traveller-npc',
    name,
    role,
    citizenCategory,
    experience,
    gender,
    characteristics,
    upp,
    skills: skillsWithLabels,
    skillsForActor,
    MODULE_ID,
    UPP_ORDER,
    display: {
      roleLabel: ROLE_LABELS_FR[role.key] || role.label,
      categoryLabel: CITIZEN_CATEGORY_LABELS_FR[citizenCategory.key] || citizenCategory.label,
      experienceLabel: EXPERIENCE_LEVEL_LABELS_FR[experience.key] || experience.label,
      genderLabel: GENDER_LABELS_FR[gender.key] || gender.label,
      characteristicLabels
    }
  };
}

// ============================================================================
// Récupération du système de base mgt2e
// ============================================================================

/**
 * Récupère le système de base des acteurs mgt2e
 * @returns {Promise<Object|null>} - Système de base ou null
 */
async function fetchMgt2eBaseActorSystem() {
  try {
    // Vérifier que le système mgt2e est actif
    if (game.system?.id !== 'mgt2e') {
      console.warn(`${MODULE_ID} | Le système mgt2e n'est pas actif`);
      return null;
    }
    
    const pack = game.packs.get('mgt2e.base-actors');
    if (!pack) {
      console.warn(`${MODULE_ID} | Le compendium mgt2e.base-actors n'est pas disponible`);
      return null;
    }
    
    const index = Array.from(await pack.getIndex({ fields: ['name', 'type'] }));
    const entry = index.find((document) => document.name === 'DEFAULT TRAVELLER')
      ?? index.find((document) => document.type === 'traveller')
      ?? index[0];
    
    if (!entry?._id) {
      console.warn(`${MODULE_ID} | Aucun acteur de base trouvé dans mgt2e.base-actors`);
      return null;
    }
    
    const document = await pack.getDocument(entry._id);
    const system = document?.toObject()?.system;
    
    if (!system) {
      console.warn(`${MODULE_ID} | Le système de l'acteur de base est vide`);
      return null;
    }
    
    return system;
  } catch (error) {
    console.error(`${MODULE_ID} | Erreur lors de la récupération du système de base mgt2e:`, error);
    throw new TravellerNpcError(
      'Erreur lors de la récupération du système de base mgt2e',
      ERROR_CODES.BASE_ACTOR_NOT_FOUND,
      { error: error.message }
    );
  }
}

/**
 * Récupère le système de base des acteurs mgt2e avec cache
 * @param {Object} [options={}] - Options
 * @param {boolean} [options.forceRefresh=false] - Forcer le rafraîchissement
 * @returns {Promise<Object|null>}
 */
export async function getMgt2eBaseActorSystem(options = {}) {
  const { forceRefresh = false } = options;
  const cacheKey = 'baseActorSystem';
  
  return travellerNpcCache.getOrFetch(
    cacheKey,
    fetchMgt2eBaseActorSystem,
    { forceRefresh }
  );
}

// ============================================================================
// Construction des données pour l'acteur
// ============================================================================

/**
 * Construit les caractéristiques au format mgt2e
 * 
 * @param {Object} existingCharacteristics - Caractéristiques existantes
 * @param {Object} characteristics - Caractéristiques générées
 * @returns {Object} - Caractéristiques au format mgt2e
 */
export function buildMgt2eCharacteristics(existingCharacteristics = {}, characteristics) {
  const result = foundry.utils.deepClone(existingCharacteristics);
  
  for (const char of CHARACTERISTIC_LIST) {
    const value = characteristics[char.key] || 7;
    result[char.mgt2eKey] = foundry.utils.mergeObject(result[char.mgt2eKey] ?? {}, {
      value,
      current: value,
      dm: calculateDm(value),
      show: true,
      default: false,
    });
  }
  
  return result;
}

/**
 * Construit les compétences au format mgt2e
 * 
 * @param {Object} existingSkills - Compétences existantes
 * @param {Array<{name: string, level: number}>} skills - Compétences générées
 * @param {boolean} [alreadyMapped=false] - Les compétences sont déjà au format mgt2e
 * @returns {Object} - Compétences au format mgt2e
 */
export function buildMgt2eSkills(existingSkills = {}, skills, alreadyMapped = false) {
  const result = foundry.utils.deepClone(existingSkills);
  
  for (const { name, level } of skills) {
    const skillName = alreadyMapped ? name : convertSkillToMgt2eFormat(name);
    setSkillLevel(result, skillName, level);
  }
  
  return result;
}

/**
 * Construit la description de l'acteur
 * 
 * @param {Object} npcData - Données du PNJ généré
 * @param {string} actorName - Nom de l'acteur
 * @returns {string} - Description formatée
 */
function buildActorDescription(npcData, actorName) {
  const notableSkills = npcData.skills
    .filter(s => s.level > 0)
    .map(s => {
      try {
        return localizeSkill(s.name) || s.name;
      } catch (e) {
        return s.name;
      }
    })
    .join(', ');
  
  const lines = [
    `${actorName} — ${npcData.role.label}`,
    `Catégorie : ${npcData.citizenCategory.label}`,
    `Expérience : ${npcData.experience.label}`,
    `UPP : ${npcData.upp}`,
    `Genre : ${npcData.gender.label}`
  ];
  
  if (notableSkills) {
    lines.push(`Compétences : ${notableSkills}`);
  }
  
  return lines.join('\n');
}

// ============================================================================
// Création de la fiche d'acteur
// ============================================================================

/**
 * Crée une fiche d'acteur pour un PNJ Traveller
 * 
 * @param {Object} npcData - Données du PNJ généré
 * @param {Object} options - Options de création
 * @param {string} [options.name] - Nom de l'acteur
 * @param {boolean} [options.openSheet=true] - Ouvrir la fiche après création
 * @returns {Promise<Actor|null>} - Acteur créé ou null
 */
export async function createTravellerNpcActor(npcData, options = {}) {
  try {
    const requestedName = options.name?.trim();
    const actorName = requestedName || npcData.name.fullName || `PNJ — ${npcData.role.label}`;
    
    // Vérifier que mgt2e est actif
    if (game.system?.id !== 'mgt2e') {
      throw new TravellerNpcError(
        'Le système mgt2e doit être actif pour créer des fiches PNJ Traveller',
        ERROR_CODES.MGT2E_NOT_ACTIVE
      );
    }
    
    // Récupérer le système de base
    const baseActorSystem = await getMgt2eBaseActorSystem();
    
    // Construire la description (avec <br> pour HTML)
    const description = buildActorDescription(npcData, actorName).replace(/\n/g, '<br>');
    
    const actorData = {
      name: actorName,
      type: 'npc',
      img: 'systems/mgt2e/icons/cargo/passenger-middle.svg',
      system: {
        settings: foundry.utils.mergeObject(
          foundry.utils.deepClone(baseActorSystem?.settings ?? {}),
          {
            hideUntrained: true,
            lockCharacteristics: true,
          }
        ),
        sophont: foundry.utils.mergeObject(
          foundry.utils.deepClone(baseActorSystem?.sophont ?? {}),
          {
            age: 18 + Math.floor(Math.random() * 40),
            homeworld: '',
            profession: npcData.role.label,
          }
        ),
        characteristics: buildMgt2eCharacteristics(
          foundry.utils.deepClone(baseActorSystem?.characteristics ?? {}),
          npcData.characteristics
        ),
        hits: foundry.utils.deepClone(baseActorSystem?.hits ?? {}),
        skills: buildMgt2eSkills(
          foundry.utils.deepClone(baseActorSystem?.skills ?? {}),
          npcData.skillsForActor,
          true
        ),
        description,
      },
      flags: {
        [MODULE_ID]: {
          generatedTravellerNpc: {
            version: 1,
            role: npcData.role.key,
            citizenCategory: npcData.citizenCategory.key,
            experience: npcData.experience.key,
            gender: npcData.gender.key,
            upp: npcData.upp,
            generatedAt: new Date().toISOString()
          },
        },
      },
    };
    
    const actor = await Actor.create(actorData, { renderSheet: false });
    
    if (options.openSheet !== false) {
      actor.sheet?.render(true);
    }
    
    return actor;
  } catch (error) {
    const npcError = TravellerNpcError.from(error, ERROR_CODES.ACTOR_CREATION_FAILED);
    npcError.notify();
    return null;
  }
}

// ============================================================================
// Fonction principale
// ============================================================================

/**
 * Fonction principale pour générer un PNJ Traveller
 * Peut créer une fiche d'acteur si demandé
 * 
 * @param {TravellerNpcOptions} [options={}]
 * @returns {Promise<TravellerNpcResult>}
 */
export async function generateAndCreateTravellerNpc(options = {}) {
  const npcData = generateTravellerNpc(options);
  
  if (options.createActor) {
    const actor = await createTravellerNpcActor(npcData, {
      name: options.actorName,
      openSheet: options.openCreatedActor !== false
    });
    
    if (actor) {
      npcData.createdActor = {
        id: actor.id,
        name: actor.name
      };
    }
  }
  
  return npcData;
}

// ============================================================================
// Export des types et données pour compatibilité
// ============================================================================

// Ré-exporter les données pour facilitier les imports
export {
  CITIZEN_CATEGORY,
  CITIZEN_CATEGORY_LIST,
  EXPERIENCE_LEVEL,
  EXPERIENCE_LEVEL_LIST,
  ROLE,
  ROLE_LIST,
  ROLE_SKILLS,
  CHARACTERISTIC_PRIORITIES,
  GENDER,
  GENDER_LIST,
  CHARACTERISTIC,
  CHARACTERISTIC_LIST,
  UPP_ORDER,
  toHex,
  calculateDm,
  pickRandomItem,
  shuffleArray,
  getRoleByKey,
  getCitizenCategoryByKey,
  getExperienceLevelByKey,
  getGenderByKey,
  getSkillsForRole,
  getCharacteristicPrioritiesForRole,
  validateOptions,
  DEFAULT_OPTIONS,
  popRandomItems
} from './data/travellerNpcGenerator.js';

// Ré-exporter ERROR_CODES et TravellerNpcError utilisés en interne (proviennent de utils)
export { ERROR_CODES, TravellerNpcError } from './utils/travellerNpcUtils.js';
