#!/usr/bin/env node
"use strict";

const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");

const BASE_URL = (process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
const ACTOR_ID = "user_real";

function fileUrl(localPath) {
  return `file://${localPath}`;
}

function writeWorkspaceManifest(root) {
  fs.mkdirSync(path.join(root, ".mnote"), { recursive: true });
  fs.writeFileSync(
    path.join(root, ".mnote", "workspace.json"),
    `${JSON.stringify({
      workspaceId: "local-ws:user_real:task472",
      ownerId: ACTOR_ID,
      createdAt: new Date().toISOString(),
      capabilities: ["local_files", "tree_commands", "markdown_edit"],
    }, null, 2)}\n`,
    "utf8",
  );
}

async function requestJson(requestPath, init = {}) {
  const response = await fetch(`${BASE_URL}${requestPath}`, {
    ...init,
    headers: {
      ...(init.body ? { "content-type": "application/json" } : {}),
      "x-mnote-actor-id": ACTOR_ID,
      "x-mnote-actor-type": "user",
      ...(init.headers || {}),
    },
  });
  const text = await response.text();
  let payload = null;
  try {
    payload = text ? JSON.parse(text) : null;
  } catch {
    payload = text;
  }
  if (!response.ok) {
    throw new Error(`${requestPath} 请求失败: ${response.status} ${typeof payload === "string" ? payload : JSON.stringify(payload)}`);
  }
  return payload;
}

async function readFileTreeProjection(rootUri) {
  const url = new URL(`${BASE_URL}/api/tree/projections/file`);
  url.searchParams.set("sourceKind", "local_folder");
  url.searchParams.set("rootUri", rootUri);
  const payload = await requestJson(`${url.pathname}${url.search}`);
  const result = payload.result || payload;
  assert.equal(result.sourceKind, "local_folder", `projection 应来自 local_folder: ${JSON.stringify(result)}`);
  assert.equal(result.projection, "file_tree", `projection 类型应为 file_tree: ${JSON.stringify(result)}`);
  assert(Array.isArray(result.items), `projection items 应为数组: ${JSON.stringify(result)}`);
  return result;
}

function rootMarkdownOrder(projection) {
  return projection.items
    .filter((item) => item && item.parentNodeId == null && item.rowKind === "markdown")
    .map((item) => item.resourceMeta?.extra?.source?.relativePath || "");
}

function readPersistedOrder(root) {
  const orderPath = path.join(root, ".mnote", "file-order.json");
  assert(fs.existsSync(orderPath), "move sortOrder 后应写入 .mnote/file-order.json");
  return JSON.parse(fs.readFileSync(orderPath, "utf8"));
}

async function moveWithSortOrder(rootUri, documentId, sortOrder) {
  const payload = await requestJson("/api/tree/commands", {
    method: "POST",
    body: JSON.stringify({
      action: "move",
      sourceKind: "local_folder",
      rootUri,
      documentId,
      parentId: null,
      sortOrder,
    }),
  });
  assert.equal(payload.result?.execution?._unsupportedFields, undefined, `sortOrder 不应再落入 _unsupportedFields: ${JSON.stringify(payload)}`);
  return payload;
}

async function main() {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-task472-sort-order-"));
  const rootUri = fileUrl(root);

  try {
    writeWorkspaceManifest(root);
    fs.writeFileSync(path.join(root, "alpha.md"), "# Alpha\n", "utf8");
    fs.writeFileSync(path.join(root, "beta.md"), "# Beta\n", "utf8");
    fs.writeFileSync(path.join(root, "source.md"), "# Source\n", "utf8");

    const initialProjection = await readFileTreeProjection(rootUri);
    assert.deepEqual(
      rootMarkdownOrder(initialProjection).slice(0, 3),
      ["alpha.md", "beta.md", "source.md"],
      "初始 projection 应按文件系统自然顺序作为基线",
    );

    await moveWithSortOrder(rootUri, "local-md:source.md", 0);

    const immediateProjection = await readFileTreeProjection(rootUri);
    assert.deepEqual(
      rootMarkdownOrder(immediateProjection).slice(0, 3),
      ["source.md", "alpha.md", "beta.md"],
      "move sortOrder 后立即重新加载 projection 应保持新顺序",
    );

    const persistedOrder = readPersistedOrder(root);
    assert(JSON.stringify(persistedOrder).includes("source.md"), `file-order 应记录 source.md: ${JSON.stringify(persistedOrder)}`);

    const reloadedProjection = await readFileTreeProjection(rootUri);
    assert.deepEqual(
      rootMarkdownOrder(reloadedProjection).slice(0, 3),
      ["source.md", "alpha.md", "beta.md"],
      "再次重新加载 projection 顺序不应回退到自然排序",
    );

    console.log(JSON.stringify({
      ok: true,
      root,
      rootUri,
      order: rootMarkdownOrder(reloadedProjection).slice(0, 3),
      persistedOrder,
    }, null, 2));
  } finally {
    if (!process.env.MNOTE_KEEP_SMOKE_TMP) {
      fs.rmSync(root, { recursive: true, force: true });
    }
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
