import { describe, it, expect, beforeEach, vi } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { RoomEditor } from "./RoomEditor";
import { useConfigStore } from "@/store/configStore";
import { Room, Agent, Config } from "@/types/config";

// Mock the store
vi.mock("@/store/configStore");

describe("RoomEditor", () => {
  const mockRoom: Room = {
    id: "lobby",
    display_name: "Lobby",
    description: "Main discussion room",
    agents: ["agent1"],
    model: "default",
  };

  const mockAgents: Agent[] = [
    {
      id: "agent1",
      display_name: "Agent 1",
      role: "Assistant",
      tools: ["file"],
      skills: [],
      instructions: [],
      rooms: ["lobby"],
    },
    {
      id: "agent2",
      display_name: "Agent 2",
      role: "Helper",
      tools: ["shell"],
      skills: [],
      instructions: [],
      rooms: [],
    },
  ];

  const mockConfig: Partial<Config> = {
    models: {
      default: { provider: "ollama", id: "llama2" },
      gpt4: { provider: "openai", id: "gpt-4" },
    },
  };

  const mockUpdateRoom = vi.fn();
  const mockDeleteRoom = vi.fn();
  const mockSaveConfig = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
    (useConfigStore as any).mockReturnValue({
      rooms: [mockRoom],
      agents: mockAgents,
      config: mockConfig,
      selectedRoomId: "lobby",
      updateRoom: mockUpdateRoom,
      deleteRoom: mockDeleteRoom,
      saveConfig: mockSaveConfig,
      isDirty: false,
    });
  });

  it("renders room editor with room details", () => {
    render(<RoomEditor />);

    const displayNameInput = screen.getByDisplayValue("Lobby");
    expect(displayNameInput).toBeInTheDocument();

    const descriptionInput = screen.getByDisplayValue("Main discussion room");
    expect(descriptionInput).toBeInTheDocument();

    expect(screen.getByText("Room Details")).toBeInTheDocument();
  });

  it("shows placeholder when no room is selected", () => {
    (useConfigStore as any).mockReturnValue({
      rooms: [],
      agents: mockAgents,
      config: mockConfig,
      selectedRoomId: null,
      updateRoom: mockUpdateRoom,
      deleteRoom: mockDeleteRoom,
      saveConfig: mockSaveConfig,
      isDirty: false,
    });

    render(<RoomEditor />);

    expect(screen.getByText("Select a room to edit")).toBeInTheDocument();
  });

  it("updates room display name", async () => {
    render(<RoomEditor />);

    const nameInput = screen.getByLabelText("Display Name");
    fireEvent.change(nameInput, { target: { value: "Updated Room Name" } });

    await waitFor(() => {
      expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
        display_name: "Updated Room Name",
      });
    });
  });

  it("updates room description", async () => {
    render(<RoomEditor />);

    const descriptionInput = screen.getByLabelText("Description");
    fireEvent.change(descriptionInput, {
      target: { value: "Updated description" },
    });

    await waitFor(() => {
      expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
        description: "Updated description",
      });
    });
  });

  it("displays agents with checkboxes", () => {
    render(<RoomEditor />);

    expect(screen.getByText("Agent 1")).toBeInTheDocument();
    expect(screen.getByText("Agent 2")).toBeInTheDocument();
    expect(screen.getByText("Assistant")).toBeInTheDocument();
    expect(screen.getByText("Helper")).toBeInTheDocument();

    // Agent 1 should be checked (in the room)
    const agent1Checkbox = screen.getByRole("checkbox", { name: /Agent 1/ });
    expect(agent1Checkbox).toBeChecked();

    // Agent 2 should not be checked
    const agent2Checkbox = screen.getByRole("checkbox", { name: /Agent 2/ });
    expect(agent2Checkbox).not.toBeChecked();
  });

  it("adds agent to room when checkbox is checked", async () => {
    render(<RoomEditor />);

    const agent2Checkbox = screen.getByRole("checkbox", { name: /Agent 2/ });
    fireEvent.click(agent2Checkbox);

    await waitFor(() => {
      expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
        agents: ["agent1", "agent2"],
      });
    });
  });

  it("removes agent from room when checkbox is unchecked", async () => {
    render(<RoomEditor />);

    const agent1Checkbox = screen.getByRole("checkbox", { name: /Agent 1/ });
    fireEvent.click(agent1Checkbox);

    await waitFor(() => {
      expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
        agents: [],
      });
    });
  });

  it("changes room model", async () => {
    render(<RoomEditor />);

    const modelSelect = screen.getByLabelText("Room Model (Optional)");
    fireEvent.click(modelSelect);

    const gpt4Option = await screen.findByText("gpt4");
    fireEvent.click(gpt4Option);

    expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
      model: "gpt4",
    });
  });

  it("sets model to undefined when default is selected", async () => {
    render(<RoomEditor />);

    const modelSelect = screen.getByLabelText("Room Model (Optional)");
    fireEvent.click(modelSelect);

    const defaultOption = await screen.findByText("Use default model");
    fireEvent.click(defaultOption);

    expect(mockUpdateRoom).toHaveBeenCalledWith("lobby", {
      model: undefined,
    });
  });

  it("calls deleteRoom when delete button is clicked", async () => {
    window.confirm = vi.fn(() => true);
    render(<RoomEditor />);

    const deleteButton = screen.getByRole("button", { name: /Delete/i });
    fireEvent.click(deleteButton);

    expect(window.confirm).toHaveBeenCalledWith(
      "Are you sure you want to delete this room?",
    );
    expect(mockDeleteRoom).toHaveBeenCalledWith("lobby");
  });

  it("does not delete room when confirm is cancelled", () => {
    window.confirm = vi.fn(() => false);
    render(<RoomEditor />);

    const deleteButton = screen.getByRole("button", { name: /Delete/i });
    fireEvent.click(deleteButton);

    expect(mockDeleteRoom).not.toHaveBeenCalled();
  });

  it("calls saveConfig when save button is clicked", async () => {
    // Re-mock with isDirty: true so the button is enabled
    (useConfigStore as any).mockReturnValue({
      rooms: [mockRoom],
      agents: mockAgents,
      config: mockConfig,
      selectedRoomId: "lobby",
      updateRoom: mockUpdateRoom,
      deleteRoom: mockDeleteRoom,
      saveConfig: mockSaveConfig,
      isDirty: true,
    });

    render(<RoomEditor />);

    const saveButton = screen.getByRole("button", { name: /Save/i });
    expect(saveButton).not.toBeDisabled();
    fireEvent.click(saveButton);

    await waitFor(() => {
      expect(mockSaveConfig).toHaveBeenCalled();
    });
  });

  it("disables save button when not dirty", () => {
    render(<RoomEditor />);

    const saveButton = screen.getByRole("button", { name: /Save/i });
    expect(saveButton).toBeDisabled();
  });

  it("enables save button when dirty", () => {
    (useConfigStore as any).mockReturnValue({
      rooms: [mockRoom],
      agents: mockAgents,
      config: mockConfig,
      selectedRoomId: "lobby",
      updateRoom: mockUpdateRoom,
      deleteRoom: mockDeleteRoom,
      saveConfig: mockSaveConfig,
      isDirty: true,
    });

    render(<RoomEditor />);

    const saveButton = screen.getByRole("button", { name: /Save/i });
    expect(saveButton).not.toBeDisabled();
  });

  it("shows empty state when no agents available", () => {
    (useConfigStore as any).mockReturnValue({
      rooms: [mockRoom],
      agents: [],
      config: mockConfig,
      selectedRoomId: "lobby",
      updateRoom: mockUpdateRoom,
      deleteRoom: mockDeleteRoom,
      saveConfig: mockSaveConfig,
      isDirty: false,
    });

    render(<RoomEditor />);

    expect(screen.getByText("No agents available")).toBeInTheDocument();
  });

  it("shows help text for agent selection", () => {
    render(<RoomEditor />);

    expect(
      screen.getByText(/Select agents that should have access to this room/),
    ).toBeInTheDocument();
  });
});
