/**
 * L5 — the in-memory {@link MockProfileGateway}: first-run flag lifecycle,
 * reference catalogue, simulated detection (only `claude` installed), and the
 * save/list/delete/configure CRUD.
 */
import { describe, it, expect } from "vitest";

import type { AgentProfile } from "@/domain";
import { MockProfileGateway, MOCK_REFERENCE_PROFILES } from "./index";

function customProfile(id: string, command: string): AgentProfile {
  return {
    id,
    name: `Custom ${id}`,
    command,
    args: [],
    contextInjection: { strategy: "stdin" },
    detect: null,
    cwdTemplate: "{projectRoot}",
  };
}

describe("MockProfileGateway", () => {
  it("firstRunState is first-run with only the selectable reference profiles", async () => {
    // §17.3/D7: only structured-drivable profiles are offered (Claude/Codex CLIs
    // + the OpenCode + llama.cpp local adapter); Gemini/Aider are filtered out of
    // the selection path server-side.
    const gw = new MockProfileGateway();
    const state = await gw.firstRunState();
    expect(state.isFirstRun).toBe(true);
    expect(state.referenceProfiles.map((p) => p.command)).toEqual([
      "claude",
      "codex",
      "opencode",
    ]);
  });

  it("referenceProfiles returns a clone of the catalogue", async () => {
    const gw = new MockProfileGateway();
    const refs = await gw.referenceProfiles();
    expect(refs).toEqual(MOCK_REFERENCE_PROFILES);
    refs[0].command = "mutated";
    const again = await gw.referenceProfiles();
    expect(again[0].command).toBe("claude");
  });

  it("detectProfiles marks only claude as installed", async () => {
    const gw = new MockProfileGateway();
    const results = await gw.detectProfiles([...MOCK_REFERENCE_PROFILES]);
    const byCommand = Object.fromEntries(
      results.map((r) => [r.profile.command, r.available]),
    );
    expect(byCommand).toEqual({
      claude: true,
      codex: false,
      opencode: false,
    });
  });

  it("saveProfile upserts and listProfiles reflects it", async () => {
    const gw = new MockProfileGateway();
    await gw.saveProfile(customProfile("c1", "foo"));
    await gw.saveProfile(customProfile("c1", "bar")); // same id ⇒ replace
    const list = await gw.listProfiles();
    expect(list).toHaveLength(1);
    expect(list[0].command).toBe("bar");
  });

  it("deleteProfile removes by id", async () => {
    const gw = new MockProfileGateway();
    await gw.saveProfile(customProfile("a", "a"));
    await gw.saveProfile(customProfile("b", "b"));
    await gw.deleteProfile("a");
    const list = await gw.listProfiles();
    expect(list.map((p) => p.id)).toEqual(["b"]);
  });

  it("configureProfiles persists the batch and closes the first run", async () => {
    const gw = new MockProfileGateway();
    expect((await gw.firstRunState()).isFirstRun).toBe(true);

    const chosen = [customProfile("x", "x")];
    const out = await gw.configureProfiles(chosen);
    expect(out).toEqual(chosen);

    expect((await gw.firstRunState()).isFirstRun).toBe(false);
    expect(await gw.listProfiles()).toEqual(chosen);
  });

  it("configureProfiles with an empty list still closes the first run", async () => {
    const gw = new MockProfileGateway();
    await gw.configureProfiles([]);
    expect((await gw.firstRunState()).isFirstRun).toBe(false);
  });

  it("cloneOpenCodeProfileFromSeed mints distinct ids and copies the seed config (F36)", async () => {
    const gw = new MockProfileGateway();

    const a = await gw.cloneOpenCodeProfileFromSeed();
    const b = await gw.cloneOpenCodeProfileFromSeed();

    expect(a.structuredAdapter).toBe("openCode");
    expect(a.opencode?.baseURL).toBe("http://localhost:8080/v1");
    // Identity is the id — two clones must never collide.
    expect(a.id).not.toBe(b.id);
  });

  it("cloneOpenCodeProfileFromSeed honours name/config overrides (Duplicate path)", async () => {
    const gw = new MockProfileGateway();
    const cloned = await gw.cloneOpenCodeProfileFromSeed({
      name: "My local model",
      opencode: {
        baseURL: "http://localhost:9999/v1",
        model: "custom-model",
        localModelServerId: "srv-7",
      },
    });

    expect(cloned.name).toBe("My local model");
    expect(cloned.opencode?.baseURL).toBe("http://localhost:9999/v1");
    expect(cloned.opencode?.model).toBe("custom-model");
    expect(cloned.opencode?.localModelServerId).toBe("srv-7");
  });
});
