// @vitest-environment happy-dom
import "@testing-library/jest-dom/vitest";
import { render, screen } from "@testing-library/react";
import { vi, describe, it, expect } from "vitest";
import { buildCommentIndex } from "./comment-index";
import type { ReviewComment } from "./types";

// ── Stub CommentThread and CommentForm so LineWithComments renders without deps ──
vi.mock("./CommentThread", () => ({
  CommentThread: () => <div data-testid="comment-thread" />,
}));
vi.mock("./CommentForm", () => ({
  CommentForm: vi.fn().mockReturnValue(<div data-testid="comment-form" />),
}));
vi.mock("./use-syntax-highlight", () => ({
  useHighlightedFile: () => null,
}));

// ── Helpers ──────────────────────────────────────────────────────────────────

function makeComment(overrides: Partial<ReviewComment> & { id: string }): ReviewComment {
  return {
    id: overrides.id,
    filePath: overrides.filePath ?? "src/foo.ts",
    lineNumber: overrides.lineNumber !== undefined ? overrides.lineNumber : 1,
    lineType: overrides.lineType ?? "added",
    body: overrides.body ?? "comment body",
    author: overrides.author ?? "alice",
    createdAt: overrides.createdAt ?? "2024-01-01T00:00:00Z",
    resolved: overrides.resolved ?? false,
    parentCommentId: overrides.parentCommentId ?? null,
  };
}

// ── buildCommentIndex — pure function tests ───────────────────────────────────

describe("buildCommentIndex", () => {
  it("happy path — basic indexing", () => {
    const c1 = makeComment({ id: "1", filePath: "src/foo.ts", lineNumber: 10, lineType: "added" });
    const c2 = makeComment({ id: "2", filePath: "src/bar.ts", lineNumber: 5, lineType: "context" });
    const map = buildCommentIndex([c1, c2]);
    expect(map.get("src/foo.ts:10:added")).toEqual([c1]);
    expect(map.get("src/bar.ts:5:context")).toEqual([c2]);
    expect(map.size).toBe(2);
  });

  it("same-line accumulation — two comments land in same array", () => {
    const c1 = makeComment({ id: "a", filePath: "src/x.ts", lineNumber: 20, lineType: "added" });
    const c2 = makeComment({ id: "b", filePath: "src/x.ts", lineNumber: 20, lineType: "added" });
    const map = buildCommentIndex([c1, c2]);
    expect(map.get("src/x.ts:20:added")).toEqual([c1, c2]);
    expect(map.size).toBe(1);
  });

  it("cross-type isolation — same lineNumber but different lineType produces separate entries", () => {
    const added = makeComment({ id: "a", filePath: "src/x.ts", lineNumber: 10, lineType: "added" });
    const removed = makeComment({ id: "r", filePath: "src/x.ts", lineNumber: 10, lineType: "removed" });
    const map = buildCommentIndex([added, removed]);
    expect(map.get("src/x.ts:10:added")).toEqual([added]);
    expect(map.get("src/x.ts:10:removed")).toEqual([removed]);
    expect(map.size).toBe(2);
  });

  it("null lineNumber — file-level comment stored under filePath:file", () => {
    const fileComment = makeComment({ id: "f", filePath: "src/z.ts", lineNumber: null, lineType: "context" });
    const map = buildCommentIndex([fileComment]);
    expect(map.get("src/z.ts:file")).toEqual([fileComment]);
  });

  it("empty input — returns empty map", () => {
    expect(buildCommentIndex([])).toEqual(new Map());
  });
});

// ── LineWithComments — component tests ───────────────────────────────────────

import { LineWithComments } from "./LineWithComments";
import type { DiffLine } from "./types";

const addedLine: DiffLine = {
  type: "added",
  content: "const x = 1;",
  oldLineNumber: null,
  newLineNumber: 5,
};

const noop = () => {};

describe("LineWithComments", () => {
  it("renders comment button with title when lineComments is non-empty", () => {
    const lineComments = [
      makeComment({ id: "c1", filePath: "src/foo.ts", lineNumber: 5, lineType: "added" }),
    ];
    render(
      <table>
        <tbody>
          <LineWithComments
            line={addedLine}
            lineKey={5}
            lineComments={lineComments}
            isCommenting={false}
            onStartComment={noop}
            onCancelComment={noop}
            onSubmitComment={noop}
            onResolveComment={noop}
            onUnresolveComment={noop}
          />
        </tbody>
      </table>,
    );
    expect(screen.getByTitle(/1 comment/)).toBeInTheDocument();
  });

  it("does not render comment thread row when lineComments is empty", () => {
    render(
      <table>
        <tbody>
          <LineWithComments
            line={addedLine}
            lineKey={5}
            lineComments={[]}
            isCommenting={false}
            onStartComment={noop}
            onCancelComment={noop}
            onSubmitComment={noop}
            onResolveComment={noop}
            onUnresolveComment={noop}
          />
        </tbody>
      </table>,
    );
    expect(document.querySelector("[data-comment-id]")).toBeNull();
  });
});
