import assert from "node:assert/strict";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, it } from "node:test";

import {
  skillLearn,
  skillPublish,
  skillSearch,
} from "../geminiSkillEvolutionTools.ts";

async function makeProject(): Promise<string> {
  const root = await mkdtemp(path.join(os.tmpdir(), "cfmu-skill-evolution-"));
  await mkdir(path.join(root, "skills", "existing-skill"), { recursive: true });
  await writeFile(
    path.join(root, "skills", "existing-skill", "SKILL.md"),
    "---\nname: existing-skill\ndescription: Search release documentation and validate version changes.\n---\n\n# Existing\n",
    "utf8",
  );
  const manifest = { version: 1, kind: "agent_skills_manifest", common_skills: [] };
  await mkdir(path.join(root, "docs", "skills"), { recursive: true });
  await mkdir(path.join(root, ".codeflowmu"), { recursive: true });
  await writeFile(path.join(root, "docs", "skills", "agent-skills.manifest.json"), JSON.stringify(manifest), "utf8");
  await writeFile(path.join(root, ".codeflowmu", "agent-skills.manifest.json"), JSON.stringify(manifest), "utf8");
  return root;
}

describe("geminiSkillEvolutionTools", () => {
  it("skillSearch combines local, GitHub, and web providers", async () => {
    const root = await makeProject();
    const mockFetch: typeof fetch = async (input) => {
      const url = String(input);
      if (url.includes("api.github.com")) {
        return new Response(JSON.stringify({ items: [{
          full_name: "example/release-skill",
          description: "Release automation skill",
          html_url: "https://github.com/example/release-skill",
          stargazers_count: 42,
          updated_at: "2026-06-01T00:00:00Z",
          license: { spdx_id: "MIT" },
        }] }), { status: 200, headers: { "content-type": "application/json" } });
      }
      return new Response('<a class="result__a" href="https://docs.example.com/release">Release docs</a><a class="result__snippet">Official guide</a>', { status: 200, headers: { "content-type": "text/html" } });
    };
    const result = await skillSearch(root, { query: "release", limit: 3 }, mockFetch);
    const providers = (result.results as Array<Record<string, unknown>>).map((row) => row.provider);
    assert.equal(providers.includes("local"), true);
    assert.equal(providers.includes("github"), true);
    assert.equal(providers.includes("web"), true);
    assert.deepEqual(result.provider_counts, { local: 1, github: 1, web: 1 });
  });

  it("skillLearn reads local and remote sources with a validation contract", async () => {
    const root = await makeProject();
    const mockFetch: typeof fetch = async () => new Response(
      "# Remote Skill\n\nInstall the package and run a smoke test before use.",
      { status: 200, headers: { "content-type": "text/plain" } },
    );
    const result = await skillLearn(root, {
      capability: "release automation",
      local_skill_ids: ["existing-skill"],
      source_urls: ["https://github.com/example/repo/blob/main/SKILL.md"],
    }, mockFetch);
    assert.equal((result.sources as unknown[]).length, 2);
    assert.match(JSON.stringify(result.learning_contract), /minimal_validation/);
  });

  it("skillPublish rejects unvalidated learning", async () => {
    const root = await makeProject();
    await assert.rejects(
      () => skillPublish(root, {
        skill_id: "new-skill",
        description: "A sufficiently detailed description for the new reusable capability.",
        body_markdown: "x".repeat(150),
        validated: false,
        validation_evidence: [],
        source_urls: ["https://example.com/source"],
      }),
      /validated=true/,
    );
  });

  it("skillPublish creates SKILL.md and updates both manifests", async () => {
    const root = await makeProject();
    const result = await skillPublish(root, {
      skill_id: "release-check",
      display_name: "Release Check",
      description: "Validate release metadata, package versions, and smoke-test evidence before publishing.",
      body_markdown: "## Workflow\n\n1. Inspect release metadata.\n2. Run the package smoke test.\n3. Compare outputs.\n\n## Guardrails\n\nDo not publish when evidence is missing.",
      validated: true,
      validation_evidence: ["npm test passed with 12 tests"],
      source_urls: ["https://docs.example.com/releases"],
    });
    assert.equal(result.published, true);
    const skill = await readFile(path.join(root, "skills", "release-check", "SKILL.md"), "utf8");
    assert.match(skill, /name: release-check/);
    assert.match(skill, /npm test passed/);
    for (const rel of ["docs/skills/agent-skills.manifest.json", ".codeflowmu/agent-skills.manifest.json"]) {
      const manifest = JSON.parse(await readFile(path.join(root, rel), "utf8"));
      assert.equal(manifest.common_skills[0].id, "release-check");
    }
  });
});
