import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, it } from "vite-plus/test";

import { syncVersions } from "./version-sync-core.mjs";

function writeJson(filePath, value) {
  writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
}

function createVersionFixture({ cargoLockLineEnding = "\n", cargoLockContent } = {}) {
  const root = mkdtempSync(path.join(tmpdir(), "moor-version-sync-"));
  mkdirSync(path.join(root, "sidecar"), { recursive: true });
  mkdirSync(path.join(root, "packages", "types"), { recursive: true });
  mkdirSync(path.join(root, "src-tauri"), { recursive: true });

  writeJson(path.join(root, "sidecar", "package.json"), {
    name: "moor-sidecar",
    version: "0.2.1-beta.1",
  });
  writeJson(path.join(root, "packages", "types", "package.json"), {
    name: "@moor/types",
    version: "0.2.1-beta.0",
  });
  writeJson(path.join(root, "package.json"), {
    name: "moor",
    version: "0.2.1-beta.0",
  });
  writeJson(path.join(root, "src-tauri", "tauri.conf.json"), {
    productName: "Moor",
    version: "0.2.1-beta.0",
  });
  writeFileSync(
    path.join(root, "src-tauri", "Cargo.toml"),
    '[package]\nname = "moor"\nversion = "0.2.1-beta.0"\nedition = "2021"\n',
  );
  writeFileSync(
    path.join(root, "src-tauri", "Cargo.lock"),
    cargoLockContent ??
      [
        "# This file is automatically @generated by Cargo.",
        "",
        "[[package]]",
        'name = "other"',
        'version = "1.0.0"',
        "",
        "[[package]]",
        'name = "moor"',
        'version = "0.2.1-beta.0"',
        "dependencies = [",
        ' "serde",',
        "]",
        "",
      ].join(cargoLockLineEnding),
  );

  return root;
}

describe("version sync", () => {
  it("syncVersions updates Cargo.lock for the local moor package", () => {
    const root = createVersionFixture();
    const logs = [];

    try {
      const result = syncVersions({
        root,
        checkOnly: false,
        log: (message) => logs.push(message),
        error: (message) => logs.push(message),
      });

      assert.equal(result.expected, "0.2.1-beta.1");
      assert.equal(result.hasMismatch, true);

      const cargoLock = readFileSync(path.join(root, "src-tauri", "Cargo.lock"), "utf8");
      assert.match(cargoLock, /name = "moor"\nversion = "0\.2\.1-beta\.1"/);
      assert.match(logs.join("\n"), /\[synced\] Cargo\.lock/);
    } finally {
      rmSync(root, { recursive: true, force: true });
    }
  });

  it("syncVersions check-only reports Cargo.lock mismatch without writing", () => {
    const root = createVersionFixture();

    try {
      const result = syncVersions({
        root,
        checkOnly: true,
        log: () => {},
        error: () => {},
      });

      assert.equal(result.hasMismatch, true);
      assert.equal(result.exitCode, 1);

      const cargoLock = readFileSync(path.join(root, "src-tauri", "Cargo.lock"), "utf8");
      assert.match(cargoLock, /name = "moor"\nversion = "0\.2\.1-beta\.0"/);
    } finally {
      rmSync(root, { recursive: true, force: true });
    }
  });

  it("syncVersions check-only supports CRLF Cargo.lock line endings", () => {
    const root = createVersionFixture({ cargoLockLineEnding: "\r\n" });
    const logs = [];

    try {
      const result = syncVersions({
        root,
        checkOnly: true,
        log: (message) => logs.push(message),
        error: (message) => logs.push(message),
      });

      assert.equal(result.hasMismatch, true);
      assert.doesNotMatch(logs.join("\n"), /Cargo\.lock: null/);
      assert.match(logs.join("\n"), /\[mismatch\] Cargo\.lock: 0\.2\.1-beta\.0/);
    } finally {
      rmSync(root, { recursive: true, force: true });
    }
  });

  it("syncVersions updates CRLF Cargo.lock for the local moor package", () => {
    const root = createVersionFixture({ cargoLockLineEnding: "\r\n" });

    try {
      const result = syncVersions({
        root,
        checkOnly: false,
        log: () => {},
        error: () => {},
      });

      assert.equal(result.hasMismatch, true);

      const cargoLock = readFileSync(path.join(root, "src-tauri", "Cargo.lock"), "utf8");
      assert.match(cargoLock, /name = "moor"\r\nversion = "0\.2\.1-beta\.1"/);
    } finally {
      rmSync(root, { recursive: true, force: true });
    }
  });

  it("syncVersions reports invalid Cargo.lock package metadata explicitly", () => {
    const root = createVersionFixture({
      cargoLockContent:
        '# This file is automatically @generated by Cargo.\n\n[[package]]\nname = "other"\nversion = "1.0.0"\n',
    });
    const logs = [];

    try {
      const result = syncVersions({
        root,
        checkOnly: true,
        log: (message) => logs.push(message),
        error: (message) => logs.push(message),
      });

      assert.equal(result.exitCode, 1);
      assert.match(
        logs.join("\n"),
        /\[invalid\] Cargo\.lock: package "moor" section was not found or has no readable version/,
      );
    } finally {
      rmSync(root, { recursive: true, force: true });
    }
  });
});
