import { i as listCustomerSlugs } from "./customer-dir-CkMMXhb0.js";
import { i as writeJsonFile } from "./json-store-WWsFzXub.js";
import { n as getActor } from "./audit-log-DNMY9mUZ.js";
import { t as withJsonFile } from "./file-lock-CcHotQkZ.js";
import { l as runSimulation } from "./revenue-simulation-D8f_YkUY.js";
import { t as readPipeline } from "./pipeline-writer-CIllfnZl.js";
import { o as guardIsoDate, t as callLlm } from "./llm-PZzgPphl.js";
import path from "path";
import fs from "fs";
//#region src/core/goal-engine.ts
function goalsPath(dataDir) {
	return path.join(dataDir, ".agentic", "goals.json");
}
function readGoals(dataDir) {
	const p = goalsPath(dataDir);
	if (!fs.existsSync(p)) return [];
	try {
		const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
		if (Array.isArray(raw)) return raw;
		return raw.goals ?? [];
	} catch {
		return [];
	}
}
function writeGoals(dataDir, goals) {
	writeJsonFile(goalsPath(dataDir), {
		goals,
		updatedAt: (/* @__PURE__ */ new Date()).toISOString()
	});
}
function makeGoalId() {
	return `goal_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
}
function parseTargetFromDescription(desc) {
	const millionMatch = desc.match(/[\$€£]?\s*(\d+(?:\.\d+)?)\s*(?:M\b|million)/i);
	if (millionMatch) return Math.round(parseFloat(millionMatch[1]) * 1e6);
	const kMatch = desc.match(/[\$€£]?\s*(\d+(?:\.\d+)?)\s*k\b/i);
	if (kMatch) return Math.round(parseFloat(kMatch[1]) * 1e3);
	const rawMatch = desc.match(/[\$€£]\s*(\d{4,}(?:[,.\d]*\d)?)/);
	if (rawMatch) return parseInt(rawMatch[1].replace(/[,. ]/g, ""), 10);
	return 0;
}
function inferGoalType(desc) {
	const lower = desc.toLowerCase();
	if (/churn|retain|renewal|renew/.test(lower)) return "churn_prevention";
	if (/meeting|call|book|relationship|contact/.test(lower)) return "relationship";
	if (/pipeline|prospect|lead|qualify/.test(lower)) return "pipeline";
	return "revenue";
}
function inferMetric(type) {
	switch (type) {
		case "pipeline": return "pipeline_created";
		case "relationship": return "meetings_booked";
		case "revenue": return "revenue";
		case "churn_prevention": return "revenue";
	}
}
function rankDealsByLeverage(deals) {
	return deals.filter((d) => d.stage !== "won" && d.stage !== "lost").sort((a, b) => {
		const leverageA = a.value * (a.probability / 100) * (a.healthScore / 100);
		return b.value * (b.probability / 100) * (b.healthScore / 100) - leverageA;
	});
}
function generateNextStep(deal) {
	if (deal.healthScore < 40 && !deal.championPresent) return "Re-engage urgently and identify a champion";
	if (deal.healthScore < 60) return "Schedule an urgent check-in call";
	if (deal.daysSinceContact > 14) return "Reach out — contact is overdue";
	if (!deal.championPresent) return "Identify a champion or economic buyer";
	return "Push to next pipeline stage";
}
function decomposeGoalRuleBased(deals, target, currentP50, today, playbookLookup) {
	const gap = Math.max(0, target - currentP50);
	const decomposedAt = (/* @__PURE__ */ new Date(today + "T00:00:00Z")).toISOString();
	if (gap === 0) return {
		analysis: `Current pipeline (P50: €${currentP50.toLocaleString()}) already meets or exceeds the target of €${target.toLocaleString()}.`,
		currentPipeline: currentP50,
		gap: 0,
		subGoals: [],
		probabilisticOutcome: `Pipeline P50 (€${currentP50.toLocaleString()}) ≥ target (€${target.toLocaleString()}).`,
		decomposedAt
	};
	const ranked = rankDealsByLeverage(deals);
	if (ranked.length === 0) return {
		analysis: `No active deals found. Gap to close: €${gap.toLocaleString()}. Focus on building pipeline.`,
		currentPipeline: currentP50,
		gap,
		subGoals: [{
			priority: 1,
			action: "Build pipeline from scratch",
			slug: "_all",
			why: `No active deals. Need €${gap.toLocaleString()} to reach target.`,
			nextStep: "Use list_customers() to find prospects and log_interaction to initiate outreach",
			targetDelta: target
		}],
		probabilisticOutcome: `Insufficient pipeline. Need €${gap.toLocaleString()} in new deals.`,
		decomposedAt
	};
	const subGoals = [];
	let cumulative = 0;
	for (const deal of ranked.slice(0, 5)) {
		if (subGoals.length >= 5) break;
		const playbookName = playbookLookup?.(deal.slug, deal);
		const subGoal = {
			priority: subGoals.length + 1,
			action: `Accelerate ${deal.slug}/${deal.name}`,
			slug: deal.slug,
			...deal.name ? { dealName: deal.name } : {},
			why: `€${deal.value.toLocaleString()} deal in ${deal.stage} — health ${deal.healthScore}/100`,
			nextStep: generateNextStep(deal),
			targetDelta: deal.value,
			...playbookName ? { playbookName } : {}
		};
		subGoals.push(subGoal);
		cumulative += deal.value;
		if (cumulative >= gap) break;
	}
	const projectedTotal = currentP50 + cumulative;
	return {
		analysis: `Current pipeline P50: €${currentP50.toLocaleString()}. Gap to target: €${gap.toLocaleString()}. Top ${subGoals.length} deal(s) identified.`,
		currentPipeline: currentP50,
		gap,
		subGoals,
		probabilisticOutcome: `If all recommended deals close: ~€${projectedTotal.toLocaleString()} (target: €${target.toLocaleString()}).`,
		decomposedAt
	};
}
function buildDecompositionPrompt(description, target, deadline, currentP50, deals, today) {
	const gap = Math.max(0, target - currentP50);
	const dealLines = deals.filter((d) => d.stage !== "won" && d.stage !== "lost").slice(0, 8).map((d, i) => `${i + 1}. ${d.slug}/${d.name} — €${d.value.toLocaleString()}, stage: ${d.stage}, health: ${d.healthScore}/100, probability: ${d.probability}%${d.championPresent ? ", champion ✓" : ""}`).join("\n");
	return `You are a sales strategy AI helping decompose a revenue goal into actionable sub-goals.

Goal: ${description}
Target: €${target.toLocaleString()}
Deadline: ${deadline}
Current date: ${today}
Current weighted pipeline (P50): €${currentP50.toLocaleString()}
Gap to close: €${gap.toLocaleString()}

Active deals (sorted by weighted value):
${dealLines || "(no active deals)"}

Return JSON only (no markdown wrapper):
{
  "analysis": "<1-2 sentence summary of the situation>",
  "subGoals": [
    {
      "priority": 1,
      "action": "<what to do>",
      "slug": "<customer-slug>",
      "dealName": "<deal name>",
      "why": "<why this deal matters for the goal>",
      "nextStep": "<concrete next action with deadline>",
      "targetDelta": <expected revenue contribution in euros>
    }
  ],
  "probabilisticOutcome": "<P50 forecast summary after actions>"
}`;
}
function parseLlmDecomposition(response, fallback) {
	try {
		const match = response.match(/\{[\s\S]*\}/);
		if (!match) return fallback;
		const parsed = JSON.parse(match[0]);
		if (!parsed.analysis || !Array.isArray(parsed.subGoals)) return fallback;
		return {
			analysis: parsed.analysis,
			currentPipeline: fallback.currentPipeline,
			gap: fallback.gap,
			subGoals: parsed.subGoals.map((s, i) => ({
				priority: s.priority ?? i + 1,
				action: s.action ?? "",
				slug: s.slug ?? "_all",
				...s.dealName ? { dealName: s.dealName } : {},
				why: s.why ?? "",
				nextStep: s.nextStep ?? "",
				targetDelta: s.targetDelta ?? 0,
				...s.playbookName ? { playbookName: s.playbookName } : {}
			})),
			probabilisticOutcome: parsed.probabilisticOutcome ?? fallback.probabilisticOutcome,
			decomposedAt: fallback.decomposedAt
		};
	} catch {
		return fallback;
	}
}
async function pursueGoal(dataDir, input, options = {}) {
	guardIsoDate(input.deadline, "deadline");
	const today = options.today ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
	const actor = options.actor ?? getActor();
	const simInput = await (options.buildInputFn ?? (async (dir, horizon, t) => {
		const { buildSimulationInput } = await import("./revenue-simulation-njJZlTqm.js");
		return buildSimulationInput(dir, horizon, t);
	}))(dataDir, "quarter", today);
	const currentP50 = runSimulation(simInput).p50;
	const deals = simInput.deals;
	const target = parseTargetFromDescription(input.description);
	const type = inferGoalType(input.description);
	const metric = inferMetric(type);
	const ruleBasedDecomp = decomposeGoalRuleBased(deals, target, currentP50, today);
	let decomposition = ruleBasedDecomp;
	const llmFn = options.llmFn ?? callLlm;
	if (options.llmFn !== void 0) decomposition = parseLlmDecomposition(await llmFn(buildDecompositionPrompt(input.description, target, input.deadline, currentP50, deals, today)), ruleBasedDecomp);
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const goal = {
		id: makeGoalId(),
		description: input.description,
		type,
		target,
		metric,
		deadline: input.deadline,
		decomposition,
		progress: 0,
		status: "active",
		createdAt: now,
		updatedAt: now,
		actor
	};
	await withJsonFile(goalsPath(dataDir), (current) => {
		return {
			goals: [...Array.isArray(current?.goals) ? current.goals : [], goal],
			updatedAt: (/* @__PURE__ */ new Date()).toISOString()
		};
	});
	return goal;
}
function getActiveGoals(dataDir) {
	return readGoals(dataDir).filter((g) => g.status === "active");
}
async function updateGoalProgress(dataDir, goalId, progress) {
	let updated = null;
	await withJsonFile(goalsPath(dataDir), (current) => {
		const goals = Array.isArray(current?.goals) ? [...current.goals] : [];
		const idx = goals.findIndex((g) => g.id === goalId);
		if (idx >= 0) {
			updated = {
				...goals[idx],
				progress,
				updatedAt: (/* @__PURE__ */ new Date()).toISOString()
			};
			goals[idx] = updated;
		}
		return {
			goals,
			updatedAt: (/* @__PURE__ */ new Date()).toISOString()
		};
	});
	return updated;
}
async function cancelGoal(dataDir, goalId) {
	let cancelled = null;
	await withJsonFile(goalsPath(dataDir), (current) => {
		const goals = Array.isArray(current?.goals) ? [...current.goals] : [];
		const idx = goals.findIndex((g) => g.id === goalId);
		if (idx >= 0) {
			cancelled = {
				...goals[idx],
				status: "cancelled",
				updatedAt: (/* @__PURE__ */ new Date()).toISOString()
			};
			goals[idx] = cancelled;
		}
		return {
			goals,
			updatedAt: (/* @__PURE__ */ new Date()).toISOString()
		};
	});
	return cancelled;
}
async function syncGoalProgressFromPipeline(dataDir, _today) {
	const activeGoals = getActiveGoals(dataDir);
	const revenueGoals = activeGoals.filter((g) => g.metric === "revenue" && g.target > 0);
	if (revenueGoals.length === 0) return {
		updated: [],
		skipped: activeGoals.length
	};
	let totalWon = 0;
	for (const slug of listCustomerSlugs(dataDir)) {
		const deals = await readPipeline(dataDir, slug).catch(() => []);
		for (const deal of deals) if (deal.stage === "won") totalWon += deal.value ?? 0;
	}
	const updated = [];
	for (const goal of revenueGoals) {
		const progress = Math.min(100, Math.round(totalWon / goal.target * 100));
		if (await updateGoalProgress(dataDir, goal.id, progress)) updated.push(goal.id);
	}
	return {
		updated,
		skipped: activeGoals.length - revenueGoals.length
	};
}
//#endregion
export { goalsPath as a, makeGoalId as c, pursueGoal as d, rankDealsByLeverage as f, writeGoals as g, updateGoalProgress as h, getActiveGoals as i, parseLlmDecomposition as l, syncGoalProgressFromPipeline as m, cancelGoal as n, inferGoalType as o, readGoals as p, decomposeGoalRuleBased as r, inferMetric as s, buildDecompositionPrompt as t, parseTargetFromDescription as u };

//# sourceMappingURL=goal-engine-BbroPhqm.js.map