// @vitest-environment jsdom

import { MarkdownView, TFile } from "obsidian";
import { describe, expect, it, vi } from "vitest";

import { registerSelectionRewriteCommand } from "../../../src/features/selection-rewrite/command.obsidian";
import type { SelectionRewritePopoverOptions } from "../../../src/features/selection-rewrite/popover.dom";
import type { SelectionRewriteTransport } from "../../../src/features/selection-rewrite/transport";

const popoverMock = vi.hoisted(() => {
  const instances: { options: SelectionRewritePopoverOptions; open: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> }[] = [];
  return {
    instances,
    reset(): void {
      instances.length = 0;
    },
  };
});

vi.mock("../../../src/features/selection-rewrite/popover.dom", () => {
  class SelectionRewritePopover {
    readonly open = vi.fn();
    readonly close = vi.fn();

    constructor(readonly options: SelectionRewritePopoverOptions) {
      popoverMock.instances.push({ options, open: this.open, close: this.close });
    }
  }

  return { SelectionRewritePopover };
});

describe("selection rewrite command", () => {
  it("captures the unsaved editor buffer and clones the target range", () => {
    popoverMock.reset();
    const addedCommand = { current: null as null | { editorCallback: (editor: unknown, view: unknown) => void } };
    const cleanup = { current: null as null | (() => void) };
    const plugin = {
      settings: {
        codexPath: "/usr/local/bin/codex",
        sendShortcut: "enter",
        rewriteSelectionModel: null,
        rewriteSelectionEffort: null,
      },
      vaultPath: "/vault",
      register: vi.fn((callback: () => void) => {
        cleanup.current = callback;
      }),
      addCommand: vi.fn((command: { editorCallback: (editor: unknown, view: unknown) => void }) => {
        addedCommand.current = command;
      }),
    };
    const from = { line: 1, ch: 0 };
    const to = { line: 1, ch: 22 };
    const editor = {
      getSelection: vi.fn(() => "Revise this sentence."),
      getValue: vi.fn(() => "# Draft heading\n\nRevise this sentence.\n\nUnsaved paragraph."),
      getCursor: vi.fn((which: "from" | "to") => (which === "from" ? from : to)),
    };
    const view = new MarkdownView({} as never);
    Object.assign(view, { containerEl: { doc: document } });
    view.file = Object.assign(new TFile(), { path: "Draft.md", basename: "Draft" });

    const transport: SelectionRewriteTransport = { generate: vi.fn() };
    const controller = registerSelectionRewriteCommand(plugin as never, transport);
    expect(addedCommand.current).not.toBeNull();
    addedCommand.current?.editorCallback(editor, view);
    from.line = 99;
    to.ch = 99;

    const captured = popoverMock.instances[0]?.options.state;
    expect(captured).toMatchObject({
      filePath: "Draft.md",
      originalText: "Revise this sentence.",
      noteText: "# Draft heading\n\nRevise this sentence.\n\nUnsaved paragraph.",
      targetRange: {
        from: { line: 1, ch: 0 },
        to: { line: 1, ch: 22 },
      },
    });
    expect(popoverMock.instances[0]?.open).toHaveBeenCalledOnce();
    expect(popoverMock.instances[0]?.options.transport).toBe(transport);

    controller.closeAll();
    expect(popoverMock.instances[0]?.close).toHaveBeenCalledOnce();

    cleanup.current?.();
    expect(popoverMock.instances[0]?.close).toHaveBeenCalledOnce();
  });
});
