"use strict";
// Budget and cost tracking for companies and agents.
//
// Tracks monthly budgets per agent and per company, records spend events
// after each sub-agent run, and provides enforcement checks that the
// heartbeat can use.
//
// Storage: `.harness/companies/<companyId>/budgets.json`
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.listBudgets = listBudgets;
exports.getBudget = getBudget;
exports.createBudget = createBudget;
exports.updateBudget = updateBudget;
exports.recordSpend = recordSpend;
exports.listSpendEvents = listSpendEvents;
exports.checkBudgetStatus = checkBudgetStatus;
exports.listBudgetStatuses = listBudgetStatuses;
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const eventStore_1 = require("../persistence/eventStore");
function budgetsFile(projectDir, companyId) {
    return path.join(projectDir, '.harness', 'companies', companyId, 'budgets.json');
}
async function readBudgetData(projectDir, companyId) {
    try {
        const raw = await fs.readFile(budgetsFile(projectDir, companyId), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return { entries: [], spendEvents: [] };
    }
}
async function writeBudgetData(projectDir, companyId, data) {
    const fp = budgetsFile(projectDir, companyId);
    await fs.mkdir(path.dirname(fp), { recursive: true });
    await fs.writeFile(fp, JSON.stringify(data, null, 2), 'utf-8');
}
// ─── Budget CRUD ────────────────────────────────────────────────────
async function listBudgets(projectDir, companyId) {
    const data = await readBudgetData(projectDir, companyId);
    return data.entries;
}
async function getBudget(projectDir, companyId, agentId, periodStart) {
    const data = await readBudgetData(projectDir, companyId);
    return data.entries.find((e) => e.agentId === agentId && (!periodStart || e.periodStart === periodStart));
}
async function createBudget(projectDir, companyId, input, now = new Date()) {
    const data = await readBudgetData(projectDir, companyId);
    const entry = {
        id: crypto.randomUUID(),
        companyId,
        agentId: input.agentId,
        periodStart: input.periodStart,
        periodEnd: input.periodEnd,
        budgetCents: input.budgetCents,
        spentCents: 0,
        createdAt: now.toISOString(),
    };
    data.entries.push(entry);
    await writeBudgetData(projectDir, companyId, data);
    await (0, eventStore_1.emitEvent)(projectDir, 'service', 'budget.created', { entry }, 'system', entry.id).catch(() => { });
    return entry;
}
async function updateBudget(projectDir, companyId, id, input, now = new Date()) {
    const data = await readBudgetData(projectDir, companyId);
    const idx = data.entries.findIndex((e) => e.id === id);
    if (idx === -1)
        throw new Error(`Budget entry not found: ${id}`);
    const previous = data.entries[idx];
    const updated = {
        ...previous,
        ...(input.budgetCents !== undefined ? { budgetCents: input.budgetCents } : {}),
        ...(input.spentCents !== undefined ? { spentCents: input.spentCents } : {}),
        ...(input.periodEnd !== undefined ? { periodEnd: input.periodEnd } : {}),
    };
    data.entries[idx] = updated;
    await writeBudgetData(projectDir, companyId, data);
    await (0, eventStore_1.emitEvent)(projectDir, 'service', 'budget.updated', { entry: updated, previous }, 'system', updated.id).catch(() => { });
    return updated;
}
// ─── Spend Events ────────────────────────────────────────────────────
async function recordSpend(projectDir, companyId, input, now = new Date()) {
    const data = await readBudgetData(projectDir, companyId);
    const event = {
        id: crypto.randomUUID(),
        companyId,
        agentId: input.agentId,
        runId: input.runId,
        model: input.model,
        inputTokens: input.inputTokens,
        outputTokens: input.outputTokens,
        costCents: input.costCents,
        timestamp: now.toISOString(),
    };
    data.spendEvents.push(event);
    // Auto-increment the relevant budget entry's spentCents
    const period = data.entries.find((e) => {
        const withinPeriod = event.timestamp >= e.periodStart && event.timestamp <= e.periodEnd;
        return e.agentId === input.agentId && withinPeriod;
    });
    if (period) {
        period.spentCents += input.costCents;
    }
    await writeBudgetData(projectDir, companyId, data);
    await (0, eventStore_1.emitEvent)(projectDir, 'service', 'spend.recorded', { event }, 'system', event.id).catch(() => { });
    return event;
}
async function listSpendEvents(projectDir, companyId, filter) {
    const data = await readBudgetData(projectDir, companyId);
    return data.spendEvents.filter((e) => {
        if (filter?.agentId && e.agentId !== filter.agentId)
            return false;
        if (filter?.runId && e.runId !== filter.runId)
            return false;
        return true;
    });
}
// ─── Enforcement ──────────────────────────────────────────────────────
/**
 * Check whether an agent is over budget for the current period.
 */
async function checkBudgetStatus(projectDir, companyId, agentId, now = new Date()) {
    const data = await readBudgetData(projectDir, companyId);
    const nowIso = now.toISOString();
    const entry = data.entries.find((e) => e.agentId === agentId && nowIso >= e.periodStart && nowIso <= e.periodEnd);
    if (!entry)
        return undefined;
    return {
        agentId,
        budgetCents: entry.budgetCents,
        spentCents: entry.spentCents,
        remainingCents: entry.budgetCents - entry.spentCents,
        percentUsed: entry.budgetCents > 0 ? Math.round((entry.spentCents / entry.budgetCents) * 100) : 0,
        overBudget: entry.spentCents > entry.budgetCents,
    };
}
/**
 * Check all agents in a company for budget overages.
 */
async function listBudgetStatuses(projectDir, companyId, now = new Date()) {
    const data = await readBudgetData(projectDir, companyId);
    const nowIso = now.toISOString();
    const results = [];
    for (const entry of data.entries) {
        if (nowIso >= entry.periodStart && nowIso <= entry.periodEnd) {
            results.push({
                agentId: entry.agentId,
                budgetCents: entry.budgetCents,
                spentCents: entry.spentCents,
                remainingCents: entry.budgetCents - entry.spentCents,
                percentUsed: entry.budgetCents > 0 ? Math.round((entry.spentCents / entry.budgetCents) * 100) : 0,
                overBudget: entry.spentCents > entry.budgetCents,
            });
        }
    }
    return results;
}
//# sourceMappingURL=budgets.js.map