import { describe, expect, it } from "vitest";
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";

import { DIProvider } from "@/app/di";
import { MockProfileGateway } from "@/adapters/mock";
import type { Gateways } from "@/ports";
import type { ProfileModelCatalogEntry } from "@/domain";
import { ProfilesSettings } from "./ProfilesSettings";

function renderSettings(profile: MockProfileGateway = new MockProfileGateway()) {
  return {
    profile,
    ...render(
      <DIProvider gateways={{ profile } as unknown as Gateways}>
        <ProfilesSettings />
      </DIProvider>,
    ),
  };
}

async function waitReady() {
  await waitFor(() =>
    expect(
      (screen.getByRole("button", { name: "Creer un profil" }) as HTMLButtonElement)
        .disabled,
    ).toBe(false),
  );
}

async function createProfile() {
  const before = screen.queryAllByRole("listitem").length;
  fireEvent.click(screen.getByRole("button", { name: "Creer un profil" }));
  await waitFor(() => expect(screen.getAllByRole("listitem")).toHaveLength(before + 1));
}

describe("ProfilesSettings", () => {
  it("creates multiple named Codex and Claude profiles with different models", async () => {
    const { profile } = renderSettings();
    await waitReady();

    await createProfile();
    await createProfile();
    let rows = screen.getAllByRole("listitem");
    fireEvent.change(within(rows[1]).getByLabelText(/nom du profil/), {
      target: { value: "Codex mini" },
    });
    fireEvent.change(within(rows[1]).getByLabelText(/modele du profil/), {
      target: { value: "gpt-5-mini" },
    });
    fireEvent.click(within(rows[1]).getByRole("button", { name: "Enregistrer" }));

    fireEvent.click(screen.getByRole("tab", { name: "Claude" }));
    await waitReady();
    await createProfile();
    await createProfile();
    rows = screen.getAllByRole("listitem");
    fireEvent.change(within(rows[1]).getByLabelText(/nom du profil/), {
      target: { value: "Claude Opus" },
    });
    fireEvent.change(within(rows[1]).getByLabelText(/modele du profil/), {
      target: { value: "claude-opus-4-8" },
    });
    fireEvent.click(within(rows[1]).getByRole("button", { name: "Enregistrer" }));

    await waitFor(async () => {
      const saved = await profile.listProfiles();
      expect(saved.filter((p) => p.structuredAdapter === "codex")).toHaveLength(2);
      expect(saved.filter((p) => p.structuredAdapter === "claude")).toHaveLength(2);
      expect(saved.map((p) => p.model)).toEqual(
        expect.arrayContaining([
          "gpt-5-codex",
          "gpt-5-mini",
          "claude-sonnet-5",
          "claude-opus-4-8",
        ]),
      );
    });
  });

  it("duplicates from an existing profile with '<name> copy' and preserves the model", async () => {
    const { profile } = renderSettings();
    await waitReady();
    await createProfile();

    const row = screen.getAllByRole("listitem")[0];
    fireEvent.click(within(row).getByRole("button", { name: "Dupliquer" }));

    await waitFor(async () => {
      const saved = await profile.listProfiles();
      expect(saved.some((p) => p.name === "OpenAI Codex CLI copy copy")).toBe(true);
      expect(saved.filter((p) => p.model === "gpt-5-codex")).toHaveLength(2);
    });
  });

  it("keeps manual model entry available when the catalogue fails", async () => {
    class CatalogueDownProfileGateway extends MockProfileGateway {
      listCodexModels(): Promise<ProfileModelCatalogEntry[]> {
        return Promise.reject(new Error("catalogue down"));
      }
    }

    renderSettings(new CatalogueDownProfileGateway());
    await waitReady();
    expect(await screen.findByText(/saisie manuelle active/)).toBeTruthy();

    await createProfile();
    const model = within(screen.getAllByRole("listitem")[0]).getByLabelText(
      /modele du profil/,
    ) as HTMLInputElement;
    fireEvent.change(model, { target: { value: "future-codex-model" } });
    expect(model.value).toBe("future-codex-model");
  });
});
