import { expect } from "@esm-bundle/chai";
import sinon from "sinon";
import { narrativeManager, NarrativeManager } from "../../src/managers/NarrativeManager.js";

describe("Manager: NarrativeManager", () => {
  let manager;

  beforeEach(() => {
    // Create a fresh instance for testing
    manager = new NarrativeManager();
  });

  it("CoA 1: Should initialize with empty state", () => {
    expect(manager.currentSequence).to.be.null;
    expect(manager.currentNode).to.be.null;
    expect(manager.history).to.be.instanceof(Set);
    expect(manager.history.size).to.equal(0);
  });

  it("CoA 2: startSequence should load sequence and set first node", () => {
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        { id: "1", type: "DIALOGUE", text: "Hello", next: "2" },
        { id: "2", type: "DIALOGUE", text: "World", next: "END" },
      ],
    };

    const updateSpy = sinon.spy(manager, "broadcastUpdate");

    manager.startSequence(sequenceData);

    expect(manager.currentSequence).to.equal(sequenceData);
    expect(manager.currentNode.id).to.equal("1");
    expect(manager.history.has("TEST_SEQUENCE")).to.be.true;
    expect(updateSpy.called).to.be.true;
  });

  it("CoA 3: startSequence should handle invalid data gracefully", () => {
    const consoleError = sinon.stub(console, "error");

    manager.startSequence(null);
    expect(manager.currentSequence).to.be.null;

    manager.startSequence({});
    expect(manager.currentSequence).to.be.null;

    consoleError.restore();
  });

  it("CoA 4: next should advance to next node in sequence", () => {
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        { id: "1", type: "DIALOGUE", text: "First", next: "2" },
        { id: "2", type: "DIALOGUE", text: "Second", next: "END" },
      ],
    };

    manager.startSequence(sequenceData);
    expect(manager.currentNode.id).to.equal("1");

    manager.next();

    expect(manager.currentNode.id).to.equal("2");
  });

  it("CoA 5: next should end sequence when reaching END", () => {
    const endSpy = sinon.spy(manager, "endSequence");
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [{ id: "1", type: "DIALOGUE", text: "Last", next: "END" }],
    };

    manager.startSequence(sequenceData);
    manager.next();

    expect(endSpy.called).to.be.true;
    expect(manager.currentSequence).to.be.null;
  });

  it("CoA 6: next should not advance on CHOICE nodes", () => {
    const consoleWarn = sinon.stub(console, "warn");
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        {
          id: "1",
          type: "CHOICE",
          text: "Choose",
          choices: [{ text: "Option 1", next: "2" }],
        },
      ],
    };

    manager.startSequence(sequenceData);
    manager.next();

    expect(consoleWarn.called).to.be.true;
    expect(manager.currentNode.id).to.equal("1");

    consoleWarn.restore();
  });

  it("CoA 7: makeChoice should advance based on choice selection", () => {
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        {
          id: "1",
          type: "CHOICE",
          choices: [
            { text: "Option A", next: "2" },
            { text: "Option B", next: "3" },
          ],
        },
        { id: "2", type: "DIALOGUE", text: "Result A", next: "END" },
        { id: "3", type: "DIALOGUE", text: "Result B", next: "END" },
      ],
    };

    manager.startSequence(sequenceData);
    manager.makeChoice(1); // Select Option B

    expect(manager.currentNode.id).to.equal("3");
  });

  it("CoA 8: makeChoice should dispatch trigger events", () => {
    const triggerSpy = sinon.spy();
    manager.addEventListener("narrative-trigger", triggerSpy);

    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        {
          id: "1",
          type: "CHOICE",
          choices: [
            {
              text: "Option",
              next: "2",
              trigger: { action: "GAIN_REPUTATION", value: 10 },
            },
          ],
        },
        { id: "2", type: "DIALOGUE", next: "END" },
      ],
    };

    manager.startSequence(sequenceData);
    manager.makeChoice(0);

    expect(triggerSpy.called).to.be.true;
    expect(triggerSpy.firstCall.args[0].detail.action).to.deep.equal({
      action: "GAIN_REPUTATION",
      value: 10,
    });
  });

  it("CoA 9: _advanceToNode should process node triggers", () => {
    const triggerSpy = sinon.spy();
    manager.addEventListener("narrative-trigger", triggerSpy);

    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        { id: "1", type: "DIALOGUE", next: "2" },
        {
          id: "2",
          type: "DIALOGUE",
          next: "END",
          trigger: { action: "UNLOCK_MISSION" },
        },
      ],
    };

    manager.startSequence(sequenceData);
    manager.next();

    expect(triggerSpy.called).to.be.true;
  });

  it("CoA 10: _advanceToNode should auto-advance ACTION nodes", () => {
    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [
        { id: "1", type: "DIALOGUE", next: "2" },
        { id: "2", type: "ACTION", next: "3", trigger: { action: "DO_SOMETHING" } },
        { id: "3", type: "DIALOGUE", text: "After action", next: "END" },
      ],
    };

    manager.startSequence(sequenceData);
    manager.next(); // Should skip ACTION node and go to 3

    expect(manager.currentNode.id).to.equal("3");
  });

  it("CoA 11: endSequence should dispatch narrative-end event", () => {
    const endSpy = sinon.spy();
    manager.addEventListener("narrative-end", endSpy);

    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [{ id: "1", type: "DIALOGUE", next: "END" }],
    };

    manager.startSequence(sequenceData);
    manager.endSequence();

    expect(endSpy.called).to.be.true;
    expect(manager.currentSequence).to.be.null;
    expect(manager.currentNode).to.be.null;
  });

  it("CoA 12: broadcastUpdate should dispatch narrative-update event", () => {
    const updateSpy = sinon.spy();
    manager.addEventListener("narrative-update", updateSpy);

    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [{ id: "1", type: "DIALOGUE", text: "Test", next: "END" }],
    };

    manager.startSequence(sequenceData);

    expect(updateSpy.called).to.be.true;
    const eventDetail = updateSpy.firstCall.args[0].detail;
    expect(eventDetail.node).to.equal(manager.currentNode);
    expect(eventDetail.active).to.be.true;
  });

  it("CoA 13: _advanceToNode should handle missing nodes gracefully", () => {
    const consoleError = sinon.stub(console, "error");
    const endSpy = sinon.spy(manager, "endSequence");

    const sequenceData = {
      id: "TEST_SEQUENCE",
      nodes: [{ id: "1", type: "DIALOGUE", next: "MISSING_NODE" }],
    };

    manager.startSequence(sequenceData);
    manager.next();

    expect(consoleError.called).to.be.true;
    expect(endSpy.called).to.be.true;

    consoleError.restore();
  });
});

