const fs = require('node:fs');
const path = require('node:path');
const genshin = require('genshin-db');

const opts = { matchCategories: true, verboseCategories: true, resultLanguage: 'English', queryLanguages: ['English'] };

function asArray(value) {
  return Array.isArray(value) ? value : [];
}

const characters = asArray(genshin.characters('names', opts))
  .map((entry) => ({
    id: entry.id,
    name: entry.name,
    rarity: entry.rarity,
    element: entry.elementText,
    weapon: entry.weaponText,
  }))
  .filter((entry) => entry.name)
  .sort((a, b) => a.name.localeCompare(b.name));

const artifacts = asArray(genshin.artifacts('names', opts))
  .map((entry) => ({
    id: entry.id,
    name: entry.name,
    rarityList: entry.rarityList ?? [],
    pieces: [entry.flower, entry.plume, entry.sands, entry.goblet, entry.circlet]
      .filter(Boolean)
      .map((piece) => ({ name: piece.name, relicType: piece.relicType })),
  }))
  .filter((entry) => entry.name)
  .sort((a, b) => a.name.localeCompare(b.name));

const artifactPieces = artifacts
  .flatMap((set) =>
    set.pieces.map((piece) => ({
      name: piece.name,
      setName: set.name,
      slot: slotFromRelicType(piece.relicType),
      relicType: piece.relicType,
    })),
  )
  .sort((a, b) => a.name.localeCompare(b.name));

const slotByPiece = Object.fromEntries(artifactPieces.map((piece) => [piece.name, piece.slot]));
const setByPiece = Object.fromEntries(artifactPieces.map((piece) => [piece.name, piece.setName]));
const setToPieces = Object.fromEntries(
  artifacts.map((set) => [set.name, artifactPieces.filter((piece) => piece.setName === set.name).map((piece) => piece.name)]),
);

const mainStats = [
  'Elemental Mastery',
  'Energy Recharge',
  'CRIT Rate',
  'CRIT DMG',
  'Healing Bonus',
  'ATK%',
  'HP%',
  'DEF%',
  'ATK',
  'HP',
  'DEF',
  'Hydro DMG Bonus',
  'Pyro DMG Bonus',
  'Electro DMG Bonus',
  'Cryo DMG Bonus',
  'Dendro DMG Bonus',
  'Anemo DMG Bonus',
  'Geo DMG Bonus',
  'Physical DMG Bonus',
];

const substats = ['CRIT DMG', 'CRIT Rate', 'Energy Recharge', 'Elemental Mastery', 'ATK', 'ATK%', 'HP', 'HP%', 'DEF', 'DEF%'];
const mainStatsBySlot = {
  'Flower of Life': ['HP'],
  'Plume of Death': ['ATK'],
  'Sands of Eon': ['HP%', 'ATK%', 'DEF%', 'Energy Recharge', 'Elemental Mastery'],
  'Goblet of Eonothem': [
    'HP%',
    'ATK%',
    'DEF%',
    'Elemental Mastery',
    'Hydro DMG Bonus',
    'Pyro DMG Bonus',
    'Electro DMG Bonus',
    'Cryo DMG Bonus',
    'Dendro DMG Bonus',
    'Anemo DMG Bonus',
    'Geo DMG Bonus',
    'Physical DMG Bonus',
  ],
  'Circlet of Logos': ['HP%', 'ATK%', 'DEF%', 'Elemental Mastery', 'CRIT Rate', 'CRIT DMG', 'Healing Bonus'],
};

const mainStatValueReferences = {
  'Flower of Life': [{ stat: 'HP', base: 717, max: 4780 }],
  'Plume of Death': [{ stat: 'ATK', base: 47, max: 311 }],
  'Sands of Eon': [
    { stat: 'HP%', base: 7.0, max: 46.6 },
    { stat: 'ATK%', base: 7.0, max: 46.6 },
    { stat: 'DEF%', base: 8.7, max: 58.3 },
    { stat: 'Energy Recharge', base: 7.8, max: 51.8 },
    { stat: 'Elemental Mastery', base: 28, max: 187 },
  ],
  'Goblet of Eonothem': [
    { stat: 'HP%', base: 7.0, max: 46.6 },
    { stat: 'ATK%', base: 7.0, max: 46.6 },
    { stat: 'DEF%', base: 8.7, max: 58.3 },
    { stat: 'Elemental Mastery', base: 28, max: 187 },
    { stat: 'Hydro DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Pyro DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Electro DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Cryo DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Dendro DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Anemo DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Geo DMG Bonus', base: 7.0, max: 46.6 },
    { stat: 'Physical DMG Bonus', base: 8.7, max: 58.3 },
  ],
  'Circlet of Logos': [
    { stat: 'HP%', base: 7.0, max: 46.6 },
    { stat: 'ATK%', base: 7.0, max: 46.6 },
    { stat: 'DEF%', base: 8.7, max: 58.3 },
    { stat: 'Elemental Mastery', base: 28, max: 187 },
    { stat: 'CRIT Rate', base: 4.7, max: 31.1 },
    { stat: 'CRIT DMG', base: 9.3, max: 62.2 },
    { stat: 'Healing Bonus', base: 5.4, max: 35.9 },
  ],
};

const data = {
  schemaVersion: 2,
  generatedAt: new Date().toISOString(),
  source: {
    package: 'genshin-db',
    version: require('genshin-db/package.json').version,
    resultLanguage: opts.resultLanguage,
  },
  sourceVersions: {
    genshinDb: require('genshin-db/package.json').version,
    resultLanguage: opts.resultLanguage,
  },
  sourceVersion: `genshin-db@${require('genshin-db/package.json').version}`,
  characters,
  artifactSets: artifacts,
  artifactPieces,
  slotByPiece,
  setByPiece,
  slots: ['Flower of Life', 'Plume of Death', 'Sands of Eon', 'Goblet of Eonothem', 'Circlet of Logos'],
  mainStats,
  mainStatsBySlot,
  mainStatValueReferences,
  substats,
  stats: {
    main: mainStats,
    mainBySlot: mainStatsBySlot,
    sub: substats,
  },
  aliases: {
    stats: {
      'Crit Damage': 'CRIT DMG',
      'Critical Damage': 'CRIT DMG',
      'Crit Rate': 'CRIT Rate',
      'Critical Rate': 'CRIT Rate',
      'Energy Recharge %': 'Energy Recharge',
      'Elemental Master': 'Elemental Mastery',
      'Elemental Masterie': 'Elemental Mastery',
      'Heal Bonus': 'Healing Bonus',
    },
    textReplacements: {
      'CIT DMG': 'CRIT DMG',
      'CRIT DMG+I': 'CRIT DMG+1',
      'CRIT Rate+Z': 'CRIT Rate+2',
      'Energv Recharge': 'Energy Recharge',
      'Elemental Masterv': 'Elemental Mastery',
      'Equipped;': 'Equipped:',
    },
    slotAliases: {
      'Sands of Eon Vi': 'Sands of Eon',
      'Sands of Eon V': 'Sands of Eon',
      'Sands of Eon 2': 'Sands of Eon',
      'Flower of Life 2': 'Flower of Life',
      'Flower of Lif': 'Flower of Life',
      'Goblet of Eonotherm': 'Goblet of Eonothem',
      'Goblet of Eonothemn': 'Goblet of Eonothem',
      'Circlet of Logas': 'Circlet of Logos',
      'Circlet of Loges': 'Circlet of Logos',
    },
    setAliases: {
      'Viridescent Venere': 'Viridescent Venerer',
      'Maiden Beloved:': 'Maiden Beloved',
      'Gladiators Finale': "Gladiator's Finale",
    },
    pieceAliases: {
      'A Note in Springs Leich': "A Note in Spring's Leich",
      'Viridescent Vencrers Vessel': "Viridescent Venerer's Vessel",
      'Holy Crown of the Believer ': 'Holy Crown of the Believer',
    },
    characterAliases: {
      'Citlall': 'Citlali',
      'Sandrone ': 'Sandrone',
      'Qiqi ': 'Qiqi',
    },
  },
  lookup: {
    normalizedKeys: {
      sets: normalizedMap(artifacts.map((set) => set.name)),
      pieces: normalizedMap(artifactPieces.map((piece) => piece.name)),
      slots: normalizedMap(['Flower of Life', 'Plume of Death', 'Sands of Eon', 'Goblet of Eonothem', 'Circlet of Logos']),
      stats: normalizedMap([...mainStats, ...substats]),
      characters: normalizedMap(characters.map((character) => character.name)),
    },
    goodKeys: {
      sets: Object.fromEntries(artifacts.map((set) => [set.name, goodKey(set.name)])),
      pieces: Object.fromEntries(artifactPieces.map((piece) => [piece.name, goodKey(piece.name)])),
      stats: Object.fromEntries([...mainStats, ...substats].map((stat) => [stat, goodStatKey(stat)])),
      characters: Object.fromEntries(characters.map((character) => [character.name, goodKey(character.name)])),
    },
    setToPieces,
    validation: validateLookupPackage({ artifacts, artifactPieces, characters, mainStats, substats, setToPieces }),
  },
  uiProfiles: {
    artifactDetailEn: {
      language: 'English',
      supportedResolutions: ['1920x1080', '2560x1440', '3840x2160'],
      detailPanel: { x: 0.5, y: 0.05, width: 0.45, height: 0.9 },
      note: 'Relative profile used as scanner contract; runtime crops may tune offsets from review samples.',
    },
  },
};

const outPath = path.join(process.cwd(), 'src', 'data', 'genshinGameData.json');
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, JSON.stringify(data, null, 2) + '\n');
console.log(`generated ${outPath}`);
console.log(`${characters.length} characters, ${artifacts.length} artifact sets`);
console.log(`${artifactPieces.length} artifact pieces, ${mainStats.length} main stats, ${substats.length} substats`);

function slotFromRelicType(relicType) {
  switch (relicType) {
    case 'EQUIP_BRACER':
      return 'Flower of Life';
    case 'EQUIP_NECKLACE':
      return 'Plume of Death';
    case 'EQUIP_SHOES':
      return 'Sands of Eon';
    case 'EQUIP_RING':
      return 'Goblet of Eonothem';
    case 'EQUIP_DRESS':
      return 'Circlet of Logos';
    default:
      return '';
  }
}

function normalizeLookupKey(value) {
  return String(value)
    .toLowerCase()
    .normalize('NFKD')
    .replace(/['’]/g, '')
    .replace(/[^a-z0-9]+/g, '');
}

function normalizedMap(values) {
  return Object.fromEntries(values.filter(Boolean).map((value) => [normalizeLookupKey(value), value]));
}

function goodKey(value) {
  return String(value)
    .replace(/['’]/g, '')
    .replace(/[^A-Za-z0-9]+(.)/g, (_match, next) => String(next).toUpperCase())
    .replace(/^[a-z]/, (first) => first.toUpperCase())
    .replace(/[^A-Za-z0-9]/g, '');
}

function goodStatKey(stat) {
  switch (stat) {
    case 'HP':
      return 'hp';
    case 'HP%':
      return 'hp_';
    case 'ATK':
      return 'atk';
    case 'ATK%':
      return 'atk_';
    case 'DEF':
      return 'def';
    case 'DEF%':
      return 'def_';
    case 'Elemental Mastery':
      return 'eleMas';
    case 'Energy Recharge':
      return 'enerRech_';
    case 'CRIT Rate':
      return 'critRate_';
    case 'CRIT DMG':
      return 'critDMG_';
    case 'Healing Bonus':
      return 'heal_';
    case 'Physical DMG Bonus':
      return 'physical_dmg_';
    default:
      return stat.toLowerCase().replace(' dmg bonus', '_dmg_').replace(/\s+/g, '');
  }
}

function validateLookupPackage({ artifacts, artifactPieces, characters, mainStats, substats, setToPieces }) {
  const errors = [];
  const warnings = [];
  const setNames = new Set(artifacts.map((set) => set.name));
  const slotNames = new Set(['Flower of Life', 'Plume of Death', 'Sands of Eon', 'Goblet of Eonothem', 'Circlet of Logos']);
  const goodSetKeys = new Set();

  for (const set of artifacts) {
    const key = goodKey(set.name);
    if (goodSetKeys.has(key)) errors.push(`Duplicate GOOD set key: ${key}`);
    goodSetKeys.add(key);
    if ((setToPieces[set.name] ?? []).length === 0) warnings.push(`Set has no pieces: ${set.name}`);
  }

  for (const piece of artifactPieces) {
    if (!setNames.has(piece.setName)) errors.push(`Piece ${piece.name} references missing set ${piece.setName}`);
    if (!slotNames.has(piece.slot)) errors.push(`Piece ${piece.name} references missing slot ${piece.slot}`);
  }

  if (!characters.length) warnings.push('No characters generated.');
  if (!mainStats.length || !substats.length) errors.push('Stats were not generated.');

  return {
    valid: errors.length === 0,
    errors,
    warnings,
    summary: {
      artifactSets: artifacts.length,
      artifactPieces: artifactPieces.length,
      characters: characters.length,
      stats: mainStats.length + substats.length,
    },
  };
}
