import { persistModuleStateRow } from "../../core/state/module-state-sidecar-migration.js";
import { UnifiedStateDb } from "../../core/state/unified-state-db.js";
import { getPlanArtifactStoragePaths, readPlanArtifactVersion, resolveLatestPlanArtifactVersion, writeNextPlanArtifactVersion } from "../../core/planning/plan-artifact-storage.js";
import { planningSqliteDatabaseRelativePath } from "../task-engine/planning-config.js";
import { digestPayload, stableStringify } from "../task-engine/mutation-utils.js";
const DRAFT_IDEMPOTENCY_MODULE_PREFIX = "planning-plan-artifact-draft-idempotency:";
export function userSuppliedPlanArtifactVersion(raw) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
        return undefined;
    }
    const version = raw.version;
    if (typeof version === "number" && Number.isInteger(version) && version >= 1) {
        return version;
    }
    return undefined;
}
export function resolveNextPlanArtifactVersion(workspacePath, planId) {
    const latest = resolveLatestPlanArtifactVersion(workspacePath, planId);
    return latest === null ? 1 : latest + 1;
}
/** Digest for idempotency per PLANNER_COMMANDS §7 (normalized body, planId, target version). */
export function planArtifactDraftPersistDigest(artifact, targetVersion) {
    const { version: _drop, ...body } = artifact;
    return digestPayload({
        planId: artifact.planId,
        version: targetVersion,
        artifact: JSON.parse(stableStringify(body))
    });
}
function idempotencyModuleId(clientMutationId) {
    return `${DRAFT_IDEMPOTENCY_MODULE_PREFIX}${clientMutationId}`;
}
function upsertModuleStateOnDatabase(db, moduleId, stateSchemaVersion, state) {
    const updatedAt = new Date().toISOString();
    db.prepare(`INSERT INTO workspace_module_state (module_id, state_schema_version, state_json, updated_at)
     VALUES (?, ?, ?, ?)
     ON CONFLICT(module_id) DO UPDATE SET
       state_schema_version=excluded.state_schema_version,
       state_json=excluded.state_json,
       updated_at=excluded.updated_at`).run(moduleId, stateSchemaVersion, JSON.stringify(state), updatedAt);
}
function sqliteDbRelativePath(workspacePath, effectiveConfig) {
    return planningSqliteDatabaseRelativePath({
        runtimeVersion: "0",
        workspacePath,
        effectiveConfig
    });
}
function readModuleStateJson(db, moduleId) {
    const row = db
        .prepare("SELECT state_json FROM workspace_module_state WHERE module_id = ?")
        .get(moduleId);
    if (!row) {
        return null;
    }
    const parsed = JSON.parse(row.state_json);
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
        return null;
    }
    return parsed;
}
function readIdempotencyRecord(workspacePath, clientMutationId, effectiveConfig, sqliteDb) {
    const moduleId = idempotencyModuleId(clientMutationId);
    const raw = sqliteDb
        ? readModuleStateJson(sqliteDb, moduleId)
        : new UnifiedStateDb(workspacePath, sqliteDbRelativePath(workspacePath, effectiveConfig)).getModuleState(moduleId)?.state;
    if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
        return null;
    }
    const row = raw;
    if (row.schemaVersion !== 1 ||
        typeof row.payloadDigest !== "string" ||
        typeof row.planId !== "string" ||
        typeof row.version !== "number" ||
        typeof row.planRef !== "string" ||
        typeof row.storagePath !== "string") {
        return null;
    }
    return row;
}
function writeIdempotencyRecord(workspacePath, clientMutationId, record, effectiveConfig, sqliteDb) {
    const moduleId = idempotencyModuleId(clientMutationId);
    const state = record;
    if (sqliteDb) {
        upsertModuleStateOnDatabase(sqliteDb, moduleId, 1, state);
        return;
    }
    persistModuleStateRow({
        workspacePath,
        databaseRelativePath: sqliteDbRelativePath(workspacePath, effectiveConfig),
        moduleId,
        stateSchemaVersion: 1,
        state
    });
}
export function preludePlanArtifactDraftPersist(args) {
    const { workspacePath, artifact, artifactRaw, clientMutationId, effectiveConfig, sqliteDb } = args;
    const targetVersion = resolveNextPlanArtifactVersion(workspacePath, artifact.planId);
    if (clientMutationId) {
        const prior = readIdempotencyRecord(workspacePath, clientMutationId, effectiveConfig, sqliteDb);
        if (prior) {
            const replayDigest = planArtifactDraftPersistDigest(artifact, prior.version);
            if (prior.payloadDigest !== replayDigest) {
                return {
                    kind: "conflict",
                    code: "idempotency-key-conflict",
                    message: `clientMutationId '${clientMutationId}' was already used for a different draft-plan-artifact payload`
                };
            }
            const stored = readPlanArtifactVersion(workspacePath, prior.planId, prior.version) ?? {
                ...artifact,
                version: prior.version,
                planRef: prior.planRef
            };
            return {
                kind: "replay",
                artifact: stored,
                storagePath: prior.storagePath,
                paths: getPlanArtifactStoragePaths(workspacePath, prior.planId)
            };
        }
    }
    const suppliedVersion = userSuppliedPlanArtifactVersion(artifactRaw);
    if (suppliedVersion !== undefined && suppliedVersion !== targetVersion) {
        return {
            kind: "conflict",
            code: "plan-artifact-version-conflict",
            message: `artifact.version ${suppliedVersion} does not match next version ${targetVersion} for plan ${artifact.planId}`
        };
    }
    const digest = planArtifactDraftPersistDigest(artifact, targetVersion);
    return { kind: "commit", targetVersion, digest };
}
/** Write artifact file + index; record idempotency when `clientMutationId` is set. */
export function commitPlanArtifactDraftPersist(args) {
    const { workspacePath, artifact, clientMutationId, digest, effectiveConfig, sqliteDb } = args;
    const toWrite = { ...artifact, status: "draft" };
    const { artifact: written, paths } = writeNextPlanArtifactVersion(workspacePath, toWrite, {
        effectiveConfig,
        sqliteDb
    });
    const storagePath = paths.artifactFileRelative(written.version);
    if (clientMutationId) {
        writeIdempotencyRecord(workspacePath, clientMutationId, {
            schemaVersion: 1,
            payloadDigest: digest,
            planId: written.planId,
            version: written.version,
            planRef: written.planRef,
            storagePath
        }, effectiveConfig, sqliteDb);
    }
    return { artifact: written, paths, storagePath };
}
export function planArtifactDraftPersistSuccessResult(args) {
    return {
        ok: true,
        code: args.code,
        message: args.replayed ? "PlanArtifact draft idempotent replay" : "PlanArtifact draft persisted",
        data: {
            schemaVersion: 1,
            responseSchemaVersion: 1,
            planId: args.artifact.planId,
            version: args.artifact.version,
            planRef: args.artifact.planRef,
            status: args.artifact.status,
            storagePath: args.storagePath,
            replayed: args.replayed
        }
    };
}
