import { expect } from "@esm-bundle/chai";
import { MissionBoard } from "../../src/ui/components/MissionBoard.js";
import { gameStateManager } from "../../src/core/GameStateManager.js";

describe("UI: MissionBoard", () => {
  let element;
  let container;
  let mockMissionManager;

  beforeEach(() => {
    container = document.createElement("div");
    document.body.appendChild(container);
    element = document.createElement("mission-board");
    container.appendChild(element);

    // Mock MissionManager
    mockMissionManager = {
      missionRegistry: new Map(),
      completedMissions: new Set(),
    };

    gameStateManager.missionManager = mockMissionManager;
  });

  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("Mission Display", () => {
    it("should display registered missions", async () => {
      const mission1 = {
        id: "MISSION_TUTORIAL_01",
        type: "TUTORIAL",
        config: {
          title: "Protocol: First Descent",
          description: "Establish a foothold in the Rusting Wastes.",
          difficulty_tier: 1,
        },
        rewards: {
          guaranteed: {
            xp: 100,
            currency: {
              aether_shards: 50,
            },
          },
        },
      };

      const mission2 = {
        id: "MISSION_01",
        type: "STORY",
        config: {
          title: "The First Strike",
          description: "Strike back at the enemy.",
          recommended_level: 2,
        },
        rewards: {
          guaranteed: {
            xp: 200,
            currency: {
              aether_shards: 100,
              ancient_cores: 1,
            },
          },
        },
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);

      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);

      const titles = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-title")?.textContent.trim()
      );
      expect(titles).to.include("Protocol: First Descent");
      expect(titles).to.include("The First Strike");
    });

    it("should show empty state when no missions available", async () => {
      mockMissionManager.missionRegistry.clear();
      await waitForUpdate();

      const emptyState = queryShadow(".empty-state");
      expect(emptyState).to.exist;
      expect(emptyState.textContent).to.include("No missions available");
    });
  });

  describe("Mission Card Details", () => {
    it("should display mission type badge", async () => {
      const mission = {
        id: "MISSION_01",
        type: "STORY",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const typeBadge = missionCard.querySelector(".mission-type.STORY");
      expect(typeBadge).to.exist;
      expect(typeBadge.textContent).to.include("STORY");
    });

    it("should display mission description", async () => {
      const mission = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: {
          title: "Test Mission",
          description: "This is a test mission description.",
        },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const description = missionCard.querySelector(".mission-description");
      expect(description).to.exist;
      expect(description.textContent).to.include("This is a test mission description.");
    });

    it("should display difficulty information", async () => {
      const mission = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: {
          title: "Test Mission",
          difficulty_tier: 3,
        },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const difficulty = missionCard.querySelector(".difficulty");
      expect(difficulty).to.exist;
      expect(difficulty.textContent).to.include("Tier 3");
    });
  });

  describe("Rewards Display", () => {
    it("should display currency rewards", async () => {
      const mission = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {
          guaranteed: {
            currency: {
              aether_shards: 150,
              ancient_cores: 2,
            },
          },
        },
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const rewards = missionCard.querySelector(".mission-rewards");
      expect(rewards).to.exist;
      expect(rewards.textContent).to.include("150");
      expect(rewards.textContent).to.include("2");
    });

    it("should display XP rewards", async () => {
      const mission = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {
          guaranteed: {
            xp: 250,
          },
        },
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const rewards = missionCard.querySelector(".mission-rewards");
      expect(rewards).to.exist;
      expect(rewards.textContent).to.include("250");
      expect(rewards.textContent).to.include("XP");
    });

    it("should handle both snake_case and camelCase currency", async () => {
      // Test snake_case (from JSON)
      const mission1 = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: { title: "Test 1", description: "Test" },
        rewards: {
          guaranteed: {
            currency: {
              aether_shards: 100,
            },
          },
        },
      };

      // Test camelCase (from code)
      const mission2 = {
        id: "MISSION_02",
        type: "TUTORIAL",
        config: { title: "Test 2", description: "Test" },
        rewards: {
          guaranteed: {
            currency: {
              aetherShards: 200,
            },
          },
        },
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);

      // Both should display rewards correctly
      const rewards1 = missionCards[0].querySelector(".mission-rewards");
      const rewards2 = missionCards[1].querySelector(".mission-rewards");
      expect(rewards1.textContent).to.include("100");
      expect(rewards2.textContent).to.include("200");
    });
  });

  describe("Completed Missions", () => {
    it("should mark completed missions", async () => {
      const mission = {
        id: "MISSION_TUTORIAL_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      mockMissionManager.completedMissions.add(mission.id);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      expect(missionCard.classList.contains("completed")).to.be.true;
      expect(missionCard.textContent).to.include("Completed");
    });

    it("should not show select button for completed missions", async () => {
      const mission = {
        id: "MISSION_TUTORIAL_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      mockMissionManager.completedMissions.add(mission.id);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      const selectButton = missionCard.querySelector(".select-button");
      expect(selectButton).to.be.null;
    });
  });

  describe("Mission Selection", () => {
    it("should dispatch mission-selected event when mission is clicked", async () => {
      const mission = {
        id: "MISSION_TUTORIAL_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      let eventDispatched = false;
      let eventData = null;

      element.addEventListener("mission-selected", (e) => {
        eventDispatched = true;
        eventData = e.detail;
      });

      const missionCard = queryShadow(".mission-card");
      missionCard.click();
      await waitForUpdate();

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

    it("should dispatch mission-selected event when select button is clicked", async () => {
      const mission = {
        id: "MISSION_TUTORIAL_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      let eventDispatched = false;
      let eventData = null;

      element.addEventListener("mission-selected", (e) => {
        eventDispatched = true;
        eventData = e.detail;
      });

      const selectButton = queryShadow(".select-button");
      expect(selectButton).to.exist;

      selectButton.click();
      await waitForUpdate();

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

  describe("Close Button", () => {
    it("should dispatch close event when close button is clicked", async () => {
      const mission = {
        id: "MISSION_01",
        type: "TUTORIAL",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      let closeEventDispatched = false;

      element.addEventListener("close", () => {
        closeEventDispatched = true;
      });

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

      closeButton.click();
      await waitForUpdate();

      expect(closeEventDispatched).to.be.true;
    });
  });

  describe("Mission Type Styling", () => {
    it("should apply correct styling for different mission types", async () => {
      const missions = [
        { id: "M1", type: "STORY", config: { title: "Story", description: "Test" }, rewards: {} },
        { id: "M2", type: "SIDE_QUEST", config: { title: "Side", description: "Test" }, rewards: {} },
        { id: "M3", type: "TUTORIAL", config: { title: "Tutorial", description: "Test" }, rewards: {} },
        { id: "M4", type: "PROCEDURAL", config: { title: "Proc", description: "Test" }, rewards: {} },
      ];

      missions.forEach((m) => mockMissionManager.missionRegistry.set(m.id, m));
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(4);

      const typeBadges = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-type")
      );

      expect(typeBadges[0].classList.contains("STORY")).to.be.true;
      expect(typeBadges[1].classList.contains("SIDE_QUEST")).to.be.true;
      expect(typeBadges[2].classList.contains("TUTORIAL")).to.be.true;
      expect(typeBadges[3].classList.contains("PROCEDURAL")).to.be.true;
    });
  });

  describe("Mission Prerequisites", () => {
    it("should show mission as available when no prerequisites", async () => {
      const mission = {
        id: "MISSION_01",
        type: "STORY",
        config: { title: "Test Mission", description: "Test" },
        rewards: {},
      };
      mockMissionManager.missionRegistry.set(mission.id, mission);
      await waitForUpdate();

      const missionCard = queryShadow(".mission-card");
      expect(missionCard).to.exist;
      expect(missionCard.classList.contains("locked")).to.be.false;
    });

    it("should show mission as locked when prerequisites not met", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "SIDE_QUEST",
        config: { title: "First Mission", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "SIDE_QUEST",
        config: {
          title: "Second Mission",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);

      const mission2Card = Array.from(missionCards).find((card) =>
        card.querySelector(".mission-title")?.textContent.includes("Second Mission")
      );
      expect(mission2Card).to.exist;
      expect(mission2Card.classList.contains("locked")).to.be.true;
    });

    it("should show mission as available when prerequisites are met", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "SIDE_QUEST",
        config: { title: "First Mission", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "SIDE_QUEST",
        config: {
          title: "Second Mission",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      mockMissionManager.completedMissions.add("MISSION_01");
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      const mission2Card = Array.from(missionCards).find((card) =>
        card.querySelector(".mission-title")?.textContent.includes("Second Mission")
      );
      expect(mission2Card).to.exist;
      expect(mission2Card.classList.contains("locked")).to.be.false;
    });

    it("should display prerequisite requirements for locked missions", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "SIDE_QUEST",
        config: { title: "First Mission", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "SIDE_QUEST",
        config: {
          title: "Second Mission",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      const mission2Card = Array.from(missionCards).find((card) =>
        card.querySelector(".mission-title")?.textContent.includes("Second Mission")
      );
      expect(mission2Card).to.exist;
      expect(mission2Card.textContent).to.include("Requires");
      expect(mission2Card.textContent).to.include("First Mission");
    });
  });

  describe("Mission Visibility", () => {
    it("should hide STORY missions when prerequisites not met", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "STORY",
        config: { title: "First Story", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "STORY",
        config: {
          title: "Second Story",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(1);
      const titles = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-title")?.textContent.trim()
      );
      expect(titles).to.include("First Story");
      expect(titles).to.not.include("Second Story");
    });

    it("should show SIDE_QUEST missions as locked when prerequisites not met", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "SIDE_QUEST",
        config: { title: "First Quest", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "SIDE_QUEST",
        config: {
          title: "Second Quest",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);
      const titles = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-title")?.textContent.trim()
      );
      expect(titles).to.include("First Quest");
      expect(titles).to.include("Second Quest");

      const mission2Card = Array.from(missionCards).find((card) =>
        card.querySelector(".mission-title")?.textContent.includes("Second Quest")
      );
      expect(mission2Card.classList.contains("locked")).to.be.true;
    });

    it("should show STORY mission when prerequisites are met", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "STORY",
        config: { title: "First Story", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "STORY",
        config: {
          title: "Second Story",
          description: "Test",
          prerequisites: ["MISSION_01"],
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      mockMissionManager.completedMissions.add("MISSION_01");
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);
      const titles = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-title")?.textContent.trim()
      );
      expect(titles).to.include("First Story");
      expect(titles).to.include("Second Story");
    });

    it("should respect explicit visibility_when_locked setting", async () => {
      const mission1 = {
        id: "MISSION_01",
        type: "STORY",
        config: { title: "First Story", description: "Test" },
        rewards: {},
      };
      const mission2 = {
        id: "MISSION_02",
        type: "STORY",
        config: {
          title: "Second Story",
          description: "Test",
          prerequisites: ["MISSION_01"],
          visibility_when_locked: "locked", // Override default hidden behavior
        },
        rewards: {},
      };

      mockMissionManager.missionRegistry.set(mission1.id, mission1);
      mockMissionManager.missionRegistry.set(mission2.id, mission2);
      await waitForUpdate();

      const missionCards = queryShadowAll(".mission-card");
      expect(missionCards.length).to.equal(2);
      const titles = Array.from(missionCards).map((card) =>
        card.querySelector(".mission-title")?.textContent.trim()
      );
      expect(titles).to.include("Second Story");
    });
  });
});

