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

import type { Gateways } from "@/ports";
import { MockLayoutGateway, MockTerminalGateway } from "@/adapters/mock";
import { DIProvider } from "@/app/di";

const terminalViewSpy = vi.hoisted(() => vi.fn());
const terminalMountSpy = vi.hoisted(() => vi.fn());
const terminalUnmountSpy = vi.hoisted(() => vi.fn());

vi.mock("@/features/terminals", () => ({
  TerminalView: (props: { refitSignal?: number }) => {
    terminalViewSpy(props);
    useEffect(() => {
      terminalMountSpy();
      return () => terminalUnmountSpy();
    }, []);
    return (
      <div
        data-testid="mock-terminal-view"
        data-refit-signal={String(props.refitSignal)}
      />
    );
  },
  ResumeConversationPopup: () => null,
  useWritePortal: () => ({
    portal: {
      onHumanData: () => {},
      isSuspended: () => false,
      bindHandle: () => {},
      unbindHandle: () => {},
    },
    overlay: false,
  }),
}));

import { LayoutGrid } from "./LayoutGrid";

function renderGrid(
  layout: MockLayoutGateway,
  props: { projectId: string; cwd: string; layoutId?: string },
) {
  const gateways = {
    layout,
    terminal: new MockTerminalGateway(),
  } as unknown as Gateways;

  return render(
    <DIProvider gateways={gateways}>
      <LayoutGrid {...props} />
    </DIProvider>,
  );
}

function latestRefitSignal(): number {
  const lastCall = terminalViewSpy.mock.calls.at(-1);
  return Number(lastCall?.[0]?.refitSignal ?? -1);
}

describe("LayoutGrid refit epoch (#4)", () => {
  it("bumps TerminalView refitSignal after cwd/layout/project transitions", async () => {
    const layout = new MockLayoutGateway();
    const { activeId: firstLayoutId } = await layout.listLayouts("p1");
    const { layoutId: secondLayoutId } = await layout.createLayout("p1", "Second");
    terminalViewSpy.mockClear();
    terminalMountSpy.mockClear();
    terminalUnmountSpy.mockClear();

    const view = renderGrid(layout, {
      projectId: "p1",
      cwd: "/same/cwd",
      layoutId: firstLayoutId,
    });

    await screen.findByTestId("mock-terminal-view");
    await waitFor(() => expect(latestRefitSignal()).toBeGreaterThan(0));
    const initialSignal = latestRefitSignal();
    expect(terminalMountSpy).toHaveBeenCalledTimes(1);

    view.rerender(
      <DIProvider
        gateways={{
          layout,
          terminal: new MockTerminalGateway(),
        } as unknown as Gateways}
      >
        <LayoutGrid
          projectId="p1"
          cwd="/same/cwd/after-transition"
          layoutId={firstLayoutId}
        />
      </DIProvider>,
    );

    await waitFor(() => expect(latestRefitSignal()).toBeGreaterThan(initialSignal));
    const afterCwdSignal = latestRefitSignal();
    expect(terminalMountSpy).toHaveBeenCalledTimes(1);
    expect(terminalUnmountSpy).not.toHaveBeenCalled();

    view.rerender(
      <DIProvider
        gateways={{
          layout,
          terminal: new MockTerminalGateway(),
        } as unknown as Gateways}
      >
        <LayoutGrid
          projectId="p1"
          cwd="/same/cwd/after-transition"
          layoutId={secondLayoutId}
        />
      </DIProvider>,
    );

    await waitFor(() => expect(latestRefitSignal()).toBeGreaterThan(afterCwdSignal));
    const afterLayoutSwitchSignal = latestRefitSignal();

    view.rerender(
      <DIProvider
        gateways={{
          layout,
          terminal: new MockTerminalGateway(),
        } as unknown as Gateways}
      >
        <LayoutGrid projectId="p2" cwd="/same/cwd" />
      </DIProvider>,
    );

    await waitFor(() =>
      expect(latestRefitSignal()).toBeGreaterThan(afterLayoutSwitchSignal),
    );
    expect(terminalMountSpy).toHaveBeenCalled();
  });
});
