import { expect } from "@esm-bundle/chai";
import sinon from "sinon";
// Import to register custom element
import "../../src/ui/screens/BarracksScreen.js";
import { gameStateManager } from "../../src/core/GameStateManager.js";
import vanguardDef from "../../src/assets/data/classes/vanguard.json" with {
  type: "json",
};

describe("UI: BarracksScreen", () => {
  let element;
  let container;
  let mockPersistence;
  let mockRosterManager;
  let mockHubStash;
  let mockGameLoop;

  beforeEach(async () => {
    container = document.createElement("div");
    document.body.appendChild(container);
    element = document.createElement("barracks-screen");
    container.appendChild(element);

    // Wait for element to be defined
    await element.updateComplete;

    // Create mock hub stash
    mockHubStash = {
      currency: {
        aetherShards: 1000,
        ancientCores: 0,
      },
    };

    // Create mock persistence
    mockPersistence = {
      loadRun: sinon.stub().resolves({
        inventory: {
          runStash: {
            currency: {
              aetherShards: 500,
              ancientCores: 0,
            },
          },
        },
      }),
      saveRoster: sinon.stub().resolves(),
      saveHubStash: sinon.stub().resolves(),
    };

    // Create mock class registry
    const mockClassRegistry = new Map();
    mockClassRegistry.set("CLASS_VANGUARD", vanguardDef);

    // Create mock game loop with class registry
    mockGameLoop = {
      classRegistry: mockClassRegistry,
    };

    // Create mock roster with test units
    const testRoster = [
      {
        id: "UNIT_1",
        name: "Valerius",
        classId: "CLASS_VANGUARD",
        activeClassId: "CLASS_VANGUARD",
        status: "READY",
        classMastery: {
          CLASS_VANGUARD: {
            level: 3,
            xp: 150,
            skillPoints: 2,
            unlockedNodes: [],
          },
        },
        history: { missions: 2, kills: 5 },
      },
      {
        id: "UNIT_2",
        name: "Aria",
        classId: "CLASS_VANGUARD",
        activeClassId: "CLASS_VANGUARD",
        status: "INJURED",
        currentHealth: 60, // Injured unit with stored HP
        classMastery: {
          CLASS_VANGUARD: {
            level: 2,
            xp: 80,
            skillPoints: 1,
            unlockedNodes: [],
          },
        },
        history: { missions: 1, kills: 2 },
      },
      {
        id: "UNIT_3",
        name: "Kael",
        classId: "CLASS_VANGUARD",
        activeClassId: "CLASS_VANGUARD",
        status: "READY",
        classMastery: {
          CLASS_VANGUARD: {
            level: 5,
            xp: 300,
            skillPoints: 3,
            unlockedNodes: [],
          },
        },
        history: { missions: 5, kills: 12 },
      },
    ];

    // Create mock roster manager
    mockRosterManager = {
      roster: testRoster,
      rosterLimit: 12,
      getDeployableUnits: sinon.stub().returns(testRoster.filter((u) => u.status === "READY")),
      save: sinon.stub().returns({
        roster: testRoster,
        graveyard: [],
      }),
    };

    // Replace gameStateManager properties with mocks
    gameStateManager.persistence = mockPersistence;
    gameStateManager.rosterManager = mockRosterManager;
    gameStateManager.hubStash = mockHubStash;
    gameStateManager.gameLoop = mockGameLoop;
  });

  afterEach(() => {
    if (container && container.parentNode) {
      container.parentNode.removeChild(container);
    }
  });

  // Helper to wait for LitElement update
  async function waitForUpdate() {
    await element.updateComplete;
    await new Promise((resolve) => setTimeout(resolve, 10));
  }

  // Helper to query shadow DOM
  function queryShadow(selector) {
    return element.shadowRoot?.querySelector(selector);
  }

  function queryShadowAll(selector) {
    return element.shadowRoot?.querySelectorAll(selector) || [];
  }

  describe("CoA 1: Roster Synchronization", () => {
    it("should load roster from RosterManager on connectedCallback", async () => {
      await waitForUpdate();
      // Wait for async _loadRoster to complete
      let attempts = 0;
      while (element.units.length === 0 && attempts < 20) {
        await new Promise((resolve) => setTimeout(resolve, 50));
        attempts++;
      }

      expect(element.units.length).to.equal(3);
      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.equal(3);
    });

    it("should display roster count correctly", async () => {
      await waitForUpdate();

      const rosterCount = queryShadow(".roster-count");
      expect(rosterCount).to.exist;
      expect(rosterCount.textContent).to.include("3/12");
    });

    it("should update roster when unit is dismissed", async () => {
      await waitForUpdate();

      // Select a unit first
      const unitCards = queryShadowAll(".unit-card");
      unitCards[0].click();
      await waitForUpdate();

      // Mock confirm to return true
      const originalConfirm = window.confirm;
      window.confirm = sinon.stub().returns(true);

      // Click dismiss button
      const dismissButton = queryShadow(".btn-danger");
      expect(dismissButton).to.exist;
      dismissButton.click();
      await waitForUpdate();

      // Restore confirm
      window.confirm = originalConfirm;

      // Verify unit was removed
      const updatedCards = queryShadowAll(".unit-card");
      expect(updatedCards).to.have.length(2);
      expect(mockRosterManager.roster).to.have.length(2);
      expect(mockPersistence.saveRoster.called).to.be.true;
    });
  });

  describe("CoA 2: Healing Transaction", () => {
    it("should calculate heal cost correctly", async () => {
      await waitForUpdate();

      // Select injured unit
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      expect(injuredCard).to.exist;
      injuredCard.click();
      await waitForUpdate();

      // Check heal button exists and shows cost
      const healButton = queryShadow(".btn-primary");
      expect(healButton).to.exist;
      expect(healButton.textContent).to.include("Treat Wounds");
    });

    it("should heal unit when heal button is clicked", async () => {
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Select injured unit (UNIT_2 with 60 HP)
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      expect(injuredCard).to.exist;
      injuredCard.click();
      await waitForUpdate();

      // Get the selected unit to check actual maxHp
      const selectedUnit = element._getSelectedUnit();
      expect(selectedUnit).to.exist;
      const expectedCost = Math.ceil((selectedUnit.maxHp - selectedUnit.currentHp) * 0.5);

      const healButton = queryShadow(".btn-primary");
      expect(healButton).to.exist;
      expect(healButton.textContent).to.include(expectedCost.toString());

      // Click heal
      healButton.click();
      await waitForUpdate();

      // Verify unit was healed
      const injuredUnit = mockRosterManager.roster.find((u) => u.id === "UNIT_2");
      expect(injuredUnit.currentHealth).to.equal(selectedUnit.maxHp); // Max HP
      expect(injuredUnit.status).to.equal("READY");

      // Verify currency was deducted
      const expectedRemaining = 1000 - expectedCost;
      expect(mockHubStash.currency.aetherShards).to.equal(expectedRemaining);

      // Verify save was called
      expect(mockPersistence.saveRoster.called).to.be.true;
      expect(mockPersistence.saveHubStash.called).to.be.true;
    });

    it("should disable heal button when unit has full HP", async () => {
      await waitForUpdate();

      // Select healthy unit
      const unitCards = queryShadowAll(".unit-card");
      const readyCard = Array.from(unitCards).find(
        (card) => !card.classList.contains("injured")
      );
      readyCard.click();
      await waitForUpdate();

      // Check that heal button is disabled or shows "Full Health"
      const healButton = queryShadow(".btn:disabled");
      expect(healButton).to.exist;
      expect(healButton.textContent).to.include("Full Health");
    });

    it("should show insufficient funds message when wallet is too low", async () => {
      // Set low wallet balance
      mockHubStash.currency.aetherShards = 10;
      element.wallet = { aetherShards: 10, ancientCores: 0 };
      await waitForUpdate();

      // Select injured unit
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      injuredCard.click();
      await waitForUpdate();

      // Check for insufficient funds message
      const healCost = queryShadow(".heal-cost");
      expect(healCost).to.exist;
      expect(healCost.textContent).to.include("Insufficient funds");

      // Heal button should be disabled
      const healButton = queryShadow(".btn-primary");
      expect(healButton).to.exist;
      expect(healButton.disabled).to.be.true;
    });

    it("should dispatch wallet-updated event after healing", async () => {
      await waitForUpdate();
      // Wait for units to load
      let attempts = 0;
      while (element.units.length === 0 && attempts < 10) {
        await new Promise((resolve) => setTimeout(resolve, 50));
        attempts++;
      }

      let walletUpdatedEvent = null;
      const handler = (e) => {
        walletUpdatedEvent = e;
      };
      window.addEventListener("wallet-updated", handler);

      // Select injured unit and heal
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      expect(injuredCard).to.exist;
      injuredCard.click();
      await waitForUpdate();

      const healButton = queryShadow(".btn-primary");
      expect(healButton).to.exist;
      healButton.click();
      await waitForUpdate();
      
      // Wait for event dispatch
      attempts = 0;
      while (!walletUpdatedEvent && attempts < 20) {
        await new Promise((resolve) => setTimeout(resolve, 50));
        attempts++;
      }

      expect(walletUpdatedEvent).to.exist;
      expect(walletUpdatedEvent.detail.wallet).to.exist;
      expect(walletUpdatedEvent.detail.wallet.aetherShards).to.be.lessThan(1000);

      window.removeEventListener("wallet-updated", handler);
    });
  });

  describe("CoA 3: Navigation", () => {
    it("should dispatch open-character-sheet event when Inspect is clicked", async () => {
      await waitForUpdate();
      // Wait for units to load
      let attempts = 0;
      while (element.units.length === 0 && attempts < 10) {
        await new Promise((resolve) => setTimeout(resolve, 50));
        attempts++;
      }

      let characterSheetEvent = null;
      const handler = (e) => {
        characterSheetEvent = e;
      };
      window.addEventListener("open-character-sheet", handler);

      // Find UNIT_1 (Valerius) in the list
      const valeriusIndex = element.units.findIndex((u) => u.id === "UNIT_1");
      expect(valeriusIndex).to.be.greaterThan(-1);

      // Select UNIT_1
      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.be.greaterThan(0);
      // Find the card that contains "Valerius"
      const valeriusCard = Array.from(unitCards).find((card) =>
        card.textContent.includes("Valerius")
      );
      expect(valeriusCard).to.exist;
      valeriusCard.click();
      await waitForUpdate();

      // Verify selection
      expect(element.selectedUnitId).to.equal("UNIT_1");

      // Click inspect button (first action button)
      const actionButtons = queryShadowAll(".action-button");
      expect(actionButtons.length).to.be.greaterThan(0);
      actionButtons[0].click();
      await waitForUpdate();

      // Wait for event
      attempts = 0;
      while (!characterSheetEvent && attempts < 20) {
        await new Promise((resolve) => setTimeout(resolve, 50));
        attempts++;
      }

      expect(characterSheetEvent).to.exist;
      expect(characterSheetEvent.detail.unitId).to.equal("UNIT_1");
      expect(characterSheetEvent.detail.unit).to.exist;

      window.removeEventListener("open-character-sheet", handler);
    });

    it("should maintain selection state when character sheet is opened", async () => {
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Find injured unit (UNIT_2) in the list
      const injuredUnitIndex = element.units.findIndex((u) => u.id === "UNIT_2");
      expect(injuredUnitIndex).to.be.greaterThan(-1);

      // Select the injured unit
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      expect(injuredCard).to.exist;
      injuredCard.click();
      await waitForUpdate();

      expect(element.selectedUnitId).to.equal("UNIT_2");

      // Open character sheet (doesn't change selection)
      const actionButtons = queryShadowAll(".action-button");
      expect(actionButtons.length).to.be.greaterThan(0);
      actionButtons[0].click(); // Inspect button

      // Selection should remain
      expect(element.selectedUnitId).to.equal("UNIT_2");
    });
  });

  describe("CoA 4: Selection Persistence", () => {
    it("should maintain selection when roster is re-sorted", async () => {
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Find injured unit (UNIT_2) and select it
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      expect(injuredCard).to.exist;
      injuredCard.click();
      await waitForUpdate();

      expect(element.selectedUnitId).to.equal("UNIT_2");

      // Change sort
      const sortButtons = queryShadowAll(".sort-button");
      expect(sortButtons.length).to.be.greaterThan(0);
      sortButtons[1].click(); // Name sort
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Selection should persist
      expect(element.selectedUnitId).to.equal("UNIT_2");

      // Verify selected card is still highlighted
      const selectedCard = queryShadow(".unit-card.selected");
      expect(selectedCard).to.exist;
    });
  });

  describe("Filtering", () => {
    it("should filter units by READY status", async () => {
      await waitForUpdate();

      const filterButtons = queryShadowAll(".filter-button");
      const readyFilter = Array.from(filterButtons).find((btn) =>
        btn.textContent.includes("Ready")
      );
      readyFilter.click();
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards).to.have.length(2); // Only READY units
    });

    it("should filter units by INJURED status", async () => {
      await waitForUpdate();

      const filterButtons = queryShadowAll(".filter-button");
      const injuredFilter = Array.from(filterButtons).find((btn) =>
        btn.textContent.includes("Injured")
      );
      injuredFilter.click();
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards).to.have.length(1); // Only INJURED unit
      expect(unitCards[0].classList.contains("injured")).to.be.true;
    });

    it("should show all units when ALL filter is selected", async () => {
      await waitForUpdate();

      // First filter to INJURED
      const filterButtons = queryShadowAll(".filter-button");
      const injuredFilter = Array.from(filterButtons).find((btn) =>
        btn.textContent.includes("Injured")
      );
      injuredFilter.click();
      await waitForUpdate();

      // Then switch to ALL
      const allFilter = Array.from(filterButtons).find((btn) =>
        btn.textContent.includes("All")
      );
      allFilter.click();
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards).to.have.length(3); // All units
    });
  });

  describe("Sorting", () => {
    it("should sort units by level (descending)", async () => {
      await waitForUpdate();

      // Default sort should be LEVEL_DESC
      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.be.greaterThan(0);

      // First unit should be highest level (UNIT_3, level 5)
      const firstCard = unitCards[0];
      expect(firstCard.textContent).to.include("Kael");
    });

    it("should sort units by name (ascending)", async () => {
      await waitForUpdate();

      const sortButtons = queryShadowAll(".sort-button");
      const nameSort = Array.from(sortButtons).find((btn) =>
        btn.textContent.includes("Name")
      );
      nameSort.click();
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.be.greaterThan(0);
      // First should be alphabetically first (Aria)
      expect(unitCards[0].textContent).to.include("Aria");
    });

    it("should sort units by HP (ascending)", async () => {
      await waitForUpdate();

      const sortButtons = queryShadowAll(".sort-button");
      const hpSort = Array.from(sortButtons).find((btn) =>
        btn.textContent.includes("HP")
      );
      hpSort.click();
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.be.greaterThan(0);
      // First should be lowest HP (injured unit)
      expect(unitCards[0].classList.contains("injured")).to.be.true;
    });
  });

  describe("Unit Card Rendering", () => {
    it("should render unit cards with correct information", async () => {
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      const unitCards = queryShadowAll(".unit-card");
      expect(unitCards.length).to.equal(3);

      // Check that all units are rendered (order may vary by sort)
      const allNames = Array.from(unitCards).map((card) => card.textContent);
      expect(allNames.some((text) => text.includes("Valerius"))).to.be.true;
      expect(allNames.some((text) => text.includes("Aria"))).to.be.true;
      expect(allNames.some((text) => text.includes("Kael"))).to.be.true;
    });

    it("should show HP bar with correct percentage", async () => {
      await waitForUpdate();

      // Find injured unit card
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );

      const hpBar = injuredCard.querySelector(".progress-bar-fill");
      expect(hpBar).to.exist;
      // Should be around 50% (60/120)
      const width = hpBar.style.width;
      expect(width).to.include("%");
    });

    it("should highlight selected unit card", async () => {
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      unitCards[0].click();
      await waitForUpdate();

      const selectedCard = queryShadow(".unit-card.selected");
      expect(selectedCard).to.exist;
      expect(selectedCard).to.equal(unitCards[0]);
    });
  });

  describe("Detail Sidebar", () => {
    it("should show empty state when no unit is selected", async () => {
      await waitForUpdate();

      const emptyState = queryShadow(".empty-state");
      expect(emptyState).to.exist;
      expect(emptyState.textContent).to.include("Select a unit");
    });

    it("should display unit details when unit is selected", async () => {
      await waitForUpdate();
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Find Valerius unit
      const valeriusUnit = element.units.find((u) => u.name === "Valerius");
      expect(valeriusUnit).to.exist;

      // Find and click the card for Valerius
      const unitCards = queryShadowAll(".unit-card");
      const valeriusCard = Array.from(unitCards).find((card) =>
        card.textContent.includes("Valerius")
      );
      expect(valeriusCard).to.exist;
      valeriusCard.click();
      await waitForUpdate();

      const detailSidebar = queryShadow(".detail-sidebar");
      expect(detailSidebar).to.exist;
      expect(detailSidebar.textContent).to.include("Valerius");
      expect(detailSidebar.textContent).to.include("Level");
    });

    it("should show heal button for injured units", async () => {
      await waitForUpdate();

      // Select injured unit
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      injuredCard.click();
      await waitForUpdate();

      const healButton = queryShadow(".btn-primary");
      expect(healButton).to.exist;
      expect(healButton.textContent).to.include("Treat Wounds");
    });

    it("should show dismiss button", async () => {
      await waitForUpdate();

      const unitCards = queryShadowAll(".unit-card");
      unitCards[0].click();
      await waitForUpdate();

      const dismissButton = queryShadow(".btn-danger");
      expect(dismissButton).to.exist;
      expect(dismissButton.textContent).to.include("Dismiss");
    });
  });

  describe("Close Functionality", () => {
    it("should dispatch close-barracks event when close button is clicked", async () => {
      await waitForUpdate();

      let closeEvent = null;
      element.addEventListener("close-barracks", (e) => {
        closeEvent = e;
      });

      const closeButton = queryShadow(".btn-close");
      expect(closeButton).to.exist;
      closeButton.click();

      expect(closeEvent).to.exist;
    });
  });

  describe("HP Calculation", () => {
    it("should calculate maxHp from class definition and level", async () => {
      await waitForUpdate();

      // UNIT_1 is level 3, should have base + (3-1) * growth
      // Vanguard base health: 120, growth: 10
      // Expected: 120 + (3-1) * 10 = 140
      const unitCards = queryShadowAll(".unit-card");
      unitCards[0].click();
      await waitForUpdate();

      const detailSidebar = queryShadow(".detail-sidebar");
      // Should show HP values
      expect(detailSidebar.textContent).to.match(/\d+\s*\/\s*\d+/);
    });

    it("should use stored currentHealth if available", async () => {
      await waitForUpdate();

      // UNIT_2 has currentHealth: 60 stored
      const unitCards = queryShadowAll(".unit-card");
      const injuredCard = Array.from(unitCards).find((card) =>
        card.classList.contains("injured")
      );
      injuredCard.click();
      await waitForUpdate();

      const detailSidebar = queryShadow(".detail-sidebar");
      // Should show 60 in HP display
      expect(detailSidebar.textContent).to.include("60");
    });
  });
});

