// #region TestAgentChat.ConversationList [C:2] [TYPE Module] [SEMANTICS test,conversation,list,ui]
// @BRIEF L2 UX tests for ConversationList — renders conversations, groups by date, search, delete.
// @RELATION BINDS_TO -> [AgentChat.ConversationList]
// @UX_STATE loading -> skeleton visible
// @UX_STATE loaded -> grouped list with conversation titles
// @UX_STATE empty -> "No conversations" placeholder
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/svelte";
import ConversationList from "../ConversationList.svelte";

// Mock i18n — t must be a store with subscribe() returning assistant namespace
vi.mock("$lib/i18n/index.svelte.js", () => {
  const tStore = {
    subscribe: (run: (v: unknown) => void) => {
      run({
        assistant: {
          search_placeholder: "Search conversations...",
          no_conversations: "No conversations",
          delete: "Delete",
          load_more: "Load more",
          delete_confirm: "Delete this conversation?",
        },
      });
      return () => {};
    },
  };
  return { t: tStore };
});

const MOCK_CONVERSATIONS = [
  { id: "c1", title: "Chat About Dashboards", updated_at: new Date().toISOString(), message_count: 5 },
  { id: "c2", title: "Deploy Configuration", updated_at: new Date(Date.now() - 86400000).toISOString(), message_count: 3 },
  { id: "c3", title: "Migration Plan", updated_at: new Date(Date.now() - 86400000 * 3).toISOString(), message_count: 12 },
];

// #region TestAgentChat.ConversationList.Render [C:2] [TYPE Test]
// @UX_STATE loaded — conversations render with titles, grouped by date.
describe("ConversationList — loaded state", () => {
  it("renders conversation titles", () => {
    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
      },
    });
    expect(screen.getByText("Chat About Dashboards")).toBeTruthy();
    expect(screen.getByText("Deploy Configuration")).toBeTruthy();
    expect(screen.getByText("Migration Plan")).toBeTruthy();
  });

  it("highlights active conversation with bg class", () => {
    const { container } = render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: "c1",
        isLoading: false,
        hasNext: false,
      },
    });
    // The active conversation has class "bg-surface-muted"
    const items = container.querySelectorAll('[role="button"]');
    let found = false;
    items.forEach((item) => {
      if (item.className.includes("bg-surface-muted")) {
        found = true;
      }
    });
    expect(found).toBe(true);
  });

  it("shows message count when > 0", () => {
    const { container } = render(ConversationList, {
      props: {
        conversations: [{ id: "c1", title: "Test", updated_at: new Date().toISOString(), message_count: 5 }],
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
      },
    });
    expect(container.textContent).toContain("5");
    expect(container.textContent).toContain("msgs");
  });
});
// #endregion TestAgentChat.ConversationList.Render

// #region TestAgentChat.ConversationList.Loading [C:2] [TYPE Test]
// @UX_STATE loading — skeleton cards visible.
describe("ConversationList — loading state", () => {
  it("shows skeleton when isLoading is true and no conversations", () => {
    const { container } = render(ConversationList, {
      props: {
        conversations: [],
        currentConversationId: null,
        isLoading: true,
        hasNext: false,
      },
    });
    const skeleton = container.querySelector(".animate-pulse");
    expect(skeleton).toBeTruthy();
  });

  it("does not show skeleton when conversations are already loaded", () => {
    const { container } = render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: true,
        hasNext: false,
      },
    });
    const skeleton = container.querySelector(".animate-pulse");
    expect(skeleton).toBeFalsy();
  });
});
// #endregion TestAgentChat.ConversationList.Loading

// #region TestAgentChat.ConversationList.Empty [C:2] [TYPE Test]
// @UX_STATE empty — "No conversations" placeholder shown when no conversations and not loading.
describe("ConversationList — empty state", () => {
  it("shows empty placeholder when no conversations", () => {
    render(ConversationList, {
      props: {
        conversations: [],
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
      },
    });
    const emptyText = screen.queryByText(/no conversations/i);
    expect(emptyText).toBeTruthy();
  });
});
// #endregion TestAgentChat.ConversationList.Empty

// #region TestAgentChat.ConversationList.Search [C:2] [TYPE Test]
// @UX_STATE loaded — search fires onsearch with debounce.
describe("ConversationList — search", () => {
  it("fires onsearch with 300ms debounce when typing", async () => {
    vi.useFakeTimers();
    const onsearch = vi.fn();
    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
        onsearch,
      },
    });
    const input = screen.getByPlaceholderText(/search/i);
    expect(input).toBeTruthy();
    await fireEvent.input(input, { target: { value: "dash" } });
    // Should not fire immediately (debounce 300ms)
    expect(onsearch).not.toHaveBeenCalled();
    // Advance past debounce
    await vi.advanceTimersByTimeAsync(350);
    expect(onsearch).toHaveBeenCalledWith("dash");
    vi.useRealTimers();
  });
});
// #endregion TestAgentChat.ConversationList.Search

// #region TestAgentChat.ConversationList.Delete [C:2] [TYPE Test]
// @UX_STATE loaded — delete button opens ConfirmDialog, confirm triggers ondelete.
describe("ConversationList — delete action", () => {
  it("calls ondelete after confirming in the dialog", () => {
    const ondelete = vi.fn();

    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
        ondelete,
      },
    });

    // Find delete buttons by title attribute
    const deleteBtns = screen.getAllByTitle(/delete/i);
    expect(deleteBtns.length).toBeGreaterThan(0);
    fireEvent.click(deleteBtns[0]);

    // ConfirmDialog appears — click the "Delete" button inside it
    const confirmBtn = screen.getByText("Delete");
    expect(confirmBtn).toBeTruthy();
    fireEvent.click(confirmBtn);

    expect(ondelete).toHaveBeenCalledWith("c1");
  });

  it("does not call ondelete when dialog is cancelled", () => {
    const ondelete = vi.fn();

    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
        ondelete,
      },
    });

    const deleteBtns = screen.getAllByTitle(/delete/i);
    fireEvent.click(deleteBtns[0]);

    // ConfirmDialog appears — click "Cancel" button
    const cancelBtn = screen.getByText("Cancel");
    expect(cancelBtn).toBeTruthy();
    fireEvent.click(cancelBtn);

    expect(ondelete).not.toHaveBeenCalled();
  });
});
// #endregion TestAgentChat.ConversationList.Delete

// #region TestAgentChat.ConversationList.Props [C:2] [TYPE Test]
// @UX_STATE loaded — callbacks are properly wired.
describe("ConversationList — callbacks", () => {
  it("fires onselect when clicking a conversation", () => {
    const onselect = vi.fn();
    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
        onselect,
      },
    });
    const chatItem = screen.getByText("Chat About Dashboards");
    fireEvent.click(chatItem);
    expect(onselect).toHaveBeenCalledWith("c1");
  });

  it("renders load more button when hasNext is true", () => {
    const onloadmore = vi.fn();
    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: true,
        onloadmore,
      },
    });
    const loadMore = screen.queryByText(/load more/i);
    expect(loadMore).toBeTruthy();
    if (loadMore) {
      fireEvent.click(loadMore);
      expect(onloadmore).toHaveBeenCalled();
    }
  });

  it("does not render load more button when hasNext is false", () => {
    render(ConversationList, {
      props: {
        conversations: MOCK_CONVERSATIONS,
        currentConversationId: null,
        isLoading: false,
        hasNext: false,
      },
    });
    expect(screen.queryByText(/load more/i)).toBeNull();
  });
});
// #endregion TestAgentChat.ConversationList.Props
// #endregion TestAgentChat.ConversationList
