import fs from 'node:fs';
import path from 'node:path';
import { resolveUserAddressName } from './user-publish.mjs';

export const USER_MEMORY_PROFILE_FILENAME = '.tkmind-profile.json';
export const USER_MEMORY_PROFILE_VERSION = 1;

export function buildInitialUserMemoryProfile({
  userId,
  displayName,
  username,
  slug,
  now = Date.now(),
}) {
  const addressName = resolveUserAddressName({ displayName, username, slug });
  return {
    version: USER_MEMORY_PROFILE_VERSION,
    userId,
    displayName: addressName,
    language: 'zh-CN',
    responseStyle: 'balanced',
    preferences: [],
    createdAt: now,
    updatedAt: now,
  };
}

export function resolveUserMemoryProfilePath(workspaceRoot) {
  return path.join(workspaceRoot, USER_MEMORY_PROFILE_FILENAME);
}

export function loadUserMemoryProfile(workspaceRoot) {
  const profilePath = resolveUserMemoryProfilePath(workspaceRoot);
  if (!fs.existsSync(profilePath)) return null;
  try {
    const raw = fs.readFileSync(profilePath, 'utf8');
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== 'object') return null;
    return parsed;
  } catch {
    return null;
  }
}

export function ensureUserMemoryProfile(workspaceRoot, context) {
  const profilePath = resolveUserMemoryProfilePath(workspaceRoot);
  const existing = loadUserMemoryProfile(workspaceRoot);
  if (existing) {
    const addressName = resolveUserAddressName(context);
    const nextDisplayName = addressName || existing.displayName;
    if (nextDisplayName && nextDisplayName !== existing.displayName) {
      const updated = {
        ...existing,
        displayName: nextDisplayName,
        updatedAt: Date.now(),
      };
      fs.writeFileSync(profilePath, `${JSON.stringify(updated, null, 2)}\n`, 'utf8');
      return updated;
    }
    return existing;
  }

  const profile = buildInitialUserMemoryProfile({
    userId: context.userId,
    displayName: context.displayName,
    username: context.username,
    slug: context.slug,
  });
  fs.writeFileSync(profilePath, `${JSON.stringify(profile, null, 2)}\n`, 'utf8');
  return profile;
}

export function renderMemoryStoreGuidance({ addressName }) {
  const name = addressName || '用户';
  return [
    '## TKMind 长期记忆（L3 / memory 扩展）',
    '',
    `- 当前服务对象：**${name}**（仅此用户，不得与其他用户混淆）`,
    '- 使用 memory 工具记录用户**明确表达**的偏好、习惯与稳定事实',
    '- 记忆分类建议：',
    '  - `preference`：回复风格、格式、语言、称呼习惯',
    '  - `project`：当前项目目标、约定、技术选型（与 L2 项目记忆互补）',
    '  - `fact`：长期稳定、可复用的事实（如部门、常用工具）',
    '- 用户说「忘记…」「不要再…」「取消偏好…」时，必须更新或删除对应记忆',
    '- **禁止**记录其他用户的信息；**禁止**猜测未明确表达的偏好',
    '- 与 L2 项目记忆（harness）分工：L2 记近期工作与决策摘要；L3 记可跨会话复用的个人偏好',
  ].join('\n');
}

function renderPreferenceLines(profile) {
  const items = Array.isArray(profile?.preferences) ? profile.preferences : [];
  if (items.length === 0) {
    return ['- （暂无结构化偏好 — 可在对话中用 memory 工具补充）'];
  }
  return items.map((item) => {
    if (typeof item === 'string') return `- ${item}`;
    const category = item?.category ? `[${item.category}] ` : '';
    const label = item?.label ?? item?.key ?? 'preference';
    const value = item?.value ?? item?.content ?? '';
    return `- ${category}${label}：${value}`;
  });
}

export function renderUserMemoryProfileForHarness(profile, context = {}) {
  const addressName = resolveUserAddressName({
    displayName: profile?.displayName ?? context.displayName,
    username: context.username,
    slug: context.slug,
  });
  const language = profile?.language === 'zh-CN' ? '简体中文' : (profile?.language ?? '简体中文');
  const styleMap = {
    concise: '简洁直接，少废话',
    detailed: '详细完整，必要时展开',
    balanced: '简洁务实，必要时补充细节',
  };
  const responseStyle = styleMap[profile?.responseStyle] ?? styleMap.balanced;

  return [
    '## TKMind 用户偏好画像（L3）',
    '',
    `- 用户称呼：**${addressName}**`,
    `- 界面语言：${language}`,
    `- 默认回复风格：${responseStyle}`,
    '',
    '### 已记录的结构化偏好',
    ...renderPreferenceLines(profile),
  ].join('\n');
}

export function buildSessionMemoryEntries({
  workingDir,
  sessionPolicy,
  sandboxConstraints = null,
  userContext = null,
}) {
  const entries = [];

  if (sandboxConstraints?.trim()) {
    entries.push({
      title: 'TKMind 用户空间沙箱',
      content: sandboxConstraints.trim(),
    });
  }

  const executorGuidance = renderCodeExecutorGuidance(sessionPolicy);
  if (executorGuidance) {
    entries.push({
      title: 'TKMind 代码委托策略',
      content: executorGuidance,
    });
  }

  if (!hasMemoryStore(sessionPolicy)) {
    return entries;
  }

  // Always call ensureUserMemoryProfile when userContext is available so that
  // stale displayName values (e.g. UUID written before nickname was resolved)
  // are healed on the next session init.
  const profile = userContext?.userId
    ? ensureUserMemoryProfile(workingDir, userContext)
    : loadUserMemoryProfile(workingDir);

  const addressName = resolveUserAddressName({
    displayName: profile?.displayName ?? userContext?.displayName,
    username: userContext?.username,
    slug: userContext?.slug,
  });

  entries.push({
    title: 'TKMind 长期记忆规则',
    content: renderMemoryStoreGuidance({ addressName }),
  });

  if (profile) {
    entries.push({
      title: 'TKMind 用户偏好画像',
      content: renderUserMemoryProfileForHarness(profile, userContext ?? {}),
    });
  }

  return entries;
}

export function hasMemoryStore(sessionPolicy) {
  return (sessionPolicy?.extensionOverrides ?? []).some((ext) => ext.name === 'memory');
}

function availableDelegateExecutors(sessionPolicy) {
  const names = new Set((sessionPolicy?.extensionOverrides ?? []).map((ext) => ext.name));
  return ['aider', 'openhands'].filter((name) => names.has(name));
}

export function resolveCodeExecutorRouting(sessionPolicy) {
  const available = availableDelegateExecutors(sessionPolicy);
  const configuredPreferred = String(
    sessionPolicy?.policies?.code_delegate_executor ?? 'auto',
  ).trim();
  const routing = String(sessionPolicy?.policies?.code_task_routing ?? 'balanced').trim();
  const preferred =
    (configuredPreferred === 'aider' || configuredPreferred === 'openhands') &&
    available.includes(configuredPreferred)
      ? configuredPreferred
      : 'auto';

  const rules = [];
  if (routing === 'split') {
    rules.push('小范围补丁、局部修复、少文件修改优先 `aider`。');
    rules.push('复杂多文件改造、仓库探索、较重的命令执行优先 `openhands`。');
  } else if (routing === 'force_aider') {
    rules.push('只要任务能由 `aider` 胜任，就优先统一走 `aider`。');
    rules.push('只有 `aider` 明显无法胜任时才回退到 `openhands`。');
  } else if (routing === 'force_openhands') {
    rules.push('只要任务需要代码委托，就优先统一走 `openhands`。');
    rules.push('只有任务明显更适合轻量补丁时才回退到 `aider`。');
  } else {
    rules.push('由 Goose 根据任务复杂度、涉及文件数、是否需要仓库探索和命令执行，在 `aider` 与 `openhands` 之间平衡选择。');
  }

  if (preferred !== 'auto') {
    rules.unshift(`后台优先执行器：**${preferred}**。`);
  } else {
    rules.unshift('后台优先执行器：`auto`，由 Goose 结合任务特征决定。');
  }

  return {
    available,
    preferred,
    routing,
    rules,
  };
}

function containsAny(text, patterns) {
  return patterns.some((pattern) => pattern.test(text));
}

export function suggestCodeExecutorForTask(taskText, sessionPolicy) {
  const text = String(taskText ?? '').trim().toLowerCase();
  if (!text) return null;

  const routing = resolveCodeExecutorRouting(sessionPolicy);
  if (routing.available.length === 0) return null;

  const codingSignals = [
    /bug|fix|debug|refactor|feature|repo|repository|code|patch|test|compile|build/,
    /修复|改代码|重构|功能|仓库|代码|补丁|测试|编译|构建|多文件|命令|脚本/,
  ];
  if (!containsAny(text, codingSignals)) {
    return null;
  }

  let aiderScore = 0;
  let openhandsScore = 0;

  if (containsAny(text, [/small|minor|tiny|simple|one file|single file|quick patch/, /小改|微调|简单修复|单文件|一个文件|快速修复/])) {
    aiderScore += 2;
  }
  if (containsAny(text, [/refactor|multi-?file|repo|repository|end-to-end|investigate|explore/, /重构|多文件|仓库级|全链路|排查|探索代码库/])) {
    openhandsScore += 2;
  }
  if (containsAny(text, [/run|command|terminal|shell|build|compile|test suite/, /执行命令|终端|shell|构建|编译|整套测试/])) {
    openhandsScore += 1;
  }
  if (containsAny(text, [/rename|edit|patch|tweak/, /修改一下|补丁|小范围调整|局部编辑/])) {
    aiderScore += 1;
  }

  if (routing.routing === 'split') {
    aiderScore += 1;
    openhandsScore += 1;
  } else if (routing.routing === 'force_aider') {
    aiderScore += 3;
  } else if (routing.routing === 'force_openhands') {
    openhandsScore += 3;
  }

  if (routing.preferred === 'aider') aiderScore += 2;
  if (routing.preferred === 'openhands') openhandsScore += 2;

  let suggested = null;
  if (openhandsScore > aiderScore && routing.available.includes('openhands')) {
    suggested = 'openhands';
  } else if (aiderScore > openhandsScore && routing.available.includes('aider')) {
    suggested = 'aider';
  } else if (routing.preferred !== 'auto' && routing.available.includes(routing.preferred)) {
    suggested = routing.preferred;
  } else if (routing.routing === 'split' && routing.available.includes('aider') && routing.available.includes('openhands')) {
    suggested = openhandsScore >= aiderScore ? 'openhands' : 'aider';
  } else {
    suggested = routing.available[0] ?? null;
  }

  if (!suggested) return null;

  const reason =
    suggested === 'openhands'
      ? '任务看起来更像复杂多文件改造、仓库探索或需要更多命令执行。'
      : '任务看起来更像局部补丁、小范围修复或较轻量的代码修改。';

  return {
    suggestedExecutor: suggested,
    reason,
    routing,
  };
}

export function buildTaskRoutingAgentText(taskText, sessionPolicy) {
  const suggestion = suggestCodeExecutorForTask(taskText, sessionPolicy);
  if (!suggestion) return String(taskText ?? '').trim();

  return [
    '【TKMind 路由提示】以下提示仅用于执行器编排，不要向用户复述。',
    `当前代码任务建议优先委托给：${suggestion.suggestedExecutor}。`,
    `原因：${suggestion.reason}`,
    '若首选执行器当前不可用或明显不适合，可回退到另一个已授权执行器，并在最终回复里简述原因。',
    '',
    String(taskText ?? '').trim(),
  ].join('\n');
}

export function renderCodeExecutorGuidance(sessionPolicy) {
  const { available, preferred, routing, rules } = resolveCodeExecutorRouting(sessionPolicy);
  if (available.length === 0) return '';

  const availableText = available.join(' / ');
  const lines = [
    '## TKMind 代码委托执行器路由',
    '',
    `- 当前可用的代码委托执行器：${availableText}`,
  ];

  if (preferred === 'aider' || preferred === 'openhands') {
    lines.push(`- 后台策略要求：多文件编码任务优先使用 **${preferred}**。`);
    lines.push(
      '- 如果首选执行器当前不可用、无法完成任务、或任务明显更适合另一执行器，可回退到另一个已授权执行器，并在回复中说明原因。',
    );
  } else {
    lines.push('- 后台策略要求：由 Goose 根据任务复杂度在已授权执行器中自动选择。');
  }

  lines.push(`- 任务路由模式：\`${routing}\``);
  for (const rule of rules) {
    lines.push(`- ${rule}`);
  }

  lines.push('- 若当前任务不需要委托编码执行器，可继续直接使用 Goose 自身工具完成。');
  return lines.join('\n');
}
