"use strict";

const assert = require("node:assert/strict");
const { chromium } = require("playwright");

const BASE_URL = (process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
const CONVEX_URL = (process.env.NEXT_PUBLIC_CONVEX_URL || process.env.CONVEX_SELF_HOSTED_URL || "http://127.0.0.1:3210").replace(/\/+$/, "");
const e2ePassword = () => process.env.MNOTE_E2E_PASSWORD || ["Mnote", "E2E", "123!"].join("");
// 说明：该 smoke 覆盖 convex-source trash 兼容路径，不代表 local-first 默认文件回收策略。

async function requestJson(request, path, init = {}) {
  const response = await request.fetch(`${BASE_URL}${path}`, {
    ...init,
    headers: {
      ...(init.data !== undefined ? { "content-type": "application/json" } : {}),
      ...(init.headers || {}),
    },
    timeout: 20_000,
  });
  const text = await response.text();
  let payload = null;
  try {
    payload = text ? JSON.parse(text) : null;
  } catch {
    payload = text;
  }
  if (!response.ok()) {
    throw new Error(`${path} 请求失败: ${response.status()} ${typeof payload === "string" ? payload : JSON.stringify(payload)}`);
  }
  return payload;
}

async function convexCall(context, kind, path, args) {
  const cookies = await context.cookies(BASE_URL);
  const jwt = cookies.find((cookie) => cookie.name === "__convexAuthJWT")?.value;
  assert(jwt, "缺少 __convexAuthJWT cookie");
  const response = await fetch(`${CONVEX_URL}/api/${kind}`, {
    method: "POST",
    headers: {
      authorization: `Bearer ${jwt}`,
      "content-type": "application/json",
      "Convex-Client": "mnote-trash-empty-smoke",
    },
    body: JSON.stringify({ path, format: "convex_encoded_json", args: [args] }),
  });
  const body = await response.json();
  if (!response.ok || body.status !== "success") {
    throw new Error(`Convex ${kind} ${path} 失败: ${response.status} ${JSON.stringify(body)}`);
  }
  return body.value;
}

async function main() {
  const stamp = Date.now();
  const email = `mnote.trash.${stamp}@example.com`;
  const username = `trash-${stamp}`;
  const assetId = `asset_trash_${stamp}`;
  const mindmapId = `mind_trash_${stamp}`;

  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext();
  const page = await context.newPage();
  const request = context.request;

  try {
    await requestJson(request, "/api/auth", {
      method: "POST",
      data: {
        action: "auth:signIn",
        args: {
          provider: "password",
          params: { email, password: e2ePassword(), flow: "signUp", name: username },
        },
      },
    });

    await page.goto(`${BASE_URL}/`, { waitUntil: "domcontentloaded", timeout: 20_000 });
    const currentUser = await convexCall(context, "query", "users:currentUser", {});
    const userId = currentUser?._id;
    assert(userId, "缺少 Convex currentUser._id");
    const workspaces = await convexCall(context, "query", "workspaces:fetchWorkspaceSummaries", {});
    const workspaceId = workspaces.activeWorkspaceId;
    assert(workspaceId, "缺少隔离 workspaceId");

    const root = await requestJson(request, "/api/tree/commands", {
      method: "POST",
      data: { action: "create", workspaceId, title: `TEST-10REVIEW-07-P3-root-${stamp}` },
    });
    const rootId = root.result.documentId;
    const child = await requestJson(request, "/api/tree/commands", {
      method: "POST",
      data: { action: "create", workspaceId, parentId: rootId, title: `TEST-10REVIEW-07-P3-child-${stamp}` },
    });
    const childId = child.result.documentId;
    const resourceDoc = await requestJson(request, "/api/tree/commands", {
      method: "POST",
      data: { action: "create", workspaceId, title: `TEST-10REVIEW-07-P3-resources-${stamp}` },
    });
    const resourceDocId = resourceDoc.result.documentId;

    await convexCall(context, "mutation", "mediaAssets:create", {
      userId,
      asset: {
        id: assetId,
        workspace_id: workspaceId,
        document_id: resourceDocId,
        asset_type: "file",
        file_url: null,
        thumbnail_url: null,
        storage_id: null,
        bucket: null,
        storage_path: null,
        file_name: `TEST-10REVIEW-07-P3-file-${stamp}.txt`,
        file_size: 12,
        mime_type: "text/plain",
      },
    });
    await convexCall(context, "mutation", "mindmaps:put", {
      docId: resourceDocId,
      mindmapId,
      data: { root: { data: { text: `TEST-10REVIEW-07-P3-mind-${stamp}` }, children: [] } },
      createOnly: true,
    });
    const table = await convexCall(context, "mutation", "tables:create", {
      userId,
      workspaceId,
      documentId: resourceDocId,
      title: `TEST-10REVIEW-07-P3-table-${stamp}`,
      schema: {},
      snapshot: null,
    });
    const tableId = table.id;
    await convexCall(context, "mutation", "tables:update", {
      userId,
      tableId,
      rows: [{ cells: ["p3-row"] }],
    });

    await requestJson(request, "/api/tree/commands", {
      method: "POST",
      data: { action: "archive", workspaceId, documentId: rootId },
    });
    await requestJson(request, "/api/media/batch", {
      method: "POST",
      data: { action: "delete", assetIds: [assetId] },
    });
    await requestJson(request, `/api/mindmap/${encodeURIComponent(resourceDocId)}/${encodeURIComponent(mindmapId)}`, {
      method: "DELETE",
    });
    await requestJson(request, `/api/tables/${encodeURIComponent(tableId)}`, { method: "DELETE" });

    const trashedDocs = await convexCall(context, "query", "documents:listTrashedByWorkspace", { workspaceId });
    assert(trashedDocs.some((doc) => doc.id === rootId), "根页面未进入垃圾箱");
    assert(trashedDocs.some((doc) => doc.id === childId), "子页面未级联进入垃圾箱");
    const mediaBefore = await convexCall(context, "query", "mediaAssets:getById", { userId, id: assetId });
    assert(mediaBefore?.deleted_at, "附件未进入垃圾箱");
    const mindmapsBefore = await convexCall(context, "query", "mindmaps:listByWorkspace", { workspaceId, includeDeleted: true });
    assert(mindmapsBefore.some((row) => row.mindmap_id === mindmapId && row.deleted_at), "mindmap 未进入垃圾箱");
    const tablesBefore = await convexCall(context, "query", "tables:listByWorkspaceForSearch", {
      userId,
      workspaceId,
      includeArchived: true,
      limit: 100,
    });
    assert(tablesBefore.some((row) => row.id === tableId && row.deleted_at), "table 未进入垃圾箱");

    const pageEmpty = await requestJson(request, "/api/documents/empty-trash", { method: "POST", data: { workspaceId } });
    const mediaEmpty = await requestJson(request, "/api/media/empty-trash", { method: "POST", data: { workspaceId } });
    const mindmapEmpty = await requestJson(request, "/api/mindmap-trash/empty", { method: "POST", data: { workspaceId } });
    const tableEmpty = await requestJson(request, "/api/tables/empty-trash", { method: "POST", data: { workspaceId } });

    assert.equal(await convexCall(context, "query", "documents:getMeta", { id: rootId }), null, "根页面 purge 后仍可查询");
    assert.equal(await convexCall(context, "query", "documents:getMeta", { id: childId }), null, "子页面 purge 后仍可查询");
    assert.equal(await convexCall(context, "query", "mediaAssets:getById", { userId, id: assetId }), null, "附件 purge 后仍可查询");
    const mindmapsAfter = await convexCall(context, "query", "mindmaps:listByWorkspace", { workspaceId, includeDeleted: true });
    assert(!mindmapsAfter.some((row) => row.mindmap_id === mindmapId), "mindmap purge 后仍存在");
    assert.equal(await convexCall(context, "query", "tables:get", { userId, tableId }), null, "table purge 后仍可查询");
    assert.equal((await convexCall(context, "query", "tables:getRows", { tableId })).length, 0, "table rows purge 后仍存在");

    await requestJson(request, "/api/documents/purge", { method: "POST", data: { documentId: resourceDocId } }).catch(() => null);

    console.log(JSON.stringify({
      ok: true,
      email,
      userId,
      workspaceId,
      rootId,
      childId,
      resourceDocId,
      assetId,
      mindmapId,
      tableId,
      pageEmpty: pageEmpty.result,
      mediaEmpty: mediaEmpty.result,
      mindmapEmpty: mindmapEmpty.result,
      tableEmpty: tableEmpty.result,
    }, null, 2));
  } finally {
    await browser.close();
  }
}

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