import assert from "node:assert/strict";
import { existsSync, mkdtempSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import {
  buildSkillResourcePayload,
  materializeSkillPackages,
  parseSkillPackage,
  selectSkillsForRun
} from "../packages/skills/dist/index.js";
import { createSkillTools } from "@mastra/core/workspace";
import { LocalFileAssetService } from "../packages/files/dist/index.js";
import {
  createToolObservationBoundary,
  ToolObservationDispatcher
} from "../packages/agent-runtime/dist/testing.js";
import { createRunWorkspace } from "../packages/agent-runtime/dist/tools/workspace-factory.js";
import { createMetadataStore } from "../packages/metadata/dist/index.js";
import { createVerifiedTestIdentity } from "./lib/metadata-test-identity.mjs";

const root = mkdtempSync(join(tmpdir(), "open-data-foundry-skills-smoke-"));
const metadataStore = createMetadataStore({ database_path: join(root, "metadata.sqlite") });
const __testIdentity = createVerifiedTestIdentity(metadataStore);
const userId = __testIdentity.userId;
const workspaceId = __testIdentity.workspaceId;

const fileAssetService = new LocalFileAssetService(metadataStore, { storageRoot: join(root, "files") });

const packageBody = Buffer.from(`---
name: sql-analysis-smoke
description: Use for SQL analysis smoke tests.
version: 1.0.0
tags:
  - data-analysis
  - sql
allowed-tools:
  - inspect_schema
  - run_sql_readonly
---
Inspect schema first, then run read-only SQL.
中文触发词: 数据分析 查数 指标查询.
`, "utf8");

const parsed = await parseSkillPackage({
  content: packageBody,
  filename: "SKILL.md",
  mimeType: "text/markdown"
});
assert.equal(parsed.name, "sql-analysis-smoke");
assert.deepEqual(parsed.allowedTools, ["inspect_schema", "run_sql_readonly"]);

const packageRef = fileAssetService.createRef({
  user_id: userId,
  workspace_id: workspaceId,
  filename: "SKILL.md",
  content: packageBody,
  declared_mime_type: "text/markdown",
  source: "skill-package",
  metadata: { kind: "skill-package" }
});
const resource = metadataStore.configResources.upsert({
  id: "sql-analysis-smoke",
  workspace_id: workspaceId,
  user_id: userId,
  kind: "skill",
  name: parsed.name,
  description: parsed.description,
  payload: buildSkillResourcePayload({
    packageFileRefId: packageRef.ref.id,
    parsed
  }),
  default_enabled: true,
  status: "valid"
});

const selection = selectSkillsForRun({
  metadataStore,
  runConfig: {
    enabledSkillIds: [],
    skillIds: [],
    skillMode: "auto",
    skillPolicy: {
      deniedToolNames: [],
      maxSkills: 5,
      requireUserInvocable: true,
      strictSkillTools: false
    },
    skillTags: []
  },
  userId,
  userInput: "analyze data with sql",
  workspaceId
});
assert.equal(selection.selectedSkills[0]?.id, resource.id);
assert.equal(selection.effectiveToolPolicy.mergeStrategy, "union");
assert.equal(selection.effectiveToolPolicy.allowedTools?.includes("run_sql_readonly"), true);

const materialized = await materializeSkillPackages({
  fileAssetService,
  runDir: join(root, "workspace"),
  skills: selection.selectedSkills,
  userId,
  workspaceId
});
assert.equal(materialized[0]?.path, "skills/sql-analysis-smoke");
const materializedSkillPath = join(root, "workspace", "skills", "sql-analysis-smoke", "SKILL.md");
assert.equal(existsSync(materializedSkillPath), true);
assert.equal(readFileSync(materializedSkillPath, "utf8").includes("Inspect schema first"), true);

const runContext = {
  user_id: userId,
  workspace_id: workspaceId,
  session_id: "skill-search-session",
  run_id: "skill-search-run",
  selected_datasource_id: "skill-smoke-source",
  enabled_datasource_ids: ["skill-smoke-source"],
  user_input: "分析数据",
  chat_mode: "copilotkit",
  model_name: "skill-smoke-model"
};
const searchWorkspaceRoot = join(root, "runtime-workspace");
const searchWorkspace = createRunWorkspace({ runContext, workspaceRoot: searchWorkspaceRoot });
const searchMaterialized = await materializeSkillPackages({
  fileAssetService,
  runDir: searchWorkspace.skillCacheDir,
  skills: selection.selectedSkills,
  userId,
  workspaceId
});
assert.equal(searchMaterialized[0]?.path, "skills/sql-analysis-smoke");
const unselectedSkillBody = Buffer.from(`---
name: unselected-report-smoke
description: Use for unselected report smoke tests.
version: 1.0.0
tags:
  - report
allowed-tools:
  - write_file
---
中文触发词: 报告草稿.
`, "utf8");
const unselectedSkillRef = fileAssetService.createRef({
  user_id: userId,
  workspace_id: workspaceId,
  filename: "SKILL.md",
  content: unselectedSkillBody,
  declared_mime_type: "text/markdown",
  source: "skill-package",
  metadata: { kind: "skill-package", skill: "unselected-report-smoke", version: "1.0.0" }
});
await materializeSkillPackages({
  fileAssetService,
  runDir: searchWorkspace.skillCacheDir,
  skills: [{
    allowedTools: ["write_file"],
    builtin: false,
    defaultDbIds: [],
    defaultEnabled: false,
    defaultKbIds: [],
    defaultMcpIds: [],
    deniedTools: [],
    description: "Use for unselected report smoke tests.",
    id: "unselected-report-smoke",
    name: "unselected-report-smoke",
    packageEntry: "SKILL.md",
    packageFileRefId: unselectedSkillRef.ref.id,
    packageFiles: ["SKILL.md"],
    packageFormat: "skill-md",
    revision: 1,
    scope: "workspace",
    status: "valid",
    tags: ["report"],
    userInvocable: true,
    version: "1.0.0"
  }],
  userId,
  workspaceId
});
assert.equal(searchWorkspace.skillCacheDir.startsWith(searchWorkspace.runDir), false);
assert.equal(
  existsSync(join(searchWorkspace.sessionDir, "skills", "sql-analysis-smoke", "SKILL.md")),
  false
);
assert.equal(
  existsSync(join(searchWorkspace.runDir, "skills", "sql-analysis-smoke", "SKILL.md")),
  false
);
const searchWorkspaceWithSkills = createRunWorkspace({
  runContext,
  skillPaths: ["skills"],
  workspaceRoot: searchWorkspaceRoot
});
await searchWorkspaceWithSkills.workspace.init();
const searchSkillTools = createSkillTools(searchWorkspaceWithSkills.workspace.skills);
const searchResult = await searchSkillTools.skill_search.execute({ query: "查数 指标", topK: 3 }, {
  context: { requestContext: new Map() },
  mastra: undefined,
  name: "skill_search",
  workspace: searchWorkspaceWithSkills.workspace
});
assert(String(searchResult).includes("sql-analysis-smoke"), "skill_search should find selected cached skills");
const unselectedSearchResult = await searchSkillTools.skill_search.execute({ query: "报告草稿", topK: 3 }, {
  context: { requestContext: new Map() },
  mastra: undefined,
  name: "skill_search",
  workspace: searchWorkspaceWithSkills.workspace
});
assert(
  String(unselectedSearchResult).includes("unselected-report-smoke"),
  "skill_search should search the full shared skill cache, not just selected skills"
);
await searchWorkspaceWithSkills.destroy();
await searchWorkspace.destroy();

const builtinPackageBody = Buffer.from(`---
name: builtin-data-analysis
description: Use for builtin data analysis smoke tests.
version: 1.0.0
tags:
  - data
  - analysis
  - sql
allowed-tools:
  - inspect_schema
  - run_sql_readonly
---
Explore schema, run read-only SQL, validate results, and present evidence-backed findings.
`, "utf8");
const builtinParsed = await parseSkillPackage({
  content: builtinPackageBody,
  filename: "SKILL.md",
  mimeType: "text/markdown"
});
const builtinPackageRef = fileAssetService.createRef({
  user_id: userId,
  workspace_id: workspaceId,
  filename: "SKILL.md",
  content: builtinPackageBody,
  declared_mime_type: "text/markdown",
  source: "skill-package",
  metadata: { builtin: true, kind: "skill-package" }
});
const builtinResource = metadataStore.configResources.upsert({
  id: "builtin-data-analysis",
  workspace_id: workspaceId,
  user_id: userId,
  kind: "skill",
  name: builtinParsed.name,
  description: builtinParsed.description,
  payload: {
    ...buildSkillResourcePayload({
      fields: { packageSource: "builtin://builtin-data-analysis" },
      packageFileRefId: builtinPackageRef.ref.id,
      parsed: builtinParsed
    }),
    builtinSource: "builtin://builtin-data-analysis"
  },
  builtin: true,
  default_enabled: false,
  status: "valid"
});
const builtinSelection = selectSkillsForRun({
  metadataStore,
  runConfig: {
    activeSkillId: "builtin-data-analysis",
    enabledSkillIds: ["builtin-data-analysis"],
    skillIds: [],
    skillMode: "selected",
    skillPolicy: {
      deniedToolNames: [],
      maxSkills: 5,
      requireUserInvocable: true,
      strictSkillTools: false
    },
    skillTags: []
  },
  userId,
  userInput: "用 SQL 分析数据",
  workspaceId
});
assert.equal(builtinSelection.selectedSkills[0]?.id, builtinResource.id);
const builtinMaterialized = await materializeSkillPackages({
  fileAssetService,
  runDir: join(root, "builtin-workspace"),
  skills: builtinSelection.selectedSkills,
  userId,
  workspaceId
});
assert.equal(builtinMaterialized[0]?.path, "skills/builtin-data-analysis");
const builtinSkillPath = join(root, "builtin-workspace", "skills", "builtin-data-analysis", "SKILL.md");
assert.equal(existsSync(builtinSkillPath), true);
assert.equal(readFileSync(builtinSkillPath, "utf8").includes("Explore schema, run read-only SQL"), true);

const boundary = createToolObservationBoundary({
  identity: {
    resourceId: userId,
    runId: "skill-smoke-run",
    sessionId: "skill-smoke-session"
  }
});
const dispatcher = new ToolObservationDispatcher(boundary.packager, {
  modelName: "skill-smoke-model",
  resourceId: userId,
  runId: "skill-smoke-run",
  sessionId: "skill-smoke-session"
});
const contextPackage = dispatcher.dispatch("skill", {
  name: "sql-analysis-smoke",
  content: "Inspect schema first, then run read-only SQL."
});
assert.equal(contextPackage.items.some((item) => item.sourceType === "skill-activation"), true);

metadataStore.close();
console.log(
  "Skills smoke OK: package parsing, FileAsset ref, user/builtin selection, materialization, context adapter"
);