import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
  Lab3TerminalFrame,
} from "~/components/labs/Lab3TerminalFrame";
import {
  LAB3_DEFAULT_TERMINAL_PATH,
  LAB3_DEFAULT_WORKING_DIRECTORY,
  fetchLab3RuntimeConfig,
  getLab3TerminalPath,
  LAB3_RUNTIME_CONFIG_PATH,
  normalizeLab3RuntimeConfig,
} from "~/lib/lab3-runtime";

describe("getLab3TerminalPath", () => {
  it("falls back to the default same-origin terminal path", () => {
    expect(getLab3TerminalPath()).toBe(LAB3_DEFAULT_TERMINAL_PATH);
    expect(getLab3TerminalPath("")).toBe(LAB3_DEFAULT_TERMINAL_PATH);
  });

  it("normalizes relative and absolute terminal values to a same-origin path", () => {
    expect(getLab3TerminalPath("wetty")).toBe("/wetty");
    expect(getLab3TerminalPath("https://labs.example.com/wetty?view=lab3")).toBe(
      "https://labs.example.com/wetty?view=lab3",
    );
  });
});

describe("normalizeLab3RuntimeConfig", () => {
  it("fills in the default student terminal metadata", () => {
    expect(normalizeLab3RuntimeConfig()).toEqual({
      terminalPath: LAB3_DEFAULT_TERMINAL_PATH,
      username: "student",
      workingDirectory: LAB3_DEFAULT_WORKING_DIRECTORY,
    });
  });
});

describe("fetchLab3RuntimeConfig", () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it("reads the runtime config artifact from the public path", async () => {
    const fetchMock = vi
      .spyOn(globalThis, "fetch")
      .mockResolvedValue(
        new Response(
          JSON.stringify({
            lab3TerminalUrl: "http://127.0.0.1:7681/wetty",
            lab3Username: "student",
            lab3WorkingDirectory: "/home/student/lab3",
          }),
          { status: 200 },
        ),
      );

    await expect(fetchLab3RuntimeConfig()).resolves.toEqual({
      terminalPath: "http://127.0.0.1:7681/wetty",
      username: "student",
      workingDirectory: "/home/student/lab3",
    });

    expect(fetchMock).toHaveBeenCalledWith(LAB3_RUNTIME_CONFIG_PATH, {
      cache: "no-store",
    });
  });
});

describe("Lab3TerminalFrame", () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it("renders the embedded terminal with the configured path override", () => {
    render(<Lab3TerminalFrame srcPath="lab-terminal" />);

    const frame = screen.getByTitle("Lab 3 terminal session");
    expect(frame).toHaveAttribute("src", "/lab-terminal");
    expect(
      screen.getAllByRole("link", { name: "Open in New Tab" }).every((link) => {
        return link.getAttribute("href") === "/lab-terminal";
      }),
    ).toBe(true);
    expect(screen.getAllByText("/home/student/lab3")).toHaveLength(2);
  });

  it("loads the terminal settings from the runtime config artifact", async () => {
    vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        JSON.stringify({
          lab3TerminalUrl: "http://127.0.0.1:7681/wetty",
          lab3Username: "student",
          lab3WorkingDirectory: "/srv/labs/lab3",
        }),
        { status: 200 },
      ),
    );

    render(<Lab3TerminalFrame />);

    await waitFor(() => {
      expect(screen.getByTitle("Lab 3 terminal session")).toHaveAttribute(
        "src",
        "http://127.0.0.1:7681/wetty",
      );
    });

    expect(screen.getAllByText("/srv/labs/lab3")).toHaveLength(2);
  });

  it("toggles the dock open and closed", () => {
    render(<Lab3TerminalFrame srcPath="/wetty" />);

    const toggle = screen.getAllByRole("button", {
      name: /open terminal|hide terminal|lab 3 terminal/i,
    })[0];
    expect(toggle).toHaveAttribute("aria-expanded", "false");

    fireEvent.click(toggle);
    expect(toggle).toHaveAttribute("aria-expanded", "true");
  });

  it("renders fallback guidance alongside the embedded frame", () => {
    render(<Lab3TerminalFrame srcPath="/wetty" />);

    expect(screen.getByText(/If the terminal page does not appear/i)).toBeInTheDocument();
  });
});
