// IMP-90 (#90) u11 — vitest coverage for the discriminated EditMode enum
// and its pure transition helper `nextEditMode`. Replaces the prior single
// `isEditMode` boolean state. u11 introduces ONLY the state surface + the
// toolbar UI; gesture gating per mode is u12 (mutually exclusive) and must
// not regress this contract.
//
// Scope (Stage 2 unit u11 contract):
//   1) EDIT_MODES is the canonical ['text','structure','image-zone'] list
//      in toolbar render order. 'off' is intentionally excluded from the
//      iterable because it is the implicit baseline (no button); the
//      toolbar only renders the three active modes per the u11 design.
//   2) nextEditMode is a pure (current, requested) -> EditMode mapping
//      with three rules:
//        - requested === 'off'              -> 'off'   (explicit exit)
//        - requested === current            -> 'off'   (toggle exit)
//        - requested !== current && != 'off'-> requested (mode switch)
//   3) The helper is referentially transparent — no side effects, no
//      React, no useState, no DOM. SlideCanvas wires it as the useState
//      updater callback (`setEditMode((prev) => nextEditMode(prev, m))`),
//      so covering the helper here covers every toolbar click outcome
//      directly without DOM rendering. (@testing-library/react is NOT in
//      devDependencies; this mirrors the imp47b_human_review_toast pattern.)
//   4) The exported EditMode type union must contain exactly the four
//      members 'off' | 'text' | 'structure' | 'image-zone'. The runtime
//      EDIT_MODES list intentionally excludes 'off' (see (1) above).
//
// Forward-compat note: u12 will discriminate per-mode gating but MUST NOT
// alter the (current, requested) -> next contract verified here. Any
// change to the toggle/switch/exit semantics is a scope-violation against
// the u11 binding contract.

import { describe, it, expect } from "vitest";
import {
  EDIT_MODES,
  nextEditMode,
  type EditMode,
} from "../src/components/SlideCanvas";

describe("EDIT_MODES (IMP-90 u11 — toolbar render order)", () => {
  it("contains exactly the three active modes in toolbar order", () => {
    expect(EDIT_MODES).toEqual(["text", "structure", "image-zone"]);
  });

  it("excludes 'off' — baseline is implicit, no toolbar button", () => {
    expect(EDIT_MODES).not.toContain("off" as EditMode);
  });

  it("has length 3", () => {
    expect(EDIT_MODES.length).toBe(3);
  });
});

describe("nextEditMode (IMP-90 u11 — pure transition helper)", () => {
  describe("explicit 'off' request always exits", () => {
    it.each<EditMode>(["off", "text", "structure", "image-zone"])(
      "current=%s, requested=off -> off",
      (current) => {
        expect(nextEditMode(current, "off")).toBe("off");
      }
    );
  });

  describe("clicking the active mode toggles back to 'off'", () => {
    it.each<EditMode>(["text", "structure", "image-zone"])(
      "current=%s, requested=%s -> off",
      (mode) => {
        expect(nextEditMode(mode, mode)).toBe("off");
      }
    );
  });

  describe("clicking a different mode switches", () => {
    const cases: Array<[EditMode, EditMode]> = [
      ["off", "text"],
      ["off", "structure"],
      ["off", "image-zone"],
      ["text", "structure"],
      ["text", "image-zone"],
      ["structure", "text"],
      ["structure", "image-zone"],
      ["image-zone", "text"],
      ["image-zone", "structure"],
    ];
    it.each(cases)("current=%s, requested=%s -> requested", (current, requested) => {
      expect(nextEditMode(current, requested)).toBe(requested);
    });
  });

  it("is referentially transparent — multiple calls with same inputs return same output", () => {
    const a = nextEditMode("text", "structure");
    const b = nextEditMode("text", "structure");
    const c = nextEditMode("text", "structure");
    expect(a).toBe("structure");
    expect(b).toBe("structure");
    expect(c).toBe("structure");
  });

  it("never returns a value outside the EditMode union", () => {
    const all: EditMode[] = ["off", "text", "structure", "image-zone"];
    for (const current of all) {
      for (const requested of all) {
        const result = nextEditMode(current, requested);
        expect(all).toContain(result);
      }
    }
  });

  it("preserves toggle semantics under repeated identical clicks", () => {
    // off -> text -> off -> text -> off (toggle behavior)
    let m: EditMode = "off";
    m = nextEditMode(m, "text");
    expect(m).toBe("text");
    m = nextEditMode(m, "text");
    expect(m).toBe("off");
    m = nextEditMode(m, "text");
    expect(m).toBe("text");
    m = nextEditMode(m, "text");
    expect(m).toBe("off");
  });

  it("preserves switch semantics across distinct mode clicks", () => {
    // off -> text -> structure -> image-zone -> off (via toggle)
    let m: EditMode = "off";
    m = nextEditMode(m, "text");
    expect(m).toBe("text");
    m = nextEditMode(m, "structure");
    expect(m).toBe("structure");
    m = nextEditMode(m, "image-zone");
    expect(m).toBe("image-zone");
    m = nextEditMode(m, "image-zone");
    expect(m).toBe("off");
  });
});
