import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { runE2eSyncCheck } from "./e2e-flows";

describe("runE2eSyncCheck", () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-check-e2e-"));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  function setupE2eFixture(
    stories: { name: string; scenarioPatterns: string[] }[],
    specContent: string | null,
  ) {
    const flow = "item-lifecycle";
    const storyDir = path.join(tmpDir, "my-app", "docs", "business-flow", flow, "story");
    fs.mkdirSync(storyDir, { recursive: true });

    for (const story of stories) {
      const patterns = story.scenarioPatterns.map((p) => `- ${p}`).join("\n");
      fs.writeFileSync(
        path.join(storyDir, `${story.name}.md`),
        `# Story\n\n## Scenario Patterns\n\n${patterns}\n`,
      );
    }

    if (specContent !== null) {
      const specDir = path.join(tmpDir, "my-app", "frontend", "e2e", "tests");
      fs.mkdirSync(specDir, { recursive: true });
      fs.writeFileSync(path.join(specDir, `${flow}.spec.ts`), specContent);
    }
  }

  it("reports missing-test-file when spec file does not exist", async () => {
    setupE2eFixture(
      [{ name: "admin--create-item", scenarioPatterns: ["Admin creates a new item"] }],
      null,
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toContainEqual(
      expect.objectContaining({
        type: "missing-test-file",
        category: "e2e-scenario",
        expectedBasename: "item-lifecycle.spec.ts",
      }),
    );
  });

  it("reports missing describe block as missing-test-file", async () => {
    setupE2eFixture(
      [{ name: "admin--create-item", scenarioPatterns: ["Admin creates a new item"] }],
      'describe("other-story", () => { test("something", async () => {}); });',
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toContainEqual(
      expect.objectContaining({
        type: "missing-test-file",
        category: "e2e-scenario",
        expectedBasename: 'describe("admin--create-item")',
      }),
    );
  });

  it("reports missing-test-case when scenario pattern not in spec", async () => {
    setupE2eFixture(
      [
        {
          name: "admin--create-item",
          scenarioPatterns: ["Admin creates a new item", "Admin creates item with all fields"],
        },
      ],
      'describe("admin--create-item", () => { test("Admin creates a new item", async () => {}); });',
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toContainEqual(
      expect.objectContaining({
        type: "missing-test-case",
        category: "e2e-scenario",
        expectedBasename: "Admin creates item with all fields",
      }),
    );
  });

  it("reports extra-test-case when spec has test not in doc", async () => {
    setupE2eFixture(
      [{ name: "admin--create-item", scenarioPatterns: ["Admin creates a new item"] }],
      'describe("admin--create-item", () => { test("Admin creates a new item", async () => {}); test("Extra test", async () => {}); });',
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toContainEqual(
      expect.objectContaining({
        type: "extra-test-case",
        category: "e2e-scenario",
        expectedBasename: "Extra test",
      }),
    );
  });

  it("returns no errors when scenarios and spec match", async () => {
    setupE2eFixture(
      [
        {
          name: "admin--create-item",
          scenarioPatterns: ["Admin creates a new item"],
        },
        {
          name: "admin--activate-item",
          scenarioPatterns: ["Admin activates a DRAFT item"],
        },
      ],
      `
describe("admin--create-item", () => {
  test("Admin creates a new item", async () => {});
});
describe("admin--activate-item", () => {
  test("Admin activates a DRAFT item", async () => {});
});`,
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toHaveLength(0);
  });

  it("skips stories with no Scenario Patterns", async () => {
    const flow = "item-lifecycle";
    const storyDir = path.join(tmpDir, "my-app", "docs", "business-flow", flow, "story");
    fs.mkdirSync(storyDir, { recursive: true });
    fs.writeFileSync(
      path.join(storyDir, "admin--create-item.md"),
      "# Create Item\n\n## Test Cases\n\n- some test\n",
    );

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toHaveLength(0);
  });

  it("reports orphaned-spec when spec has no matching business flow", async () => {
    setupE2eFixture(
      [{ name: "admin--create-item", scenarioPatterns: ["Admin creates a new item"] }],
      'describe("admin--create-item", () => { test("Admin creates a new item", async () => {}); });',
    );
    // Add an extra spec file with no matching flow
    const specDir = path.join(tmpDir, "my-app", "frontend", "e2e", "tests");
    fs.writeFileSync(path.join(specDir, "navigation.spec.ts"), 'test("nav", async () => {});');

    const { errors } = await runE2eSyncCheck("my-app", tmpDir);
    expect(errors).toContainEqual(
      expect.objectContaining({
        type: "orphaned-spec",
        category: "e2e-scenario",
        expectedBasename: "navigation.spec.ts",
      }),
    );
  });
});
