const fs = require("fs");
const path = require("path");
const chokidar = require("chokidar");

class DatapackLoader {
  constructor() {
    this.registry = {
      items: new Map(),
      recipes: new Map(),
      skills: new Map(),
      dungeons: new Map(),
      enemies: new Map(),
      rooms: new Map(),
      languages: new Map(),
      manifest: {},
    };
    this.rootPath = null;
  }

  init(datapacksRoot, io = null) {
    this.rootPath = datapacksRoot || path.join(__dirname, "../../datapacks");
    if (!fs.existsSync(this.rootPath)) return;
    this.loadAll();
    if (io) this.watch(io);
  }

  deepMerge(target, source) {
    for (const key in source) {
      if (source[key] instanceof Object && key in target) {
        Object.assign(source[key], this.deepMerge(target[key], source[key]));
      }
    }
    Object.assign(target || {}, source);
    return target;
  }

  loadAll() {
    Object.values(this.registry).forEach((val) => {
      if (val instanceof Map) val.clear();
    });
    this.registry.manifest = {};
    let manifestCount = 0;

    const packs = fs
      .readdirSync(this.rootPath, { withFileTypes: true })
      .filter((d) => d.isDirectory())
      .map((d) => d.name)
      .sort((a, b) => (a === "original" ? -1 : 1));

    packs.forEach((packName) => {
      const packPath = path.join(this.rootPath, packName);
      const dataPath = path.join(packPath, "data");
      const langPath = path.join(packPath, "assets/languages");
      const manifestPath = path.join(packPath, "manifest.json");

      if (fs.existsSync(manifestPath)) {
        try {
          const manifestContent = JSON.parse(
            fs.readFileSync(manifestPath, "utf8"),
          );
          this.deepMerge(this.registry.manifest, manifestContent);
          manifestCount++;
        } catch (err) {
          console.error(`❌ Manifest Error [${packName}]:`, err.message);
        }
      }

      if (fs.existsSync(dataPath)) this.loadRecursive(dataPath, packName);
      if (fs.existsSync(langPath)) this.loadLanguages(langPath);
    });

    console.log(
      `🚀 Registry Ready: ${this.registry.items.size} Items, ${this.registry.dungeons.size} Dungeons, ${this.registry.languages.size} Langs, ${manifestCount} Manifests`,
    );
  }
  getRecipe(id) {
    return this.registry.recipes.get(id);
  }

  getRecipesByCategory(category) {
    const allRecipes = Array.from(this.registry.recipes.values());
    return allRecipes.filter((r) => r.category === category);
  }

  getRecipeCategories() {
    const allRecipes = Array.from(this.registry.recipes.values());
    const categories = new Set(
      allRecipes.map((r) => r.category).filter(Boolean),
    );
    return Array.from(categories);
  }
  loadLanguages(langPath) {
    try {
      const files = fs.readdirSync(langPath).filter((f) => f.endsWith(".json"));
      files.forEach((file) => {
        const langCode = path.basename(file, ".json");
        const fullPath = path.join(langPath, file);
        const newContent = JSON.parse(fs.readFileSync(fullPath, "utf8"));

        if (!this.registry.languages.has(langCode)) {
          this.registry.languages.set(langCode, {});
        }

        const currentDict = this.registry.languages.get(langCode);
        this.deepMerge(currentDict, newContent);
      });
    } catch (err) {
      console.error(`❌ Language Load Error:`, err.message);
    }
  }

  watch(io) {
    const watcher = chokidar.watch(this.rootPath, {
      persistent: true,
      ignoreInitial: true,
    });
    watcher.on("all", (event, filePath) => {
      if (filePath.endsWith(".json")) {
        console.log(`🔄 Datapack change detected: ${path.basename(filePath)}`);
        this.loadAll();
        io.emit(
          "admin:log",
          `System: Datapacks reloaded (${path.basename(filePath)})`,
        );
        io.emit("system:data_updated", this.getStaticData());
      }
    });
  }

  loadRecursive(dirPath, packName) {
    const entries = fs.readdirSync(dirPath, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dirPath, entry.name);
      if (entry.isDirectory()) {
        this.loadRecursive(fullPath, packName);
      } else if (entry.isFile() && entry.name.endsWith(".json")) {
        this.parseFile(fullPath, packName);
      }
    }
  }

  parseFile(filePath, packName) {
    try {
      const fileContent = fs.readFileSync(filePath, "utf8");
      const json = JSON.parse(fileContent);
      const typeKey = Object.keys(json)[0];
      const data = json[typeKey];
      if (!data) return;

      const fullId = `${packName}:${data.id}`;

      switch (typeKey) {
        case "armour":
        case "plating":
        case "materials":
        case "weapons":
          data.type = typeKey;
          this.registry.items.set(fullId, data);
          this.registry.items.set(data.id, data);
          break;
        case "recipe":
          const recipeId = json.craft?.id || data.id;
          this.registry.recipes.set(recipeId, { ...json.craft, ...data });
          break;
        case "skills":
          this.registry.skills.set(fullId, data);
          this.registry.skills.set(data.id, data);
          break;
        case "dungeon":
          this.registry.dungeons.set(fullId, data);
          this.registry.dungeons.set(data.id, data);
          break;
        case "hostile":
          this.registry.enemies.set(fullId, data);
          this.registry.enemies.set(data.id, data);
          break;
        case "rooms":
          this.registry.rooms.set(fullId, data);
          this.registry.rooms.set(data.id, data);
          break;
      }
    } catch (err) {
      console.error(`❌ Parse Error [${filePath}]:`, err.message);
    }
  }

  getItem(id) {
    return this.registry.items.get(id);
  }
  getEnemy(id) {
    return this.registry.enemies.get(id);
  }
  getDungeon(id) {
    return this.registry.dungeons.get(id);
  }
  getRoom(id) {
    return this.registry.rooms.get(id);
  }
  getRecipes() {
    return Array.from(this.registry.recipes.values());
  }

  getStaticData() {
    return {
      items: Array.from(this.registry.items.values()),
      recipes: Array.from(this.registry.recipes.values()),
      skills: Array.from(this.registry.skills.values()),
      dungeons: Array.from(this.registry.dungeons.values()),
      enemies: Array.from(this.registry.enemies.values()),
      rooms: Array.from(this.registry.rooms.values()),
      languages: Object.fromEntries(this.registry.languages),
      manifest: this.registry.manifest,
    };
  }
}

module.exports = new DatapackLoader();
