import { expect } from "@esm-bundle/chai";
import sinon from "sinon";
import { HubScreen } from "../../src/ui/screens/hub-screen.js";
import { gameStateManager } from "../../src/core/GameStateManager.js";

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

  beforeEach(() => {
    // Set up mocks BEFORE creating element so connectedCallback can use them
    mockPersistence = {
      loadRun: sinon.stub().resolves(null),
    };

    mockRosterManager = {
      roster: [],
      getDeployableUnits: sinon.stub().returns([]),
    };

    mockMissionManager = {
      completedMissions: new Set(),
      _ensureMissionsLoaded: sinon.stub().resolves(),
      areProceduralMissionsUnlocked: sinon.stub().returns(false),
      refreshProceduralMissions: sinon.stub(),
      missionRegistry: new Map(),
      completedMissionDetails: new Map(),
    };

    mockHubStash = {
      currency: {
        aetherShards: 0,
        ancientCores: 0,
      },
    };

    // Replace gameStateManager properties with mocks
    gameStateManager.persistence = mockPersistence;
    gameStateManager.rosterManager = mockRosterManager;
    gameStateManager.missionManager = mockMissionManager;
    gameStateManager.hubStash = mockHubStash;

    // NOW create the element after mocks are set up
    container = document.createElement("div");
    document.body.appendChild(container);
    element = document.createElement("hub-screen");
    container.appendChild(element);
  });

  afterEach(() => {
    if (container && container.parentNode) {
      container.parentNode.removeChild(container);
    }
    // Clean up event listeners (element handles its own cleanup in disconnectedCallback)
  });

  // 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: Live Data Binding", () => {
    it("should fetch wallet and roster data on mount", async () => {
      // Set up hub stash (primary source for wallet)
      mockHubStash.currency = {
        aetherShards: 450,
        ancientCores: 12,
      };
      mockRosterManager.roster = [
        { id: "u1", status: "READY" },
        { id: "u2", status: "READY" },
        { id: "u3", status: "INJURED" },
      ];
      mockRosterManager.getDeployableUnits.returns([
        { id: "u1", status: "READY" },
        { id: "u2", status: "READY" },
      ]);

      // Manually trigger _loadData since element was already created in beforeEach
      await element._loadData();
      await waitForUpdate();

      expect(element.wallet.aetherShards).to.equal(450);
      expect(element.wallet.ancientCores).to.equal(12);
      expect(element.rosterSummary.total).to.equal(3);
      expect(element.rosterSummary.ready).to.equal(2);
      expect(element.rosterSummary.injured).to.equal(1);
    });

    it("should display correct currency values in top bar", async () => {
      // Set up hub stash (primary source for wallet)
      mockHubStash.currency = {
        aetherShards: 450,
        ancientCores: 12,
      };

      // Manually trigger _loadData
      await element._loadData();
      await waitForUpdate();

      const resourceStrip = queryShadow(".resource-strip");
      expect(resourceStrip).to.exist;
      expect(resourceStrip.textContent).to.include("450");
      expect(resourceStrip.textContent).to.include("12");
    });

    it("should handle missing wallet data gracefully", async () => {
      // Clear hub stash to test fallback
      mockHubStash.currency = null;
      mockPersistence.loadRun.resolves(null);
      // Reload data to test fallback path
      await element._loadData();
      await waitForUpdate();

      expect(element.wallet.aetherShards).to.equal(0);
      expect(element.wallet.ancientCores).to.equal(0);
    });
  });

  describe("CoA 2: Hotspot & Dock Sync", () => {
    it("should open MISSIONS overlay when clicking missions hotspot", async () => {
      await waitForUpdate();

      const missionsHotspot = queryShadow(".hotspot.missions");
      expect(missionsHotspot).to.exist;

      missionsHotspot.click();
      await waitForUpdate();

      expect(element.activeOverlay).to.equal("MISSIONS");
    });

    it("should open MISSIONS overlay when clicking missions dock button", async () => {
      await waitForUpdate();

      const missionsButton = queryShadowAll(".dock-button")[1]; // Second button is MISSIONS
      expect(missionsButton).to.exist;

      missionsButton.click();
      await waitForUpdate();

      expect(element.activeOverlay).to.equal("MISSIONS");
    });

    it("should open BARRACKS overlay from both hotspot and button", async () => {
      await waitForUpdate();

      // Test hotspot
      const barracksHotspot = queryShadow(".hotspot.barracks");
      barracksHotspot.click();
      await waitForUpdate();
      expect(element.activeOverlay).to.equal("BARRACKS");

      // Close and test button
      element.activeOverlay = "NONE";
      await waitForUpdate();

      const barracksButton = queryShadowAll(".dock-button")[0]; // First button is BARRACKS
      barracksButton.click();
      await waitForUpdate();
      expect(element.activeOverlay).to.equal("BARRACKS");
    });
  });

  describe("CoA 3: Overlay Management", () => {
    it("should render mission-board component when activeOverlay is MISSIONS", async () => {
      element.activeOverlay = "MISSIONS";
      await waitForUpdate();

      // Import MissionBoard dynamically (correct filename)
      await import("../../src/ui/components/mission-board.js").catch(() => {});
      await waitForUpdate();
      // Give time for component to render
      await new Promise((resolve) => setTimeout(resolve, 50));
      await waitForUpdate();

      const overlayContainer = queryShadow(".overlay-container.active");
      expect(overlayContainer).to.exist;

      const missionBoard = queryShadow("mission-board");
      expect(missionBoard).to.exist;
    });

    it("should close overlay when close event is dispatched", async () => {
      element.activeOverlay = "MISSIONS";
      await waitForUpdate();

      // Import MissionBoard to ensure it's loaded
      await import("../../src/ui/components/mission-board.js").catch(() => {});
      await waitForUpdate();

      // Simulate close event from mission-board component
      const missionBoard = queryShadow("mission-board");
      if (missionBoard) {
        const closeEvent = new CustomEvent("close", {
          bubbles: true,
          composed: true,
        });
        missionBoard.dispatchEvent(closeEvent);
      } else {
        // If mission-board not rendered, directly call _closeOverlay
        element._closeOverlay();
      }
      await waitForUpdate();

      expect(element.activeOverlay).to.equal("NONE");
    });

    it("should close overlay when backdrop is clicked", async () => {
      element.activeOverlay = "BARRACKS";
      await waitForUpdate();

      const backdrop = queryShadow(".overlay-backdrop");
      expect(backdrop).to.exist;

      backdrop.click();
      await waitForUpdate();

      expect(element.activeOverlay).to.equal("NONE");
    });

    it("should show different overlays for different types", async () => {
      const overlayTypes = [
        "BARRACKS",
        "MISSIONS",
        "MARKET",
        "RESEARCH",
        "SYSTEM",
      ];

      for (const type of overlayTypes) {
        element.activeOverlay = type;
        await waitForUpdate();

        const overlayContainer = queryShadow(".overlay-container.active");
        expect(overlayContainer).to.exist;
        expect(element.activeOverlay).to.equal(type);
      }
    });
  });

  describe("CoA 4: Mission Handoff", () => {
    it("should dispatch request-team-builder event when mission is selected", async () => {
      let eventDispatched = false;
      let eventData = null;

      window.addEventListener("request-team-builder", (e) => {
        eventDispatched = true;
        eventData = e.detail;
      });

      element.activeOverlay = "MISSIONS";
      await waitForUpdate();

      // Import MissionBoard (correct filename)
      await import("../../src/ui/components/mission-board.js").catch(() => {});
      await waitForUpdate();
      // Give time for component to render
      await new Promise((resolve) => setTimeout(resolve, 50));
      await waitForUpdate();

      const missionBoard = queryShadow("mission-board");
      expect(missionBoard).to.exist;

      // Simulate mission selection
      const missionEvent = new CustomEvent("mission-selected", {
        detail: { missionId: "MISSION_TUTORIAL_01" },
        bubbles: true,
        composed: true,
      });
      missionBoard.dispatchEvent(missionEvent);
      await waitForUpdate();

      expect(eventDispatched).to.be.true;
      expect(eventData.missionId).to.equal("MISSION_TUTORIAL_01");
      expect(element.activeOverlay).to.equal("NONE");
    });
  });

  describe("Unlock System", () => {
    it("should unlock market after first mission", async () => {
      mockMissionManager.completedMissions = new Set(["MISSION_TUTORIAL_01"]);
      await waitForUpdate();

      expect(element.unlocks.market).to.be.true;
      expect(element.unlocks.research).to.be.false;
    });

    it("should unlock research after 3 missions", async () => {
      mockMissionManager.completedMissions = new Set([
        "MISSION_1",
        "MISSION_2",
        "MISSION_3",
      ]);
      // Reload data to recalculate unlocks
      await element._loadData();
      await waitForUpdate();

      expect(element.unlocks.research).to.be.true;
    });

    it("should disable locked facilities in dock", async () => {
      mockMissionManager.completedMissions = new Set(); // No missions completed
      // Reload data to recalculate unlocks
      await element._loadData();
      await waitForUpdate();

      // Market is always enabled per spec, so it should NOT be disabled
      const marketButton = queryShadowAll(".dock-button")[2]; // MARKET is third button
      expect(marketButton).to.exist;
      // Market should not be disabled (it's always available)

      const researchButton = queryShadowAll(".dock-button")[3]; // RESEARCH is fourth button
      expect(researchButton).to.exist;
      // Research should be disabled when locked (no missions completed)
      expect(
        researchButton.hasAttribute("disabled") ||
          researchButton.classList.contains("disabled")
      ).to.be.true;
    });

    it("should hide market hotspot when locked", async () => {
      mockMissionManager.completedMissions = new Set(); // No missions completed
      // Reload data to recalculate unlocks
      await element._loadData();
      await waitForUpdate();

      // Market is always enabled per spec, so market hotspot should NOT be hidden
      const marketHotspot = queryShadow(".hotspot.market");
      // Market is always available, so hotspot should be visible
      expect(marketHotspot).to.exist;
      // If there's a hidden attribute, it should be false or not present
      if (marketHotspot) {
        expect(marketHotspot.hasAttribute("hidden")).to.be.false;
      }
    });
  });

  describe("Roster Summary Display", () => {
    it("should calculate roster summary correctly", async () => {
      mockRosterManager.roster = [
        { id: "u1", status: "READY" },
        { id: "u2", status: "READY" },
        { id: "u3", status: "INJURED" },
        { id: "u4", status: "READY" },
      ];
      mockRosterManager.getDeployableUnits.returns([
        { id: "u1", status: "READY" },
        { id: "u2", status: "READY" },
        { id: "u4", status: "READY" },
      ]);

      // Reload data to recalculate roster summary
      await element._loadData();
      await waitForUpdate();

      expect(element.rosterSummary.total).to.equal(4);
      expect(element.rosterSummary.ready).to.equal(3);
      expect(element.rosterSummary.injured).to.equal(1);
    });
  });

  describe("State Change Handling", () => {
    it("should reload data when gamestate-changed event fires", async () => {
      const initialShards = 100;
      // Set up initial hub stash
      mockHubStash.currency = { aetherShards: initialShards, ancientCores: 0 };
      // Load initial data
      await element._loadData();
      await waitForUpdate();

      expect(element.wallet.aetherShards).to.equal(initialShards);

      // Change the data in hub stash
      const newShards = 200;
      mockHubStash.currency = { aetherShards: newShards, ancientCores: 0 };

      // Simulate state change
      window.dispatchEvent(
        new CustomEvent("gamestate-changed", {
          detail: { oldState: "STATE_COMBAT", newState: "STATE_MAIN_MENU" },
        })
      );
      // Wait for _handleStateChange to call _loadData
      await new Promise((resolve) => setTimeout(resolve, 100));
      await waitForUpdate();

      expect(element.wallet.aetherShards).to.equal(newShards);
    });
  });
});
