#!/usr/bin/env npx tsx

/*
Tests for the sync-back.ts script
*/

import * as assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";

import {
  scanGeneratedWorkflows,
  updateSyncTs,
  updateTemplateFiles,
} from "./sync-back";

let testDir: string;
let workflowDir: string;
let checksDir: string;
let syncTsPath: string;

beforeEach(() => {
  /** Set up temporary directories and files for testing */
  testDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-back-test-"));
  workflowDir = path.join(testDir, ".github", "workflows");
  checksDir = path.join(testDir, "pr-checks", "checks");
  fs.mkdirSync(workflowDir, { recursive: true });
  fs.mkdirSync(checksDir, { recursive: true });

  // Create sync.ts file path
  syncTsPath = path.join(testDir, "pr-checks", "sync.ts");
});

afterEach(() => {
  /** Clean up temporary directories */
  fs.rmSync(testDir, { recursive: true, force: true });
});

describe("scanGeneratedWorkflows", async () => {
  await it("basic workflow scanning", () => {
    /** Test basic workflow scanning functionality */
    const workflowContent = `
name: Test Workflow
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v5
      - uses: actions/setup-go@v6
`;

    fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);

    const result = scanGeneratedWorkflows(workflowDir);

    assert.equal(result["actions/checkout"], "v4");
    assert.equal(result["actions/setup-node"], "v5");
    assert.equal(result["actions/setup-go"], "v6");
  });

  await it("scanning workflows with version comments", () => {
    /** Test scanning workflows with version comments */
    const workflowContent = `
name: Test Workflow
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
      - uses: actions/setup-python@v6 # Latest Python
`;

    fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);

    const result = scanGeneratedWorkflows(workflowDir);

    assert.equal(result["actions/checkout"], "v4");
    assert.equal(
      result["ruby/setup-ruby"],
      "44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
    );
    assert.equal(result["actions/setup-python"], "v6 # Latest Python");
  });

  await it("ignores local actions", () => {
    /** Test that local actions (starting with ./) are ignored */
    const workflowContent = `
name: Test Workflow
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/local-action
      - uses: ./another-local-action@v1
`;

    fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);

    const result = scanGeneratedWorkflows(workflowDir);

    assert.equal(result["actions/checkout"], "v4");
    assert.equal("./.github/actions/local-action" in result, false);
    assert.equal("./another-local-action" in result, false);
  });
});

describe("updateSyncTs", async () => {
  await it("updates sync.ts file", () => {
    /** Test updating sync.ts file */
    const syncTsContent = `
const steps = [
  {
    uses: "actions/setup-node@v4",
    with: { "node-version": "16" },
  },
  {
    uses: "actions/setup-go@v5",
    with: { "go-version": "1.19" },
  },
];
`;

    fs.writeFileSync(syncTsPath, syncTsContent);

    const actionVersions = {
      "actions/setup-node": "v5",
      "actions/setup-go": "v6",
    };

    const result = updateSyncTs(syncTsPath, actionVersions);
    assert.equal(result, true);

    const updatedContent = fs.readFileSync(syncTsPath, "utf8");

    assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
    assert.ok(updatedContent.includes('uses: "actions/setup-go@v6"'));
  });

  await it("strips comments from versions", () => {
    /** Test updating sync.ts file when versions have comments */
    const syncTsContent = `
const steps = [
  {
    uses: "actions/setup-node@v4",
    with: { "node-version": "16" },
  },
];
`;

    fs.writeFileSync(syncTsPath, syncTsContent);

    const actionVersions = {
      "actions/setup-node": "v5 # Latest version",
    };

    const result = updateSyncTs(syncTsPath, actionVersions);
    assert.equal(result, true);

    const updatedContent = fs.readFileSync(syncTsPath, "utf8");

    // sync.ts should get the version without comment
    assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
    assert.ok(!updatedContent.includes("# Latest version"));
  });

  await it("returns false when no changes are needed", () => {
    /** Test that updateSyncTs returns false when no changes are needed */
    const syncTsContent = `
const steps = [
  {
    uses: "actions/setup-node@v5",
    with: { "node-version": "16" },
  },
];
`;

    fs.writeFileSync(syncTsPath, syncTsContent);

    const actionVersions = {
      "actions/setup-node": "v5",
    };

    const result = updateSyncTs(syncTsPath, actionVersions);
    assert.equal(result, false);
  });
});

describe("updateTemplateFiles", async () => {
  await it("updates template files", () => {
    /** Test updating template files */
    const templateContent = `
name: Test Template
steps:
  - uses: actions/checkout@v3
  - uses: actions/setup-node@v4
    with:
      node-version: 16
`;

    const templatePath = path.join(checksDir, "test.yml");
    fs.writeFileSync(templatePath, templateContent);

    const actionVersions = {
      "actions/checkout": "v4",
      "actions/setup-node": "v5 # Latest",
    };

    const result = updateTemplateFiles(checksDir, actionVersions);
    assert.equal(result.length, 1);
    assert.ok(result.includes(templatePath));

    const updatedContent = fs.readFileSync(templatePath, "utf8");

    assert.ok(updatedContent.includes("uses: actions/checkout@v4"));
    assert.ok(updatedContent.includes("uses: actions/setup-node@v5 # Latest"));
  });

  await it("preserves version comments", () => {
    /** Test that updating template files preserves version comments */
    const templateContent = `
name: Test Template
steps:
  - uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.256.0
`;

    const templatePath = path.join(checksDir, "test.yml");
    fs.writeFileSync(templatePath, templateContent);

    const actionVersions = {
      "ruby/setup-ruby": "55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
    };

    const result = updateTemplateFiles(checksDir, actionVersions);
    assert.equal(result.length, 1);

    const updatedContent = fs.readFileSync(templatePath, "utf8");

    assert.ok(
      updatedContent.includes(
        "uses: ruby/setup-ruby@55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
      ),
    );
  });
});
