import { getConfig, updateConfig } from '../config.mjs';
import { parseLLMJson } from '../json-utils.mjs';

export const name = 'configure-agent';
export const description = 'Update agent identity or runtime config from natural-language requests (name, provider, models, loop settings, workspace root)';

const SUPPORTED_CLIS = new Set(['claude', 'codex', 'copilot', 'gemini']);

function normalizeAgentName(input) {
  const clean = String(input || '').trim().replace(/["'`]/g, '').replace(/\s+/g, ' ');
  const safe = clean.replace(/[^a-zA-Z0-9 _-]/g, '').trim();
  if (!safe) return null;
  return safe.slice(0, 40);
}

function clampInt(value, min, max) {
  const num = Number.parseInt(String(value), 10);
  if (!Number.isFinite(num)) return null;
  return Math.max(min, Math.min(max, num));
}

function sanitizeModelName(input) {
  const model = String(input || '').trim();
  if (!model) return null;
  return model.slice(0, 80);
}

export async function execute(ctx) {
  const requestText = String(ctx.args?.task || ctx.args?.topic || ctx.args?.target || '').trim();
  if (!requestText) return 'No configuration request provided.';

  const current = getConfig();
  const currentView = {
    agentName: current.agentName,
    cli: current.cli,
    models: current.models,
    loopMinutes: current.loopMinutes,
    maxUserTaskConcurrency: current.maxUserTaskConcurrency,
    autonomyActiveChatPauseMs: current.autonomyActiveChatPauseMs,
    workspaceRoot: current.workspaceRoot
  };

  const extractPrompt = `You extract agent config updates from user text.

Current config:
${JSON.stringify(currentView, null, 2)}

User request:
"${requestText.slice(0, 1200)}"

Return ONLY one JSON object in this exact shape:
{
  "agentName": "string or null",
  "cli": "claude|codex|copilot|gemini|null",
  "models": { "fast": "string?", "balanced": "string?", "powerful": "string?" },
  "loopMinutes": "integer minutes or null",
  "maxUserTaskConcurrency": "integer or null",
  "autonomyActiveChatPauseMs": "integer milliseconds or null",
  "workspaceRoot": "string path or null"
}

Rules:
- Include only requested changes; use null for unchanged scalar fields.
- Do not infer changes that were not requested.
- If the request is not a config update, return all null fields and empty "models".`;

  const raw = await ctx.askLLM(extractPrompt, { model: ctx.pickModel('classify') });
  const parsed = parseLLMJson(raw);
  if (!parsed.ok || !parsed.data || typeof parsed.data !== 'object') {
    return 'Could not parse a valid config update from that request.';
  }

  const proposal = parsed.data;
  const updates = {};
  const applied = [];

  if (typeof proposal.agentName === 'string') {
    const nextName = normalizeAgentName(proposal.agentName);
    if (nextName && nextName !== current.agentName) {
      updates.agentName = nextName;
      applied.push(`name="${nextName}"`);
    }
  }

  if (typeof proposal.cli === 'string') {
    const nextCli = proposal.cli.trim().toLowerCase();
    if (SUPPORTED_CLIS.has(nextCli) && nextCli !== current.cli) {
      updates.cli = nextCli;
      applied.push(`cli=${nextCli}`);
    }
  }

  if (proposal.models && typeof proposal.models === 'object') {
    const modelUpdates = {};
    for (const tier of ['fast', 'balanced', 'powerful']) {
      if (typeof proposal.models[tier] !== 'string') continue;
      const modelName = sanitizeModelName(proposal.models[tier]);
      if (!modelName) continue;
      if (modelName !== current.models?.[tier]) {
        modelUpdates[tier] = modelName;
      }
    }
    if (Object.keys(modelUpdates).length) {
      updates.models = modelUpdates;
      applied.push(`models(${Object.keys(modelUpdates).join(',')})`);
    }
  }

  if (proposal.loopMinutes !== null && proposal.loopMinutes !== undefined) {
    const loopMinutes = clampInt(proposal.loopMinutes, 1, 120);
    if (loopMinutes && loopMinutes !== current.loopMinutes) {
      updates.loopMinutes = loopMinutes;
      applied.push(`loopMinutes=${loopMinutes}`);
    }
  }

  if (proposal.maxUserTaskConcurrency !== null && proposal.maxUserTaskConcurrency !== undefined) {
    const concurrency = clampInt(proposal.maxUserTaskConcurrency, 1, 8);
    if (concurrency && concurrency !== current.maxUserTaskConcurrency) {
      updates.maxUserTaskConcurrency = concurrency;
      applied.push(`maxUserTaskConcurrency=${concurrency}`);
    }
  }

  if (proposal.autonomyActiveChatPauseMs !== null && proposal.autonomyActiveChatPauseMs !== undefined) {
    const pauseMs = clampInt(proposal.autonomyActiveChatPauseMs, 10000, 1800000);
    if (pauseMs && pauseMs !== current.autonomyActiveChatPauseMs) {
      updates.autonomyActiveChatPauseMs = pauseMs;
      applied.push(`autonomyActiveChatPauseMs=${pauseMs}`);
    }
  }

  if (typeof proposal.workspaceRoot === 'string') {
    const workspaceRoot = proposal.workspaceRoot.trim();
    if (workspaceRoot && workspaceRoot !== current.workspaceRoot) {
      updates.workspaceRoot = workspaceRoot;
      applied.push('workspaceRoot=updated');
    }
  }

  if (!Object.keys(updates).length) {
    return 'No valid config changes detected in that message.';
  }

  const updated = updateConfig(updates);
  if (updates.agentName) {
    ctx.remember('agent-name', updates.agentName, 'preference');
  }

  ctx.addEvent('memory', 'agent', `Configuration updated via ${name}: ${applied.join(', ')}`, {
    skill: name,
    updates: Object.keys(updates)
  });

  return `Updated config: ${applied.join(', ')}. Active name: ${updated.agentName}.`;
}

export default { name, description, execute };
