import { randomUUID } from "node:crypto";
import { openPlanningStores } from "../../core/planning/index.js";
import { appendLineageEvent } from "../../core/lineage-store.js";
import { loadImprovementState, saveImprovementState } from "./improvement-state.js";
import { ingestAgentTranscripts, ingestConfigMutations, ingestGitDiffBetweenTags, ingestPolicyDenials, ingestTaskTransitionFriction } from "./ingest.js";
import { priorityForTier } from "./confidence.js";
import { buildImprovementTaskPayload } from "./improvement-task-payload.js";
import { TRANSCRIPT_CHURN_TASK_TYPE } from "../task-engine/transcript-churn.js";
import { buildImprovementSupportingReasoning } from "./improvement-supporting-reasoning.js";
import { allocateNextTaskNumericId } from "../task-engine/wishlist/wishlist-intake.js";
import { planningConcurrencySaveOpts } from "../task-engine/mutation-utils.js";
import { enforcePlanningGenerationPolicy, getPlanningGenerationPolicy } from "../task-engine/planning-config.js";
import { TaskEngineError } from "../task-engine/transitions.js";
function hasEvidenceKey(tasks, key) {
    return tasks.some((t) => {
        const m = t.metadata;
        if (!m || typeof m !== "object")
            return false;
        return m.evidenceKey === key;
    });
}
function resolveTranscriptArchivePath(ctx, args) {
    if (typeof args.transcriptsRoot === "string" && args.transcriptsRoot.trim().length > 0) {
        return args.transcriptsRoot.trim();
    }
    const improvement = ctx.effectiveConfig?.improvement && typeof ctx.effectiveConfig.improvement === "object"
        ? ctx.effectiveConfig.improvement
        : {};
    const transcripts = improvement.transcripts && typeof improvement.transcripts === "object"
        ? improvement.transcripts
        : {};
    const archivePath = typeof transcripts.archivePath === "string" ? transcripts.archivePath.trim() : "";
    return archivePath || "agent-transcripts";
}
export function resolveImprovementHeuristicVersion(ctx) {
    const improvement = ctx.effectiveConfig?.improvement && typeof ctx.effectiveConfig.improvement === "object"
        ? ctx.effectiveConfig.improvement
        : {};
    const rec = improvement.recommendations && typeof improvement.recommendations === "object"
        ? improvement.recommendations
        : {};
    const v = rec.heuristicVersion;
    return v === 2 ? 2 : 1;
}
export function getMaxRecommendationCandidatesPerRun(ctx) {
    const improvement = ctx.effectiveConfig?.improvement && typeof ctx.effectiveConfig.improvement === "object"
        ? ctx.effectiveConfig.improvement
        : {};
    const cadence = improvement.cadence && typeof improvement.cadence === "object"
        ? improvement.cadence
        : {};
    const raw = cadence.maxRecommendationCandidatesPerRun;
    if (typeof raw === "number" && Number.isFinite(raw)) {
        return Math.max(1, Math.floor(raw));
    }
    return 500;
}
export async function runGenerateRecommendations(ctx, args) {
    const dryRun = args.dryRun === true;
    const runId = randomUUID();
    const planning = await openPlanningStores(ctx);
    const store = planning.taskStore;
    const state = await loadImprovementState(ctx.workspacePath, ctx.effectiveConfig);
    const transcriptsRoot = resolveTranscriptArchivePath(ctx, args);
    const fromTag = typeof args.fromTag === "string" ? args.fromTag.trim() : undefined;
    const toTag = typeof args.toTag === "string" ? args.toTag.trim() : undefined;
    const heuristicVersion = resolveImprovementHeuristicVersion(ctx);
    const candidates = [];
    candidates.push(...(await ingestAgentTranscripts(ctx.workspacePath, transcriptsRoot, state, heuristicVersion)));
    candidates.push(...(await ingestPolicyDenials(ctx.workspacePath, state, heuristicVersion, ctx.effectiveConfig)));
    candidates.push(...(await ingestConfigMutations(ctx.workspacePath, state, heuristicVersion)));
    candidates.push(...ingestTaskTransitionFriction(store.getTransitionLog(), state, heuristicVersion));
    if (fromTag && toTag) {
        const g = ingestGitDiffBetweenTags(ctx.workspacePath, fromTag, toTag, heuristicVersion);
        if (g)
            candidates.push(g);
    }
    const allTasks = store.getAllTasks();
    const created = [];
    const simulatedCreates = [];
    let skippedDuplicateEvidenceKey = 0;
    /** Retained for JSON shape compatibility; numeric **T###** allocation makes collisions extremely unlikely. */
    let skippedExistingTaskId = 0;
    let cappedRemaining = 0;
    const now = new Date().toISOString();
    const maxCreates = getMaxRecommendationCandidatesPerRun(ctx);
    const shadowTasksForDryRun = dryRun ? [...allTasks] : null;
    for (const c of candidates) {
        if (hasEvidenceKey(allTasks, c.evidenceKey)) {
            skippedDuplicateEvidenceKey += 1;
            continue;
        }
        const wouldCount = dryRun ? simulatedCreates.length : created.length;
        if (wouldCount >= maxCreates) {
            cappedRemaining += 1;
            continue;
        }
        if (dryRun) {
            const id = allocateNextTaskNumericId(shadowTasksForDryRun);
            const isTranscript = c.evidenceKind === "transcript";
            shadowTasksForDryRun.push({
                id,
                status: isTranscript ? "research" : "proposed",
                type: isTranscript ? TRANSCRIPT_CHURN_TASK_TYPE : "improvement",
                title: "",
                createdAt: now,
                updatedAt: now
            });
            simulatedCreates.push(id);
            continue;
        }
        const id = allocateNextTaskNumericId(allTasks);
        if (store.getTask(id)) {
            skippedExistingTaskId += 1;
            continue;
        }
        const body = buildImprovementTaskPayload(c);
        const supportingReasoning = buildImprovementSupportingReasoning(c);
        const meta = {
            evidenceKey: c.evidenceKey,
            evidenceKind: c.evidenceKind,
            confidence: c.confidence.score,
            confidenceTier: c.confidence.tier,
            confidenceReasons: c.confidence.reasons,
            provenanceRefs: c.provenanceRefs,
            issue: body.issue,
            supportingReasoning,
            proposedSolutions: [body.proposedSolution],
            heuristicVersion
        };
        if (c.scoutMeta && typeof c.scoutMeta === "object") {
            const sm = c.scoutMeta;
            if (sm.primaryLens !== undefined)
                meta.primaryLens = sm.primaryLens;
            if (sm.adversarialLens !== undefined)
                meta.adversarialLens = sm.adversarialLens;
            if (sm.findingType !== undefined)
                meta.findingType = sm.findingType;
            if (sm.evidenceAnchors !== undefined)
                meta.evidenceAnchors = sm.evidenceAnchors;
            if (sm.riskNotes !== undefined)
                meta.riskNotes = sm.riskNotes;
            if (sm.noveltyHint !== undefined)
                meta.noveltyHint = sm.noveltyHint;
        }
        if (c.evidenceKind === "transcript" && typeof c.provenanceRefs.transcriptPath === "string") {
            meta.transcriptSourceRelPath = c.provenanceRefs.transcriptPath;
        }
        const isTranscript = c.evidenceKind === "transcript";
        const task = {
            id,
            status: isTranscript ? "research" : "proposed",
            type: isTranscript ? TRANSCRIPT_CHURN_TASK_TYPE : "improvement",
            title: body.title,
            createdAt: now,
            updatedAt: now,
            priority: priorityForTier(c.confidence.tier),
            approach: body.approach,
            technicalScope: body.technicalScope,
            acceptanceCriteria: body.acceptanceCriteria,
            metadata: meta
        };
        store.addTask(task);
        allTasks.push(task);
        created.push(id);
        await appendLineageEvent(ctx.workspacePath, {
            eventType: "rec",
            recommendationTaskId: id,
            evidenceKey: c.evidenceKey,
            payload: {
                recommendationTaskId: id,
                evidenceKey: c.evidenceKey,
                title: body.title,
                issue: body.issue,
                proposedSolution: body.proposedSolution,
                confidence: c.confidence.score,
                confidenceTier: c.confidence.tier,
                provenanceRefs: c.provenanceRefs
            }
        });
    }
    if (!dryRun) {
        const grGate = enforcePlanningGenerationPolicy(getPlanningGenerationPolicy({
            effectiveConfig: ctx.effectiveConfig
        }), args);
        if (!grGate.ok) {
            throw new TaskEngineError(grGate.code, grGate.message);
        }
        await store.save(planningConcurrencySaveOpts(args));
        await saveImprovementState(ctx.workspacePath, state, ctx.effectiveConfig);
    }
    const skipped = skippedDuplicateEvidenceKey + skippedExistingTaskId;
    return {
        runId,
        created,
        skipped,
        candidates: candidates.length,
        ...(dryRun ? { dryRun: true, simulatedCreates } : {}),
        dedupe: {
            skippedDuplicateEvidenceKey,
            skippedExistingTaskId,
            cappedRemaining
        }
    };
}
