import { normalizeWbsItemToTaskDraft, resolvePlanArtifactPhaseProposal } from "../../core/planning/index.js";
import { readPlanArtifactVersion, resolveLatestPlanArtifactVersion, writeNextPlanArtifactVersion } from "../../core/planning/plan-artifact-storage.js";
import { openPlanningStores } from "../../core/planning/index.js";
import { allocateNextTaskNumericId } from "../../core/planning/index.js";
import { inferTaskPhaseKey } from "../task-engine/phase-resolution.js";
import { reviewPlanningExecutionDraftGaps } from "../task-engine/planning-execution-draft-review.js";
import { runTaskRowMutationCommands } from "../task-engine/commands/task-row-mutation-commands.js";
import { buildTaskFromConversionPayload } from "../task-engine/mutation-utils.js";
import { attachPolicyMeta } from "../task-engine/attach-planning-response-meta.js";
import { planningGenPolicyGate } from "../task-engine/planning-generation-gate.js";
const ACTIVE_PHASE_TASK_STATUSES = new Set([
    "ready",
    "in_progress",
    "blocked",
    "awaiting_review",
    "awaiting_policy_approval",
    "awaiting_external_decision"
]);
function parseVersion(raw) {
    if (typeof raw === "number" && Number.isInteger(raw) && raw >= 1) {
        return raw;
    }
    return undefined;
}
function parseWbsFilter(raw) {
    if (!Array.isArray(raw) || raw.length === 0) {
        return undefined;
    }
    const ids = raw
        .filter((x) => typeof x === "string" && x.trim().length > 0)
        .map((x) => x.trim());
    return ids.length > 0 ? new Set(ids) : undefined;
}
function collectActivePhaseKeys(tasks) {
    const keys = new Set();
    for (const task of tasks) {
        if (!ACTIVE_PHASE_TASK_STATUSES.has(task.status)) {
            continue;
        }
        const key = inferTaskPhaseKey(task);
        if (key) {
            keys.add(key);
        }
    }
    return [...keys];
}
function resolveApprovalTargetVersion(loaded) {
    if (loaded.status === "accepted" && loaded.approvalRecord?.approvedVersion !== undefined) {
        return loaded.approvalRecord.approvedVersion;
    }
    return loaded.version;
}
function buildFinalizeTasks(drafts, wbsRows, existing, phaseKey, phaseLabel, desiredStatus) {
    const now = new Date().toISOString();
    let allocBase = [...existing];
    const assignedIds = new Map();
    const rowsWithIds = drafts.map((draft, index) => {
        const id = draft.id ?? allocateNextTaskNumericId(allocBase);
        const wbsId = wbsRows[index]?.wbsId;
        if (wbsId) {
            assignedIds.set(wbsId, id);
        }
        const row = { ...draft, id };
        allocBase = [
            ...allocBase,
            {
                id,
                title: draft.title,
                type: draft.type ?? "workspace-kit",
                status: desiredStatus,
                createdAt: now,
                updatedAt: now,
                phase: phaseLabel,
                phaseKey,
                approach: draft.approach,
                technicalScope: draft.technicalScope,
                acceptanceCriteria: draft.acceptanceCriteria
            }
        ];
        return row;
    });
    const built = [];
    for (const draft of rowsWithIds) {
        const row = {
            ...draft,
            dependsOn: draft.dependsOn?.map((dep) => assignedIds.get(dep) ?? dep),
            phase: phaseLabel,
            phaseKey,
            status: desiredStatus
        };
        const bt = buildTaskFromConversionPayload(row, now);
        if (!bt.ok) {
            return { ok: false, message: bt.message };
        }
        const task = {
            ...bt.task,
            status: desiredStatus,
            phaseKey,
            phase: phaseLabel
        };
        built.push(task);
    }
    return { ok: true, tasks: built };
}
function applyResolvedPhaseToDrafts(drafts, phaseKey, phaseLabel, desiredStatus) {
    return drafts.map((d) => ({
        ...d,
        phaseKey,
        phase: phaseLabel,
        status: desiredStatus
    }));
}
export async function runFinalizePlanToPhase(args, ctx, instructionPath) {
    const planId = typeof args.planId === "string" ? args.planId.trim() : "";
    if (!planId) {
        return { ok: false, code: "invalid-run-args", message: "finalize-plan-to-phase requires planId" };
    }
    const dryRun = args.dryRun !== false;
    const stores = await openPlanningStores(ctx);
    const pg = planningGenPolicyGate(ctx, args, instructionPath, stores.sqliteDual.getPlanningGeneration());
    if (pg.block) {
        return pg.block;
    }
    const latestVersion = resolveLatestPlanArtifactVersion(ctx.workspacePath, planId);
    if (latestVersion === null) {
        return {
            ok: false,
            code: "plan-artifact-not-found",
            message: `PlanArtifact ${planId} not found`,
            data: { schemaVersion: 1, responseSchemaVersion: 1, planId }
        };
    }
    const requestedVersion = parseVersion(args.version);
    const targetVersion = requestedVersion ?? latestVersion;
    const loaded = readPlanArtifactVersion(ctx.workspacePath, planId, targetVersion);
    if (!loaded) {
        return {
            ok: false,
            code: "plan-artifact-not-found",
            message: `PlanArtifact ${planId} version ${targetVersion} not found`,
            data: { schemaVersion: 1, responseSchemaVersion: 1, planId, version: targetVersion }
        };
    }
    if (requestedVersion !== undefined && requestedVersion !== latestVersion) {
        return {
            ok: false,
            code: "plan-artifact-version-mismatch",
            message: `Requested version ${requestedVersion} is not the latest (${latestVersion})`,
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: requestedVersion,
                latestVersion
            }
        };
    }
    if (loaded.status !== "accepted") {
        return {
            ok: false,
            code: "plan-artifact-not-accepted",
            message: `PlanArtifact ${planId} is not accepted (status: ${loaded.status})`,
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: loaded.version,
                status: loaded.status
            }
        };
    }
    const approvalTarget = resolveApprovalTargetVersion(loaded);
    if (loaded.approvalRecord?.approvedVersion !== undefined && loaded.approvalRecord.approvedVersion !== approvalTarget) {
        return {
            ok: false,
            code: "plan-artifact-not-accepted",
            message: `PlanArtifact ${planId} approval pin is inconsistent`,
            data: { schemaVersion: 1, responseSchemaVersion: 1, planId, version: loaded.version }
        };
    }
    const desiredStatus = args.desiredStatus === "proposed" ? "proposed" : "ready";
    const phaseResolved = resolvePlanArtifactPhaseProposal({
        targetPhaseKey: typeof args.targetPhaseKey === "string" ? args.targetPhaseKey : undefined,
        targetPhase: typeof args.targetPhase === "string" ? args.targetPhase : undefined,
        phaseShortDescription: typeof args.phaseShortDescription === "string" ? args.phaseShortDescription : undefined,
        phaseRecommendations: loaded.phaseRecommendations,
        activePhaseKeys: collectActivePhaseKeys(stores.taskStore.getAllTasks()),
        allowPhaseKeyCollision: args.allowPhaseKeyCollision === true,
        strict: args.strict !== false
    });
    if (!phaseResolved.ok) {
        return {
            ok: false,
            code: "plan-artifact-finalize-review-failed",
            message: "Finalize blocked: phase proposal could not be resolved",
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: loaded.version,
                phaseFindings: phaseResolved.findings
            }
        };
    }
    const { proposal, findings: phaseFindings } = phaseResolved;
    const wbsFilter = parseWbsFilter(args.wbsFilter);
    const wbsRows = wbsFilter
        ? loaded.wbs.filter((row) => wbsFilter.has(row.wbsId))
        : loaded.wbs;
    if (wbsRows.length === 0) {
        return {
            ok: false,
            code: "plan-artifact-finalize-review-failed",
            message: "Finalize blocked: no WBS rows selected",
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: loaded.version,
                wbsFilter: wbsFilter ? [...wbsFilter] : undefined
            }
        };
    }
    const normContext = {
        planRef: loaded.planRef,
        planId: loaded.planId,
        planVersion: approvalTarget,
        planningType: loaded.identity.planningType,
        defaultPhase: proposal.label,
        defaultPhaseKey: proposal.phaseKey,
        defaultStatus: desiredStatus
    };
    const wbsFindings = [];
    const rawDrafts = [];
    for (const row of wbsRows) {
        const normalized = normalizeWbsItemToTaskDraft(row, normContext);
        if (!normalized.ok) {
            for (const f of normalized.findings) {
                wbsFindings.push({
                    code: f.code,
                    severity: "error",
                    wbsId: row.wbsId,
                    message: f.message,
                    field: f.field
                });
            }
            continue;
        }
        rawDrafts.push(normalized.draft);
    }
    if (wbsFindings.length > 0) {
        return {
            ok: false,
            code: "plan-artifact-finalize-review-failed",
            message: "Finalize blocked: WBS normalization failed",
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: loaded.version,
                wbsFindings
            }
        };
    }
    const taskPreview = applyResolvedPhaseToDrafts(rawDrafts, proposal.phaseKey, proposal.label, desiredStatus);
    const built = buildFinalizeTasks(taskPreview, wbsRows, stores.taskStore.getAllTasks(), proposal.phaseKey, proposal.label, desiredStatus);
    if (!built.ok) {
        return {
            ok: false,
            code: "plan-artifact-finalize-review-failed",
            message: built.message,
            data: { schemaVersion: 1, responseSchemaVersion: 1, planId, version: loaded.version }
        };
    }
    const batchFindings = reviewPlanningExecutionDraftGaps(built.tasks);
    const errorCount = batchFindings.filter((f) => f.severity === "error").length;
    const warningCount = batchFindings.filter((f) => f.severity === "warning").length;
    const phaseWarningCount = phaseFindings.filter((f) => f.severity === "warning").length;
    if (errorCount > 0 || phaseFindings.some((f) => f.severity === "blocker")) {
        return {
            ok: false,
            code: "plan-artifact-finalize-review-failed",
            message: "Finalize blocked: task batch review has blockers",
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: loaded.version,
                phaseKey: proposal.phaseKey,
                phaseProposal: proposal,
                review: {
                    passed: false,
                    errorCount,
                    warningCount: warningCount + phaseWarningCount,
                    findings: [...phaseFindings, ...batchFindings, ...wbsFindings],
                    reviewProfile: "ux-cae-pre-persist-v1"
                }
            }
        };
    }
    if (!dryRun) {
        const persist = await runTaskRowMutationCommands({
            name: "persist-planning-execution-drafts",
            args: {
                tasks: built.tasks,
                planRef: loaded.planRef,
                planningType: loaded.identity.planningType,
                targetPhaseKey: proposal.phaseKey,
                targetPhase: proposal.label,
                desiredStatus,
                expectedPlanningGeneration: args.expectedPlanningGeneration,
                policyApproval: args.policyApproval,
                clientMutationId: args.clientMutationId,
                actor: args.actor
            }
        }, ctx, stores, stores.taskStore);
        if (!persist) {
            return {
                ok: false,
                code: "unsupported-command",
                message: "persist-planning-execution-drafts is unavailable"
            };
        }
        if (!persist.ok) {
            return persist;
        }
        const now = new Date().toISOString();
        const finalized = {
            ...loaded,
            status: "finalized",
            taskGenerationPayloads: built.tasks.map((task) => ({
                id: task.id,
                title: task.title,
                type: task.type,
                priority: task.priority,
                phase: task.phase,
                phaseKey: task.phaseKey,
                approach: task.approach ?? "",
                technicalScope: task.technicalScope ?? [],
                acceptanceCriteria: task.acceptanceCriteria ?? [],
                dependsOn: task.dependsOn,
                status: task.status === "ready" ? "ready" : "proposed"
            })),
            provenance: {
                ...loaded.provenance,
                updatedAt: now
            }
        };
        const written = writeNextPlanArtifactVersion(ctx.workspacePath, finalized, {
            effectiveConfig: ctx.effectiveConfig,
            sqliteDb: stores.sqliteDual.getDatabase()
        });
        const persistData = (persist.data ?? {});
        const result = {
            ok: true,
            code: persist.code === "planning-execution-drafts-idempotent-replay"
                ? "plan-artifact-finalize-idempotent-replay"
                : "plan-artifact-finalize-persisted",
            message: `Finalized plan ${planId} into ${built.tasks.length} task(s) for phase ${proposal.phaseKey}`,
            data: {
                schemaVersion: 1,
                responseSchemaVersion: 1,
                planId,
                version: written.artifact.version,
                planRef: loaded.planRef,
                status: written.artifact.status,
                dryRun: false,
                phaseKey: proposal.phaseKey,
                phaseProposal: proposal,
                createdTasks: persistData.createdTasks,
                count: persistData.count ?? built.tasks.length,
                replayed: persistData.replayed === true,
                storagePath: written.paths.artifactFileRelative(written.artifact.version),
                delegatedCode: persist.code,
                review: {
                    passed: true,
                    errorCount: 0,
                    warningCount: warningCount + phaseWarningCount,
                    findings: [...phaseFindings, ...batchFindings],
                    reviewProfile: "ux-cae-pre-persist-v1",
                    taskCount: built.tasks.length
                },
                planningGeneration: persistData.planningGeneration,
                planningGenerationPolicy: persistData.planningGenerationPolicy
            }
        };
        attachPolicyMeta(result.data, ctx, stores.sqliteDual.getPlanningGeneration(), pg.warnings);
        return result;
    }
    const result = {
        ok: true,
        code: "plan-artifact-finalize-preview",
        message: `Finalize preview: ${taskPreview.length} task draft(s) for phase ${proposal.phaseKey}`,
        data: {
            schemaVersion: 1,
            responseSchemaVersion: 1,
            planId,
            version: loaded.version,
            dryRun: true,
            phaseKey: proposal.phaseKey,
            phaseProposal: proposal,
            taskPreview,
            taskGenerationPayloads: taskPreview,
            review: {
                passed: true,
                errorCount: 0,
                warningCount: warningCount + phaseWarningCount,
                findings: [...phaseFindings, ...batchFindings],
                reviewProfile: "ux-cae-pre-persist-v1",
                taskCount: taskPreview.length
            },
            phaseResolutionSource: phaseResolved.source
        }
    };
    attachPolicyMeta(result.data, ctx, stores.sqliteDual.getPlanningGeneration(), pg.warnings);
    return result;
}
