/**
 * F1 — {@link MediatedInput} wired to {@link MockInputGateway} through the real
 * {@link DIProvider}. Asserts the mediated-input contract (cadrage §4.2/§6):
 *
 *  - "Envoyer" routes to `InputGateway.submit` with the right args;
 *  - "Interrompre" routes to `InputGateway.interrupt`;
 *  - while busy, "Envoyer" is dimmed/aria-disabled but the enqueue STILL goes
 *    through (forward/fallback — never block the user), and "Interrompre" stays
 *    active.
 *
 * Fully offline: gateways are mocks, no backend.
 */
import { describe, it, expect } from "vitest";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";

import type { Gateways } from "@/ports";
import { MockInputGateway, MockSystemGateway } from "@/adapters/mock";
import { DIProvider } from "@/app/di";
import { MediatedInput } from "./MediatedInput";

function renderInput(
  input: MockInputGateway,
  props: Partial<React.ComponentProps<typeof MediatedInput>> = {},
) {
  const gateways = {
    input,
    system: new MockSystemGateway(),
  } as unknown as Gateways;
  return render(
    <DIProvider gateways={gateways}>
      <MediatedInput projectId="p1" agentId="a1" {...props} />
    </DIProvider>,
  );
}

describe("MediatedInput (with MockInputGateway)", () => {
  it("mounts and renders the input strip", () => {
    renderInput(new MockInputGateway());
    expect(screen.getByTestId("mediated-input")).toBeTruthy();
  });

  it("Envoyer routes to gateway.submit with project/agent/text", async () => {
    const input = new MockInputGateway();
    renderInput(input);

    fireEvent.change(screen.getByLabelText("Message à l'agent"), {
      target: { value: "hello agent" },
    });
    fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));

    await waitFor(() => {
      expect(input.submits).toEqual([
        { projectId: "p1", agentId: "a1", text: "hello agent" },
      ]);
    });
    // No interrupt was triggered.
    expect(input.interrupts).toEqual([]);
  });

  it("does not submit blank/whitespace-only text", async () => {
    const input = new MockInputGateway();
    renderInput(input);

    fireEvent.change(screen.getByLabelText("Message à l'agent"), {
      target: { value: "   " },
    });
    fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));

    expect(input.submits).toEqual([]);
  });

  it("Interrompre routes to gateway.interrupt (not an enqueue)", async () => {
    const input = new MockInputGateway();
    renderInput(input);

    fireEvent.click(screen.getByRole("button", { name: "Interrompre" }));

    await waitFor(() => {
      expect(input.interrupts).toEqual([{ projectId: "p1", agentId: "a1" }]);
    });
    expect(input.submits).toEqual([]);
  });

  it("while busy: Envoyer is aria-disabled but the enqueue still goes through, Interrompre stays active", async () => {
    const input = new MockInputGateway();
    renderInput(input, { busy: true });

    const send = screen.getByRole("button", { name: "Envoyer" });
    const interrupt = screen.getByRole("button", { name: "Interrompre" });

    // Visually disabled (forward/fallback: dimmed, not hard-disabled).
    expect(send.getAttribute("aria-disabled")).toBe("true");
    // Interrompre is never disabled.
    expect(interrupt.hasAttribute("disabled")).toBe(false);

    // The enqueue path is NOT blocked while busy.
    fireEvent.change(screen.getByLabelText("Message à l'agent"), {
      target: { value: "queued while busy" },
    });
    fireEvent.click(send);

    await waitFor(() => {
      expect(input.submits).toEqual([
        { projectId: "p1", agentId: "a1", text: "queued while busy" },
      ]);
    });
  });
});
