import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { AppConfigService } from "../../apps/desktop/src/main/services/app-config.js";
import { ProjectSkillRouterService } from "../../apps/desktop/src/main/services/project-skill-router.js";
import { ProjectStoreService } from "../../apps/desktop/src/main/services/project-store.js";
import { RuntimeSkillBridgeService } from "../../apps/desktop/src/main/services/runtime-skill-bridge.js";
import { SkillStoreService } from "../../apps/desktop/src/main/services/skill-store.js";

function assert(condition: unknown, message: string): asserts condition {
  if (!condition) {
    throw new Error(message);
  }
}

async function writeSkill(workspaceRoot: string, skillId: string, body: string): Promise<void> {
  const skillPath = path.join(workspaceRoot, "skills", skillId, "SKILL.md");
  await mkdir(path.dirname(skillPath), { recursive: true });
  await writeFile(skillPath, body, "utf8");
}

function createRuntimeManagerStub(vendorRuntimeDir: string, runtimeDataDir: string) {
  return {
    async status() {
      return {
        runtimeDataDir
      };
    },
    resolveBundledPaths() {
      return {
        runtimeDir: vendorRuntimeDir,
        runtimeManifestPath: path.join(vendorRuntimeDir, "runtime-manifest.json"),
        runtimeDataDir
      };
    }
  };
}

async function resolveManagedSkillsRoot(runtimeManager: ReturnType<typeof createRuntimeManagerStub>): Promise<string> {
  const paths = runtimeManager.resolveBundledPaths();
  const manifestRaw = await readFile(paths.runtimeManifestPath, "utf8");
  const manifest = JSON.parse(manifestRaw.replace(/^\uFEFF/, "")) as {
    packagedSkillsRoot?: string;
    packagedOpenClawEntry?: string;
    sourceOpenClawEntry?: string;
  };

  if (typeof manifest.packagedSkillsRoot === "string" && manifest.packagedSkillsRoot.trim()) {
    return path.resolve(paths.runtimeDir, manifest.packagedSkillsRoot.trim());
  }
  if (typeof manifest.packagedOpenClawEntry === "string" && manifest.packagedOpenClawEntry.trim()) {
    return path.join(path.dirname(path.resolve(paths.runtimeDir, manifest.packagedOpenClawEntry.trim())), "skills");
  }
  if (typeof manifest.sourceOpenClawEntry === "string" && manifest.sourceOpenClawEntry.trim()) {
    return path.join(path.dirname(manifest.sourceOpenClawEntry.trim()), "skills");
  }
  return path.join(paths.runtimeDataDir, "skills");
}

async function main(): Promise<void> {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const repoRoot = process.cwd();
  const resultPath = path.resolve(process.argv[2] ?? path.join(repoRoot, ".tmp", "project-package-orchestrator-smoke", "result.json"));
  const tempRoot = path.dirname(resultPath);
  const userDataPath = path.join(tempRoot, "user-data");
  const vendorRuntimeDir = path.join(repoRoot, "vendor", "openclaw-runtime");
  const runtimeDataDir = path.join(userDataPath, "runtime");

  await rm(tempRoot, { recursive: true, force: true });
  await mkdir(userDataPath, { recursive: true });

  const configService = new AppConfigService(userDataPath);
  await configService.load();

  const projectStore = new ProjectStoreService(configService);
  await projectStore.initialize();
  const workspaceRoot = await projectStore.getWorkspaceRoot();

  await writeSkill(workspaceRoot, "xiaohongshu-pipeline", `---
name: xiaohongshu-pipeline
description: xiaohongshu end to end publish orchestrator
---
`);
  await writeSkill(workspaceRoot, "xiaohongshu-writer", `---
name: xiaohongshu-writer
description: xiaohongshu draft writer
---
`);
  await writeSkill(workspaceRoot, "xiaohongshu-publisher", `---
name: xiaohongshu-publisher
description: xiaohongshu publisher
---
`);

  const project = await projectStore.upsertProject({
    id: "xiaohongshu-workspace",
    name: "Xiaohongshu Workspace",
    description: "\u5c0f\u7ea2\u4e66\u7f8e\u5986\u53d1\u5e16\u9879\u76ee",
    projectType: "workspace-package",
    platform: "xiaohongshu",
    capabilities: ["publish", "write", "workflow"],
    requires: ["playwright"],
    boundSkillIds: ["xiaohongshu-pipeline", "xiaohongshu-writer", "xiaohongshu-publisher"],
    defaultEntry: {
      id: "xiaohongshu-pipeline",
      type: "skill",
      capabilities: ["publish", "workflow"],
      intentAliases: ["\u5c0f\u7ea2\u4e66", "\u7f8e\u5986", "\u53d1\u5e16"],
      requiresSkills: ["xiaohongshu-writer", "xiaohongshu-publisher"]
    },
    entries: [
      {
        id: "xiaohongshu-pipeline",
        type: "skill",
        capabilities: ["publish", "workflow"],
        intentAliases: ["\u5c0f\u7ea2\u4e66", "\u7f8e\u5986", "\u53d1\u5e16"],
        requiresSkills: ["xiaohongshu-writer", "xiaohongshu-publisher"]
      },
      {
        id: "xiaohongshu-writer",
        type: "skill",
        capabilities: ["write", "draft"],
        intentAliases: ["\u6587\u6848", "\u7b14\u8bb0", "\u8349\u7a3f"]
      },
      {
        id: "xiaohongshu-publisher",
        type: "skill",
        capabilities: ["publish"],
        intentAliases: ["\u53d1\u5e03", "\u53d1\u5e16"]
      }
    ],
    ready: true
  });

  assert(project.platform === "xiaohongshu", "Project summary did not surface platform metadata.");
  assert(project.defaultEntryId === "xiaohongshu-pipeline", "Project summary did not surface default entry metadata.");

  const projectConfig = await projectStore.getProjectPackageConfig(project.id);
  assert(projectConfig?.defaultEntry?.id === "xiaohongshu-pipeline", "Project package config did not retain default entry.");

  const skillRouter = new ProjectSkillRouterService(projectStore);
  const publishPrompt = "\u53d1\u4e00\u4e2a\u7f8e\u5986\u7c7b\u7684\u5c0f\u7ea2\u4e66\u5e16\u5b50";
  const route = await skillRouter.resolve(project.id, publishPrompt);
  assert(route?.skillId === "xiaohongshu-pipeline", "Project skill router did not choose the declared orchestrator skill.");

  const skillStore = new SkillStoreService(userDataPath);
  const runtimeManager = createRuntimeManagerStub(vendorRuntimeDir, runtimeDataDir);
  const bridge = new RuntimeSkillBridgeService(
    skillStore,
    projectStore,
    runtimeManager as unknown as ConstructorParameters<typeof RuntimeSkillBridgeService>[2]
  );
  const prepared = await bridge.preparePrompt(publishPrompt, "xiaohongshu-pipeline", project.id);
  const dependencyIds = await projectStore.getProjectSkillDependencyIds(project.id, "xiaohongshu-pipeline");
  const managedSkillsRoot = await resolveManagedSkillsRoot(runtimeManager);
  const managedEntries = (await readdir(managedSkillsRoot, { withFileTypes: true }).catch(() => []))
    .filter((entry) => entry.isDirectory() && entry.name.startsWith("qjclaw-cloud-"))
    .map((entry) => entry.name)
    .sort((left, right) => left.localeCompare(right, "en"));

  assert(managedEntries.length === 3, `Expected 3 managed runtime skill directories, received ${managedEntries.length}.`);

  const materializedSkills = await Promise.all(managedEntries.map(async (entryName) => {
    const skillBody = await readFile(path.join(managedSkillsRoot, entryName, "SKILL.md"), "utf8");
    const metaRaw = await readFile(path.join(managedSkillsRoot, entryName, ".qjclaw-skill.json"), "utf8");
    const metadata = JSON.parse(metaRaw) as { runtimeSkillName?: string; selected?: boolean; selectedSkillName?: string };
    return {
      entryName,
      skillBody,
      metadata
    };
  }));

  assert(materializedSkills.some((item) => item.metadata.selected === true && item.metadata.runtimeSkillName === prepared.runtimeSkillName), "Main orchestrator skill was not materialized as the selected runtime skill.");
  assert(materializedSkills.some((item) => item.skillBody.includes("name: xiaohongshu-writer")), "Writer dependency skill was not materialized with its original skill name.");
  assert(materializedSkills.some((item) => item.skillBody.includes("name: xiaohongshu-publisher")), "Publisher dependency skill was not materialized with its original skill name.");

  const summary = {
    ok: true,
    workspaceRoot,
    projectId: project.id,
    platform: project.platform,
    defaultEntryId: project.defaultEntryId,
    routeSkillId: route?.skillId ?? null,
    routeReason: route?.reason ?? null,
    preparedRuntimeSkillName: prepared.runtimeSkillName,
    dependencyIds,
    managedSkillsRoot,
    managedEntries,
    prompt: prepared.prompt
  };

  await mkdir(path.dirname(resultPath), { recursive: true });
  await writeFile(resultPath, JSON.stringify(summary, null, 2), "utf8");
  console.log(JSON.stringify(summary, null, 2));
}

main().catch(async (error) => {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const repoRoot = process.cwd();
  const resultPath = path.resolve(process.argv[2] ?? path.join(repoRoot, ".tmp", "project-package-orchestrator-smoke", "result.json"));
  const failure = {
    ok: false,
    error: error instanceof Error ? error.stack ?? error.message : String(error)
  };
  await mkdir(path.dirname(resultPath), { recursive: true });
  await writeFile(resultPath, JSON.stringify(failure, null, 2), "utf8");
  console.error(failure.error);
  process.exitCode = 1;
});


