/**
 * UA2E Roll Helper
 *
 * Handles all percentile roll logic for Unknown Armies 2nd Edition.
 *
 * Roll mechanics:
 *  - Roll 1d100 (1-100, where 100 is read as "00")
 *  - 01  = Crit (best possible outcome)
 *  - 00  = Fumble (worst possible outcome)
 *  - Doubles (11, 22, 33…99) = Match
 *  - Roll must be ≤ target to succeed; higher is better (closer to target)
 *  - Flip-flop: swap tens/ones digits (obsession skill or special circumstances)
 *
 * Check types:
 *  - Minor:       Auto-succeed at 15%+. Roll only for effect.
 *  - Significant: Strong ≤ skill; Weak > skill but ≤ stat; Fail > stat
 *  - Major:       Only ≤ skill succeeds. Hail Mary (no skill) = match or crit only.
 */

const ROLL_TYPES = {
  SIMPLE:      "simple",
  MINOR:       "minor",
  SIGNIFICANT: "significant",
  MAJOR:       "major"
};

/**
 * Interprets a raw roll value (1-100) into its components.
 * @param {number} raw - The raw d100 roll (1-100)
 * @returns {{ raw, tens, ones, isDouble, isCrit, isFumble }}
 */
function interpretRoll(raw) {
  const normalised = raw === 100 ? 0 : raw; // 100 is displayed as "00"
  const tens = Math.floor(normalised / 10);
  const ones = normalised % 10;
  const isCrit   = normalised === 1;
  const isFumble = normalised === 0;  // 00
  const isDouble = (tens === ones) && !isCrit && !isFumble; // 11, 22 … 99
  return { raw: normalised, tens, ones, isDouble, isCrit, isFumble };
}

/**
 * Compute the flip-flopped version of a roll.
 * @param {number} raw - The raw d100 roll (1-100 → normalised 0-99)
 * @returns {number} The flipped roll value
 */
function flipFlop(raw) {
  const normalised = raw === 100 ? 0 : raw;
  const tens = Math.floor(normalised / 10);
  const ones = normalised % 10;
  const flipped = ones * 10 + tens;
  return flipped === 0 ? 100 : flipped; // 00 stays 00
}

/**
 * Determine the outcome of a roll against a target.
 *
 * @param {number} raw          - The raw result (1-100)
 * @param {number} skill        - The skill percentage (0 if unskilled)
 * @param {number} stat         - The governing stat (used as fallback)
 * @param {string} checkType    - "simple" | "minor" | "significant" | "major"
 * @param {boolean} canFlipFlop - Whether the obsession skill flip-flop applies
 * @returns {object} Full result descriptor
 */
export function evaluate(raw, skill, stat, checkType = ROLL_TYPES.MAJOR, canFlipFlop = false) {
  const info = interpretRoll(raw);
  const hasSkill = skill > 0;

  // Decide if we can offer a flip-flop
  const flippedRaw   = flipFlop(raw);
  const flippedInfo  = interpretRoll(flippedRaw);
  const flipAvailable = canFlipFlop && !info.isCrit && !info.isFumble
                        && flippedRaw !== raw;

  // Helper: evaluate a single candidate value
  function grade(candidate, candInfo) {
    if (candInfo.isCrit)   return { degree: "crit",   success: true };
    if (candInfo.isFumble) return { degree: "fumble", success: false };

    switch (checkType) {
      case ROLL_TYPES.SIMPLE: {
        if (candidate <= stat) return { degree: "success", success: true };
        return { degree: "fail", success: false };
      }
      case ROLL_TYPES.MINOR: {
        // Auto-succeed at 15%+ in canon; but if rolling, just check skill
        if (hasSkill && candidate <= skill) return { degree: "success", success: true };
        if (candidate <= stat)             return { degree: "weak",    success: true };
        return { degree: "fail", success: false };
      }
      case ROLL_TYPES.SIGNIFICANT: {
        if (hasSkill && candidate <= skill) return { degree: "strong", success: true };
        if (candidate <= stat)             return { degree: "weak",   success: true };
        return { degree: "fail", success: false };
      }
      case ROLL_TYPES.MAJOR:
      default: {
        if (hasSkill) {
          if (candidate <= skill) return { degree: "success", success: true };
          return { degree: "fail", success: false };
        } else {
          // Hail Mary: only matched successes or crits succeed (stat not reduced)
          if (candInfo.isDouble || candInfo.isCrit) return { degree: "hailmary", success: true };
          return { degree: "fail", success: false };
        }
      }
    }
  }

  const primary = grade(info.raw === 0 ? 100 : info.raw, info);
  // Mark if it's a matched roll (to trigger Cherries etc.)
  primary.isMatch  = info.isDouble;
  primary.isCrit   = info.isCrit;
  primary.isFumble = info.isFumble;
  primary.rolled   = info.raw === 0 ? "00" : String(info.raw).padStart(2, "0");

  if (flipAvailable) {
    const flipped = grade(flippedRaw, flippedInfo);
    primary.flipOption = {
      rolled:   String(flippedRaw).padStart(2, "0"),
      isMatch:  flippedInfo.isDouble,
      isCrit:   flippedInfo.isCrit,
      isFumble: flippedInfo.isFumble,
      ...flipped
    };
  }

  return primary;
}

/**
 * Roll 1d100 and evaluate the result, posting a chat message.
 *
 * @param {object} options
 * @param {string}  options.actorId      - The actor performing the roll
 * @param {string}  options.label        - Human-readable label (e.g. "Firearms")
 * @param {number}  options.skill        - Skill percentage (0 = unskilled)
 * @param {number}  options.stat         - Governing stat percentage
 * @param {string}  options.checkType    - "simple" | "minor" | "significant" | "major"
 * @param {boolean} options.canFlipFlop  - Whether obsession flip-flop is available
 * @param {number}  [options.shift=0]    - Modifier applied to skill before rolling
 * @param {number}  [options.minRoll=0]  - Minimum roll required
 */
export async function rollSkill({
  actorId,
  label = "Skill Roll",
  skill  = 0,
  stat   = 50,
  checkType = ROLL_TYPES.MAJOR,
  canFlipFlop = false,
  shift  = 0,
  minRoll = 0
} = {}) {
  // Apply shift
  const effectiveSkill = Math.max(0, Math.min(99, skill + shift));
  const effectiveStat  = Math.max(0, Math.min(99, stat));

  const roll = new Roll("1d100");
  await roll.evaluate();
  const raw = roll.total;

  const result = evaluate(raw, effectiveSkill, effectiveStat, checkType, canFlipFlop);

  // Check minimum roll requirement
  const rolledNum = raw === 100 ? 0 : raw;
  const meetsMinimum = minRoll === 0 || rolledNum >= minRoll;

  const templateData = {
    label,
    skill:          effectiveSkill,
    stat:           effectiveStat,
    shift,
    minRoll,
    checkType,
    canFlipFlop,
    result,
    meetsMinimum,
    rolledDisplay:  result.rolled,
    ROLL_TYPES
  };

  const content = await renderTemplate(
    "systems/unknown-armies-v2/templates/chat/roll-result.hbs",
    templateData
  );

  const actor = game.actors.get(actorId);
  await ChatMessage.implementation.create({
    speaker: ChatMessage.getSpeaker({ actor }),
    rolls:   [roll],
    content,
    sound:   CONFIG.sounds.dice
  });

  return result;
}

/**
 * Show a dialog for a skill check, then execute the roll.
 *
 * @param {object} options
 * @param {string}  options.actorId
 * @param {string}  options.label
 * @param {number}  options.skill
 * @param {number}  options.stat
 * @param {boolean} options.canFlipFlop
 */
export async function skillCheckDialog({
  actorId,
  label  = "Skill Check",
  skill  = 0,
  stat   = 50,
  canFlipFlop = false,
  defaultCheckType = ROLL_TYPES.MINOR
} = {}) {
  return new Promise(resolve => {
    const content = `
      <form class="ua2e-roll-dialog">
        <div class="form-group">
          <label>${game.i18n.localize("UA2E.Roll.CheckType")}</label>
          <div class="checktype-checkboxes">
            <label><input type="checkbox" name="checkType" value="major" ${defaultCheckType === ROLL_TYPES.MAJOR ? "checked" : ""} /> ${game.i18n.localize("UA2E.Roll.Major")}</label>
            <label><input type="checkbox" name="checkType" value="significant" ${defaultCheckType === ROLL_TYPES.SIGNIFICANT ? "checked" : ""} /> ${game.i18n.localize("UA2E.Roll.Significant")}</label>
            <label><input type="checkbox" name="checkType" value="minor" ${defaultCheckType === ROLL_TYPES.MINOR ? "checked" : ""} /> ${game.i18n.localize("UA2E.Roll.Minor")}</label>
          </div>
        </div>
        <div class="form-group">
          <label>${game.i18n.localize("UA2E.Roll.Shift")}</label>
          <input type="number" name="shift" value="0" />
        </div>
        <div class="form-group">
          <label>${game.i18n.localize("UA2E.Roll.MinimumRoll")}</label>
          <input type="number" name="minRoll" value="0" min="0" max="99" />
        </div>
        <p class="hint">
          ${game.i18n.localize("UA2E.Roll.SkillLabel")}: <strong>${skill || game.i18n.localize("UA2E.Roll.Unskilled")}</strong>
          &nbsp;|&nbsp;
          ${game.i18n.localize("UA2E.Roll.StatLabel")}: <strong>${stat}</strong>
        </p>
      </form>
    `;

    new Dialog({
      title: `${label} – ${game.i18n.localize("UA2E.Roll.DialogTitle")}`,
      content,
      buttons: {
        roll: {
          icon:  '<i class="fas fa-dice-d10"></i>',
          label: game.i18n.localize("UA2E.Roll.Roll"),
          callback: async html => {
            const form      = html[0].querySelector("form");
            const selected = Array.from(form.querySelectorAll('input[name="checkType"]')).find(input => input.checked);
            const checkType = selected?.value ?? defaultCheckType;
            const shift     = parseInt(form.shift.value) || 0;
            const minRoll   = parseInt(form.minRoll.value) || 0;
            const r = await rollSkill({ actorId, label, skill, stat, checkType, canFlipFlop, shift, minRoll });
            resolve(r);
          }
        },
        cancel: {
          icon:  '<i class="fas fa-times"></i>',
          label: game.i18n.localize("Cancel"),
          callback: () => resolve(null)
        }
      },
      render: html => {
        const checkboxes = html[0].querySelectorAll('input[name="checkType"]');
        checkboxes.forEach(box => {
          box.addEventListener("change", () => {
            if (!box.checked) {
              box.checked = true;
              return;
            }
            checkboxes.forEach(other => {
              if (other !== box) other.checked = false;
            });
          });
        });
      },
      default: "roll"
    }).render(true);
  });
}

export { ROLL_TYPES };
