import fs from 'node:fs';
import path from 'node:path';
import { SYSTEM_CATEGORIES } from './mindspace.mjs';
import { PUBLISH_ROOT_DIR, resolvePublishDir, resolveUserAddressName } from './user-publish.mjs';

/** 用户 MindSpace 下的分区子目录（与上传分类一致） */
export const UPLOAD_ZONE_CODES = ['oa', 'private', 'public'];

export function resolveMindspaceStorageRoot(h5Root, env = process.env) {
  return path.resolve(env.MINDSPACE_STORAGE_ROOT ?? path.join(h5Root, 'data', 'mindspace'));
}

/** Agent 工作区 = MindSpace/<userId>/ */
export function resolveUserWorkspaceRoot(h5Root, user) {
  return resolvePublishDir(h5Root, user);
}

export const WORKSPACE_HINTS_FILENAME = '.tkmindhints';
export const LEGACY_WORKSPACE_HINTS_FILENAME = '.goosehints';

function renderUserSpaceBrandingBlock(userAddressName) {
  const name = userAddressName || '用户';
  return `## 品牌与称呼（硬性）

- 你是 **TKMind** 助手；介绍产品时用 TKMind，不要称 goose、Goose、goosed
- 与用户对话时，用 **${name}** 称呼用户（可辅以「你/您」），**禁止**把用户叫作 TKMind
- 问候示例：「${name}，下午好」——不要用「TKMind，下午好」
- 不要描述本工作区为「Rust goose 项目」或「goose AI 框架」
- 本工作区是 TKMind **MindSpace 用户空间**，用于 OA/私人/公开文件管理与静态页面生成
`;
}

export function resolveZoneDir(workspaceRoot, categoryCode) {
  return path.join(workspaceRoot, categoryCode);
}

export function resolveZoneFilePath(workspaceRoot, categoryCode, filename) {
  return path.join(resolveZoneDir(workspaceRoot, categoryCode), filename);
}

export function zoneLabel(categoryCode) {
  return SYSTEM_CATEGORIES.find((item) => item.code === categoryCode)?.name ?? categoryCode;
}

export function ensureUserZoneDirs(workspaceRoot) {
  for (const code of UPLOAD_ZONE_CODES) {
    fs.mkdirSync(resolveZoneDir(workspaceRoot, code), { recursive: true });
  }
}

/** 上传完成后镜像到 MindSpace/<user>/<category>/文件名 */
export function mirrorAssetToZone({ workspaceRoot, categoryCode, filename, sourcePath }) {
  if (!UPLOAD_ZONE_CODES.includes(categoryCode)) return null;
  if (!sourcePath || !fs.existsSync(sourcePath)) return null;
  ensureUserZoneDirs(workspaceRoot);
  const dest = resolveZoneFilePath(workspaceRoot, categoryCode, filename);
  fs.mkdirSync(path.dirname(dest), { recursive: true });
  fs.copyFileSync(sourcePath, dest);
  return dest;
}

export function removeZoneMirror({ workspaceRoot, categoryCode, filename }) {
  if (!UPLOAD_ZONE_CODES.includes(categoryCode)) return;
  const target = resolveZoneFilePath(workspaceRoot, categoryCode, filename);
  if (fs.existsSync(target)) fs.unlinkSync(target);
}

export function isPathInsideUserWorkspace(workspaceRoot, requestedPath) {
  const base = path.resolve(workspaceRoot);
  const resolved = path.resolve(requestedPath);
  return resolved === base || resolved.startsWith(`${base}${path.sep}`);
}

export function renderUserSpaceHints({ username, workspaceRoot, displayName, slug }) {
  const addressName = resolveUserAddressName({ displayName, username, slug });
  const zoneLines = UPLOAD_ZONE_CODES.map(
    (code) => `- \`${code}/\` — ${zoneLabel(code)}（用户上传落在此分区）`,
  );
  return `# TKMind 用户空间分区

你是用户 **${addressName}** 的专属 TKMind 助手。当前会话工作区（唯一文件根目录）：
\`${workspaceRoot}\`

${renderUserSpaceBrandingBlock(addressName)}
## 分区规则（硬性）

用户上传的文件**只会**出现在下列子目录之一：

${zoneLines.join('\n')}

## 查找 / 读取文件

1. 在工作区内搜索，例如 \`tree oa/\`、\`find oa -name '*.csv'\`、\`cat oa/文件名.csv\`
2. 用户说「OA 区」→ \`oa/\`；「私人区」→ \`private/\`；「公开区」→ \`public/\`
3. **禁止**访问其它用户目录、\`${PUBLISH_ROOT_DIR}/\` 根目录（非本用户）、\`data/mindspace/\`、主机绝对路径
4. **禁止**用公网 URL 列目录或读 CSV；公网链接仅用于分享已发布 HTML
5. 找不到时说明该分区内没有该文件，请用户确认上传到了哪个分区

## 生成页面

- 用户要网页 / HTML / 报告 / 分享链接时：**你必须亲自完成**，不要推给用户手动操作
- 先 \`load_skill\` → \`static-page-publish\`，再使用 \`write_file\` 创建 \`public/页面名.html\`（需要调整已有页面时用 \`edit_file\`）
- 写入 \`<head>\` 的 **mindspace-cover** 元数据（详见 \`.agents/skills/static-page-publish/SKILL.md\`）
- 保存后立即给出 Markdown 可点击公网链接 \`[标题](URL)\`；写入 \`public/页面.html\` 时 URL 为 \`.../MindSpace/<用户ID>/public/页面.html\`
- **禁止**回复「请手动保存到 public 目录」「我无法生成页面」——除非 \`write_file\` / \`edit_file\` 调用已失败并说明具体错误

## 工作区文件与 OA 界面

- 写入 \`oa/\`、\`private/\`、\`public/\` 根目录的支持类型文件（docx、csv、pdf、图片等）会**自动同步**到 MindSpace 资产库
- 打开对应分区或保存文件后会出现在界面中，可直接预览或下载
- 用户上传的文件仍以界面入库为准，并镜像到上述分区
`;
}

export function buildUserSpaceConstraints({ username, workspaceRoot, publicBaseUrl, slug, displayName }) {
  const addressName = resolveUserAddressName({ displayName, username, slug });
  const zoneList = UPLOAD_ZONE_CODES.map((code) => `\`${code}/\`（${zoneLabel(code)}）`).join('、');
  return [
    '## TKMind 用户空间分区（硬性约束）',
    '',
    `- 你是 **TKMind** 助手；与用户对话时用 **${addressName}** 称呼用户，禁止把用户叫作 TKMind`,
    '- 禁止称 goose / Goose / goosed 或「Rust goose 项目」',
    `- 用户 **${addressName}** 的 Agent 工作区：\`${workspaceRoot}\``,
    `- 用户上传落在分区子目录：${zoneList}（例如 OA 文件在 \`oa/\`）`,
    '- **查找文件**：只在上述工作区内搜索（如 `oa/2025-12-06T13-34_export.csv`）',
    '- **禁止**：访问其它用户目录、MindSpace 根目录、data/mindspace 内部路径、主机绝对路径',
    '- **禁止**用公网 URL 列目录或读 CSV；生成 HTML 写入 `public/` 并给出公网链接',
    '- **生成页面**：先 `load_skill` → `static-page-publish`，再用 `write_file` / `edit_file` 写入 `public/*.html`；**禁止**让用户手动保存文件',
    publicBaseUrl && slug
      ? `- 公网 HTML 前缀（公开区）：\`${publicBaseUrl}/${PUBLISH_ROOT_DIR}/${slug}/public/\`（写入 \`public/页面.html\` 时分享链接必须含 \`public/\`）`
      : null,
  ]
    .filter(Boolean)
    .join('\n');
}

export function ensureUserSpaceHints(workspaceRoot, context) {
  const hintsPath = path.join(workspaceRoot, WORKSPACE_HINTS_FILENAME);
  const legacyPath = path.join(workspaceRoot, LEGACY_WORKSPACE_HINTS_FILENAME);
  const content = renderUserSpaceHints({ ...context, workspaceRoot });
  fs.writeFileSync(hintsPath, content, 'utf8');
  if (fs.existsSync(legacyPath)) {
    fs.unlinkSync(legacyPath);
  }
  return hintsPath;
}

export async function syncUserZonesFromAssets(pool, storageRoot, userId, workspaceRoot) {
  ensureUserZoneDirs(workspaceRoot);
  const placeholders = UPLOAD_ZONE_CODES.map(() => '?').join(', ');
  const [rows] = await pool.query(
    `SELECT a.original_filename, c.category_code, v.storage_key
     FROM h5_assets a
     JOIN h5_space_categories c ON c.id = a.category_id AND c.user_id = a.user_id
     JOIN h5_asset_versions v ON v.id = a.current_version_id
     WHERE a.user_id = ? AND a.status <> 'deleted' AND c.category_code IN (${placeholders})`,
    [userId, ...UPLOAD_ZONE_CODES],
  );
  for (const row of rows) {
    const sourcePath = path.join(storageRoot, row.storage_key);
    mirrorAssetToZone({
      workspaceRoot,
      categoryCode: row.category_code,
      filename: row.original_filename,
      sourcePath,
    });
  }
  return { workspaceRoot, mirrored: rows.length };
}

export async function ensureUserSpaceLayout({
  pool,
  storageRoot,
  userId,
  username,
  displayName,
  publicBaseUrl,
  slug,
  workspaceRoot,
}) {
  ensureUserZoneDirs(workspaceRoot);
  if (pool && userId) {
    await syncUserZonesFromAssets(pool, storageRoot, userId, workspaceRoot);
  }
  const context = {
    username: username ?? slug,
    displayName,
    workspaceRoot,
    slug,
    publicBaseUrl,
  };
  ensureUserSpaceHints(workspaceRoot, context);
  return {
    workspaceRoot,
    zonesRoot: workspaceRoot,
    zonesPublicDir: resolveZoneDir(workspaceRoot, 'public'),
    constraints: buildUserSpaceConstraints(context),
    ...context,
  };
}
