import { NPC_RELATIONS } from './data/npcTables.js';
import {
  RELATION_FORMULAS,
  AFFINITY_INIMITY_MAP,
  POWER_INFLUENCE_MAP,
  AFFINITY_LABELS,
  INIMITY_LABELS,
  POWER_LABELS,
  INFLUENCE_LABELS,
  SPECIAL_CHARACTERISTICS_TABLE,
} from './data/allyEnemyTables.js';

export function mapRollToValue(roll, mapping) {
  return mapping[roll] ?? 0;
}

export function getLabel(value, labels) {
  return labels.find(l => l.value === Math.abs(value)) ?? labels[0];
}

export function clamp(value, min, max) {
  return Math.max(min, Math.min(max, value));
}

async function rollFormula(formula) {
  const roll = await new Roll(formula).evaluate();
  return { formula, total: roll.total };
}

function getD66Entry(entries, total) {
  return entries.find(e => e.d66 === total) ?? null;
}

async function rollD66(entries) {
  const tens = await rollFormula('1d6');
  const ones = await rollFormula('1d6');
  const total = (tens.total * 10) + ones.total;
  return {
    total,
    tens: tens.total,
    ones: ones.total,
    entry: getD66Entry(entries, total),
  };
}

async function rollAffinityInimity(relationKey) {
  const formulas = RELATION_FORMULAS[relationKey];
  let affinityRoll = null;
  let inimityRoll = null;

  if (formulas.affinity !== '0') {
    affinityRoll = await rollFormula(formulas.affinity);
  }
  if (formulas.inimity !== '0') {
    inimityRoll = await rollFormula(formulas.inimity);
  }

  return {
    affinityValue: affinityRoll ? mapRollToValue(affinityRoll.total, AFFINITY_INIMITY_MAP) : 0,
    inimityValue: inimityRoll ? mapRollToValue(inimityRoll.total, AFFINITY_INIMITY_MAP) : 0,
    affinityRoll,
    inimityRoll,
    formulas,
  };
}

async function resolveSpecialCharacteristics(currentRelationKey, depth = 0) {
  if (depth > 5) return [];

  const d66Result = await rollD66(SPECIAL_CHARACTERISTICS_TABLE);
  if (!d66Result.entry) return [];

  const entry = d66Result.entry;
  const result = {
    d66: d66Result.total,
    text: entry.text,
    effects: entry.effects,
    appliedDeltas: { affinity: 0, inimity: 0, power: 0, influence: 0 },
    rerollNote: null,
    swapNote: null,
    narrativeText: entry.effects.action === 'narrativeOnly' ? entry.text : null,
    newRelationKey: null,
    subCharacteristics: [],
  };

  if (entry.effects.affinityMod) result.appliedDeltas.affinity = entry.effects.affinityMod;
  if (entry.effects.inimityMod) result.appliedDeltas.inimity = entry.effects.inimityMod;
  if (entry.effects.powerMod) result.appliedDeltas.power = entry.effects.powerMod;
  if (entry.effects.influenceMod) result.appliedDeltas.influence = entry.effects.influenceMod;

  if (entry.effects.action === 'extraRolls') {
    const count = entry.effects.actionValue || 1;
    for (let i = 0; i < count; i++) {
      const extra = await resolveSpecialCharacteristics(currentRelationKey, depth + 1);
      result.subCharacteristics.push(...extra);
    }
  }

  return result;
}

export async function generateAllyEnemy(relationKey = 'contact', options = {}) {
  const relation = NPC_RELATIONS[relationKey];
  let currentRelationKey = relationKey;

  const initial = await rollAffinityInimity(relationKey);
  let affinityValue = initial.affinityValue;
  let inimityValue = initial.inimityValue;
  let affinityRoll = initial.affinityRoll;
  let inimityRoll = initial.inimityRoll;
  let currentFormulas = initial.formulas;

  const powerRoll = await rollFormula('2d6');
  const influenceRoll = await rollFormula('2d6');
  let powerValue = mapRollToValue(powerRoll.total, POWER_INFLUENCE_MAP);
  let influenceValue = mapRollToValue(influenceRoll.total, POWER_INFLUENCE_MAP);

  let specialRoll = null;
  let specialCharacteristics = [];

  if (options.includeSpecial !== false) {
    specialRoll = await rollFormula('2d6');

    if (specialRoll.total >= 8) {
      let queue = await resolveSpecialCharacteristics(currentRelationKey);

      while (queue.length > 0) {
        const sc = queue.shift();

        if (sc.effects.action === 'extraRolls') {
          queue.push(...sc.subCharacteristics);
        }

        if (sc.effects.action === 'moderateRelation') {
          if (currentRelationKey === 'enemy') {
            currentRelationKey = 'rival';
            const rerolled = await rollAffinityInimity(currentRelationKey);
            affinityValue = rerolled.affinityValue;
            inimityValue = rerolled.inimityValue;
            affinityRoll = rerolled.affinityRoll;
            inimityRoll = rerolled.inimityRoll;
            currentFormulas = rerolled.formulas;
            sc.newRelationKey = currentRelationKey;
          } else if (currentRelationKey === 'ally') {
            currentRelationKey = 'contact';
            const rerolled = await rollAffinityInimity(currentRelationKey);
            affinityValue = rerolled.affinityValue;
            inimityValue = rerolled.inimityValue;
            affinityRoll = rerolled.affinityRoll;
            inimityRoll = rerolled.inimityRoll;
            currentFormulas = rerolled.formulas;
            sc.newRelationKey = currentRelationKey;
          }
        }

        if (sc.effects.action === 'intensifyRelation') {
          if (currentRelationKey === 'rival') {
            currentRelationKey = 'enemy';
            const rerolled = await rollAffinityInimity(currentRelationKey);
            affinityValue = rerolled.affinityValue;
            inimityValue = rerolled.inimityValue;
            affinityRoll = rerolled.affinityRoll;
            inimityRoll = rerolled.inimityRoll;
            currentFormulas = rerolled.formulas;
            sc.newRelationKey = currentRelationKey;
          } else if (currentRelationKey === 'contact') {
            currentRelationKey = 'ally';
            const rerolled = await rollAffinityInimity(currentRelationKey);
            affinityValue = rerolled.affinityValue;
            inimityValue = rerolled.inimityValue;
            affinityRoll = rerolled.affinityRoll;
            inimityRoll = rerolled.inimityRoll;
            currentFormulas = rerolled.formulas;
            sc.newRelationKey = currentRelationKey;
          }
        }

        if (sc.effects.action === 'reRollAffinity') {
          const reroll = await rollFormula('2d6');
          const rerolledValue = mapRollToValue(reroll.total, AFFINITY_INIMITY_MAP);
          if (rerolledValue > affinityValue) {
            sc.rerollNote = `Affinité relancée : ${reroll.total} → ${rerolledValue} (était ${affinityValue})`;
            sc.appliedDeltas.affinity = rerolledValue - affinityValue;
          }
        }

        if (sc.effects.action === 'reRollInimity') {
          const reroll = await rollFormula('2d6');
          const rerolledValue = mapRollToValue(reroll.total, AFFINITY_INIMITY_MAP);
          if (rerolledValue > inimityValue) {
            sc.rerollNote = `Inimitié relancée : ${reroll.total} → ${rerolledValue} (était ${inimityValue})`;
            sc.appliedDeltas.inimity = rerolledValue - inimityValue;
          }
        }

        if (sc.effects.action === 'swapAffinityInimity') {
          sc.swapNote = 'Affinité et Inimitié échangées';
          const tmpAff = affinityValue;
          const tmpInim = inimityValue;
          sc.appliedDeltas.affinity = tmpInim - affinityValue;
          sc.appliedDeltas.inimity = tmpAff - inimityValue;
        }

        if (sc.effects.action === 'setPowerToZero') {
          sc.appliedDeltas.power = -powerValue;
        }

        if (sc.effects.action === 'createEnemy') {
          sc.narrativeText = 'Un nouvel Ennemi commun au Voyageur et à cet individu est créé.';
        }

        if (sc.effects.action === 'createContactOrRival') {
          const net = affinityValue - inimityValue;
          sc.narrativeText = net > 0
            ? 'Un nouveau Contact est créé (Affinité supérieure à l\'Inimitié).'
            : 'Un nouveau Rival est créé (Inimitié supérieure à l\'Affinité).';
        }

        let newAffinity = affinityValue + (sc.appliedDeltas.affinity || 0);
        let newInimity = inimityValue + (sc.appliedDeltas.inimity || 0);
        let newPower = powerValue + (sc.appliedDeltas.power || 0);
        let newInfluence = influenceValue + (sc.appliedDeltas.influence || 0);

        affinityValue = clamp(newAffinity, 0, 6);
        inimityValue = clamp(newInimity, 0, 6);
        powerValue = clamp(newPower, 0, 6);
        influenceValue = clamp(newInfluence, 0, 6);

        specialCharacteristics.push(sc);
      }
    }
  }

  const finalRelation = currentRelationKey !== relationKey
    ? NPC_RELATIONS[currentRelationKey]
    : relation;

  const netScore = affinityValue - inimityValue;

  return {
    success: true,
    type: 'ally-enemy',
    relation: { key: currentRelationKey, label: finalRelation.label, summary: finalRelation.summary },
    originalRelationKey: relationKey,
    relationChanged: currentRelationKey !== relationKey,

    affinity: {
      formula: currentFormulas.affinity,
      roll: affinityRoll?.total ?? 0,
      value: affinityValue,
      label: getLabel(affinityValue, AFFINITY_LABELS).label,
      description: getLabel(affinityValue, AFFINITY_LABELS).description,
    },
    inimity: {
      formula: currentFormulas.inimity,
      roll: inimityRoll?.total ?? 0,
      value: inimityValue,
      label: getLabel(inimityValue, INIMITY_LABELS).label,
      description: getLabel(inimityValue, INIMITY_LABELS).description,
    },
    netScore,

    power: {
      value: powerValue,
      label: getLabel(powerValue, POWER_LABELS).label,
      description: getLabel(powerValue, POWER_LABELS).description,
    },
    influence: {
      value: influenceValue,
      label: getLabel(influenceValue, INFLUENCE_LABELS).label,
      description: getLabel(influenceValue, INFLUENCE_LABELS).description,
    },

    specialRoll: specialRoll ? { roll: specialRoll.total, triggered: specialRoll.total >= 8 } : null,
    specialCharacteristics,
  };
}
