import { expect } from "@esm-bundle/chai";
import { GameLoop } from "../../src/core/GameLoop.js";
import { VoxelGrid } from "../../src/grid/VoxelGrid.js";
import { UnitManager } from "../../src/managers/UnitManager.js";
import { EffectProcessor } from "../../src/systems/EffectProcessor.js";
import { itemRegistry } from "../../src/managers/ItemRegistry.js";

describe("Systems: Passive Item Effects", function () {
  let gameLoop;
  let grid;
  let unitManager;
  let effectProcessor;
  let sourceUnit;
  let targetUnit;
  let mockRegistry;

  beforeEach(async () => {
    // Create mock registry
    mockRegistry = new Map();
    mockRegistry.set("CLASS_VANGUARD", {
      id: "CLASS_VANGUARD",
      name: "Vanguard",
      type: "EXPLORER",
      base_stats: {
        health: 100,
        attack: 10,
        defense: 5,
        magic: 5,
        speed: 10,
        movement: 4,
      },
    });
    mockRegistry.set("ENEMY_GOBLIN", {
      id: "ENEMY_GOBLIN",
      name: "Goblin",
      type: "ENEMY",
      stats: {
        health: 50,
        attack: 8,
        defense: 3,
        magic: 0,
      },
    });

    unitManager = new UnitManager(mockRegistry);
    grid = new VoxelGrid(20, 5, 20);

    // Create walkable floor
    for (let x = 0; x < 20; x++) {
      for (let z = 0; z < 20; z++) {
        grid.setCell(x, 0, z, 1); // Floor
        grid.setCell(x, 1, z, 0); // Air (walkable)
        grid.setCell(x, 2, z, 0); // Headroom
      }
    }

    effectProcessor = new EffectProcessor(grid, unitManager);

    // Create test units
    sourceUnit = unitManager.createUnit("CLASS_VANGUARD", "PLAYER");
    sourceUnit.position = { x: 5, y: 1, z: 5 };
    sourceUnit.currentHealth = 100;
    sourceUnit.maxHealth = 100;
    grid.placeUnit(sourceUnit, sourceUnit.position);

    targetUnit = unitManager.createUnit("ENEMY_GOBLIN", "ENEMY");
    targetUnit.position = { x: 6, y: 1, z: 5 };
    targetUnit.currentHealth = 50;
    targetUnit.maxHealth = 50;
    grid.placeUnit(targetUnit, targetUnit.position);

    // Load items if needed
    if (itemRegistry.items.size === 0) {
      await itemRegistry.loadAll();
    }
  });

  describe("Integration Point 2: Passive Item Effects", () => {
    it("should process ON_DAMAGED trigger when unit takes damage", () => {
      // Create a mock item with ON_DAMAGED passive
      const mockItem = {
        id: "ITEM_THORNS",
        name: "Thorns Armor",
        type: "ARMOR",
        passives: [
          {
            trigger: "ON_DAMAGED",
            action: "DAMAGE",
            params: {
              power: 5,
              element: "PHYSICAL",
              target: "SOURCE",
            },
          },
        ],
      };

      // Mock itemRegistry
      const originalGet = itemRegistry.get;
      itemRegistry.get = (id) => {
        if (id === "ITEM_THORNS") return mockItem;
        return originalGet.call(itemRegistry, id);
      };

      // Equip item to target unit
      targetUnit.loadout = {
        mainHand: null,
        offHand: null,
        body: { defId: "ITEM_THORNS", uid: "test-thorns" },
        accessory: null,
        belt: [null, null],
      };

      // Create GameLoop instance (simplified for testing)
      const gameLoop = {
        effectProcessor,
        inventoryManager: {
          itemRegistry,
        },
        processPassiveItemEffects: function (unit, trigger, context) {
          if (!unit || !unit.loadout || !this.effectProcessor || !this.inventoryManager) {
            return;
          }

          const equippedItems = [
            unit.loadout.mainHand,
            unit.loadout.offHand,
            unit.loadout.body,
            unit.loadout.accessory,
            ...(unit.loadout.belt || []),
          ].filter(Boolean);

          for (const itemInstance of equippedItems) {
            if (!itemInstance || !itemInstance.defId) continue;

            const itemDef = this.inventoryManager.itemRegistry.get(itemInstance.defId);
            if (!itemDef || !itemDef.passives) continue;

            for (const passive of itemDef.passives) {
              if (passive.trigger !== trigger) continue;

              const effectDef = {
                type: passive.action,
                power: passive.params.power,
                element: passive.params.element,
              };

              let target = context.target || context.source || unit;
              if (passive.params.target === "SOURCE" && context.source) {
                target = context.source;
              }

              this.effectProcessor.process(effectDef, unit, target);
            }
          }
        },
      };

      const initialSourceHP = sourceUnit.currentHealth;
      const sourceDefense = sourceUnit.baseStats.defense || 0;
      const expectedThornsDamage = Math.max(0, 5 - sourceDefense); // 5 thorns - defense

      // Apply damage to target (this should trigger ON_DAMAGED)
      const damageEffect = {
        type: "DAMAGE",
        power: 10,
      };
      effectProcessor.process(damageEffect, sourceUnit, targetUnit);

      // Process passive effects
      gameLoop.processPassiveItemEffects(targetUnit, "ON_DAMAGED", {
        source: sourceUnit,
        damageAmount: 10,
      });

      // Source should have taken thorns damage (reduced by defense)
      if (expectedThornsDamage > 0) {
        expect(sourceUnit.currentHealth).to.be.lessThan(initialSourceHP);
        expect(sourceUnit.currentHealth).to.equal(initialSourceHP - expectedThornsDamage);
      } else {
        // If defense blocks all damage, health should be unchanged
        expect(sourceUnit.currentHealth).to.equal(initialSourceHP);
      }

      // Restore original get method
      itemRegistry.get = originalGet;
    });

    it("should process ON_DAMAGE_DEALT trigger when unit deals damage", () => {
      // Create a mock item with ON_DAMAGE_DEALT passive (e.g., lifesteal)
      const mockItem = {
        id: "ITEM_LIFESTEAL",
        name: "Lifesteal Blade",
        type: "WEAPON",
        passives: [
          {
            trigger: "ON_DAMAGE_DEALT",
            action: "HEAL",
            params: {
              power: 2,
            },
            target: "SELF",
          },
        ],
      };

      const originalGet = itemRegistry.get;
      itemRegistry.get = (id) => {
        if (id === "ITEM_LIFESTEAL") return mockItem;
        return originalGet.call(itemRegistry, id);
      };

      sourceUnit.loadout = {
        mainHand: { defId: "ITEM_LIFESTEAL", uid: "test-lifesteal" },
        offHand: null,
        body: null,
        accessory: null,
        belt: [null, null],
      };

      const gameLoop = {
        effectProcessor,
        inventoryManager: {
          itemRegistry,
        },
        processPassiveItemEffects: function (unit, trigger, context) {
          if (!unit || !unit.loadout || !this.effectProcessor || !this.inventoryManager) {
            return;
          }

          const equippedItems = [
            unit.loadout.mainHand,
            unit.loadout.offHand,
            unit.loadout.body,
            unit.loadout.accessory,
            ...(unit.loadout.belt || []),
          ].filter(Boolean);

          for (const itemInstance of equippedItems) {
            if (!itemInstance || !itemInstance.defId) continue;

            const itemDef = this.inventoryManager.itemRegistry.get(itemInstance.defId);
            if (!itemDef || !itemDef.passives) continue;

            for (const passive of itemDef.passives) {
              if (passive.trigger !== trigger) continue;

              const effectDef = {
                type: passive.action,
                power: passive.params.power,
              };

              let target = context.target || context.source || unit;
              if (passive.params.target === "SELF" || passive.target === "SELF") {
                target = unit;
              }

              this.effectProcessor.process(effectDef, unit, target);
            }
          }
        },
      };

      const initialSourceHP = sourceUnit.currentHealth;
      sourceUnit.currentHealth = 80; // Set to less than max

      // Deal damage (this should trigger ON_DAMAGE_DEALT)
      const damageEffect = {
        type: "DAMAGE",
        power: 10,
      };
      effectProcessor.process(damageEffect, sourceUnit, targetUnit);

      // Process passive effects
      gameLoop.processPassiveItemEffects(sourceUnit, "ON_DAMAGE_DEALT", {
        target: targetUnit,
        damageAmount: 10,
      });

      // Source should have healed from lifesteal
      expect(sourceUnit.currentHealth).to.be.greaterThan(80);

      itemRegistry.get = originalGet;
    });

    it("should not process passive effects if trigger doesn't match", () => {
      const mockItem = {
        id: "ITEM_OTHER",
        name: "Other Item",
        type: "ARMOR",
        passive_effects: [
          {
            trigger: "ON_HEAL_DEALT", // Different trigger
            action: "HEAL",
            params: { power: 5 },
          },
        ],
      };

      const originalGet = itemRegistry.get;
      itemRegistry.get = (id) => {
        if (id === "ITEM_OTHER") return mockItem;
        return originalGet.call(itemRegistry, id);
      };

      targetUnit.loadout = {
        mainHand: null,
        offHand: null,
        body: { defId: "ITEM_OTHER", uid: "test-other" },
        accessory: null,
        belt: [null, null],
      };

      const gameLoop = {
        effectProcessor,
        inventoryManager: {
          itemRegistry,
        },
        processPassiveItemEffects: function (unit, trigger, context) {
          if (!unit || !unit.loadout || !this.effectProcessor || !this.inventoryManager) {
            return;
          }

          const equippedItems = [
            unit.loadout.mainHand,
            unit.loadout.offHand,
            unit.loadout.body,
            unit.loadout.accessory,
            ...(unit.loadout.belt || []),
          ].filter(Boolean);

          for (const itemInstance of equippedItems) {
            if (!itemInstance || !itemInstance.defId) continue;

            const itemDef = this.inventoryManager.itemRegistry.get(itemInstance.defId);
            if (!itemDef || !itemDef.passives) continue;

            for (const passive of itemDef.passives) {
              if (passive.trigger !== trigger) continue;
              // Should not reach here for ON_DAMAGED trigger
              expect.fail("Should not process passive with different trigger");
            }
          }
        },
      };

      // Process ON_DAMAGED trigger (but item has ON_HEAL_DEALT)
      gameLoop.processPassiveItemEffects(targetUnit, "ON_DAMAGED", {
        source: sourceUnit,
        damageAmount: 10,
      });

      // Should not have processed anything
      itemRegistry.get = originalGet;
    });
  });
});

