import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { createPinnedMessages } from "@components/PinnedMessages";
import type { PinnedMessage, PinnedMessagesOptions } from "@components/PinnedMessages";

const samplePins: PinnedMessage[] = [
  {
    id: 1,
    content: "Hello world",
    author: "Alice",
    timestamp: "2024-01-01T12:00:00Z",
    avatarColor: "#5865f2",
  },
  {
    id: 2,
    content: "Important notice",
    author: "Bob",
    timestamp: "2024-01-02T14:30:00Z",
    avatarColor: "#e74c3c",
  },
  {
    id: 3,
    content: "Reminder",
    author: "Charlie",
    timestamp: "2024-01-03T09:00:00Z",
    avatarColor: "#2ecc71",
  },
];

describe("PinnedMessages", () => {
  let container: HTMLDivElement;

  beforeEach(() => {
    container = document.createElement("div");
    document.body.appendChild(container);
  });

  afterEach(() => {
    container.remove();
  });

  function makePanel(overrides?: Partial<PinnedMessagesOptions>) {
    const options: PinnedMessagesOptions = {
      channelId: 1,
      pinnedMessages: overrides?.pinnedMessages ?? samplePins,
      onUnpin: overrides?.onUnpin ?? vi.fn(),
      onJumpToMessage: overrides?.onJumpToMessage ?? vi.fn(),
      onClose: overrides?.onClose ?? vi.fn(),
    };
    const panel = createPinnedMessages(options);
    panel.mount(container);
    return { panel, options };
  }

  it("mounts with pinned-panel class", () => {
    const { panel } = makePanel();
    expect(container.querySelector(".pinned-panel")).not.toBeNull();
    panel.destroy?.();
  });

  it("renders header with title", () => {
    const { panel } = makePanel();
    const title = container.querySelector("h3");
    expect(title).not.toBeNull();
    expect(title!.querySelector('svg[data-icon="pin"]')).not.toBeNull();
    expect(title!.textContent).toContain("Pinned Messages");
    panel.destroy?.();
  });

  it("renders close button", () => {
    const onClose = vi.fn();
    const { panel } = makePanel({ onClose });
    const closeBtn = container.querySelector(".pinned-panel__close") as HTMLButtonElement;
    expect(closeBtn).not.toBeNull();
    closeBtn.click();
    expect(onClose).toHaveBeenCalledOnce();
    panel.destroy?.();
  });

  it("renders pinned message items", () => {
    const { panel } = makePanel();
    const items = container.querySelectorAll(".pinned-msg");
    expect(items.length).toBe(3);
    panel.destroy?.();
  });

  it("shows author, content, and timestamp for each pin", () => {
    const { panel } = makePanel();
    const authors = container.querySelectorAll(".pinned-msg__author");
    const contents = container.querySelectorAll(".pinned-msg__content");
    const times = container.querySelectorAll(".pinned-msg__time");

    expect(authors[0]!.textContent).toBe("Alice");
    expect(contents[0]!.textContent).toBe("Hello world");
    // Timestamp is formatted by formatPinTime (locale-dependent output)
    expect(times[0]!.textContent).toBeTruthy();
    panel.destroy?.();
  });

  it("Jump button calls onJumpToMessage with message id", () => {
    const onJumpToMessage = vi.fn();
    const { panel } = makePanel({ onJumpToMessage });

    const jumpBtns = container.querySelectorAll(".pinned-msg__actions button");
    // Jump is the first button in each action group
    (jumpBtns[0] as HTMLButtonElement).click();
    expect(onJumpToMessage).toHaveBeenCalledWith(1);
    panel.destroy?.();
  });

  it("Unpin button calls onUnpin with message id", () => {
    const onUnpin = vi.fn();
    const { panel } = makePanel({ onUnpin });

    const unpinBtns = container.querySelectorAll(".pinned-msg__actions button");
    // Unpin is the second button in each action group
    (unpinBtns[1] as HTMLButtonElement).click();
    expect(onUnpin).toHaveBeenCalledWith(1);
    panel.destroy?.();
  });

  it("empty pinned messages shows empty state", () => {
    const { panel } = makePanel({ pinnedMessages: [] });

    const items = container.querySelectorAll(".pinned-msg");
    expect(items.length).toBe(0);

    // Empty state rendered, list not rendered
    const empty = container.querySelector(".pinned-panel__empty");
    expect(empty).not.toBeNull();
    const list = container.querySelector(".pinned-panel__list");
    expect(list).toBeNull();
    panel.destroy?.();
  });

  it("with pinned messages, empty state is not rendered", () => {
    const { panel } = makePanel();

    // List rendered, empty state not rendered
    const empty = container.querySelector(".pinned-panel__empty");
    expect(empty).toBeNull();
    const list = container.querySelector(".pinned-panel__list");
    expect(list).not.toBeNull();
    panel.destroy?.();
  });

  it("stores message id in dataset", () => {
    const { panel } = makePanel();
    const items = container.querySelectorAll(".pinned-msg");
    expect((items[0] as HTMLDivElement).dataset.messageId).toBe("1");
    expect((items[1] as HTMLDivElement).dataset.messageId).toBe("2");
    panel.destroy?.();
  });

  // ── accessibility (DC-13): landmark panel, not a modal ─────────────────────

  it("exposes the panel as a labeled complementary landmark", () => {
    const { panel } = makePanel();
    const root = container.querySelector(".pinned-panel") as HTMLElement;
    expect(root.getAttribute("role")).toBe("complementary");
    expect(root.getAttribute("aria-label")).toBe("Pinned messages");
    // A side panel is not modal — it must never claim aria-modal.
    expect(root.getAttribute("aria-modal")).toBeNull();
    panel.destroy?.();
  });

  it("labels the icon-only buttons for screen readers", () => {
    const { panel } = makePanel();

    const closeBtn = container.querySelector(".pinned-panel__close") as HTMLButtonElement;
    expect(closeBtn.getAttribute("aria-label")).toBe("Close pinned messages");

    const actionBtns = container.querySelectorAll(".pinned-msg__actions button");
    // Jump is the first button in each action group, unpin the second
    expect(actionBtns[0]!.getAttribute("aria-label")).toBe("Jump to message");
    expect(actionBtns[1]!.getAttribute("aria-label")).toBe("Unpin message");
    panel.destroy?.();
  });

  it("destroy removes DOM", () => {
    const { panel } = makePanel();
    expect(container.querySelector(".pinned-panel")).not.toBeNull();
    panel.destroy?.();
    expect(container.querySelector(".pinned-panel")).toBeNull();
  });
});
