import { uuidv7 } from 'uuidv7';
const VALID_STATUSES = ['pending', 'approved', 'rejected', 'expired', 'cancelled'];
const TERMINAL_STATUSES = new Set(['approved', 'rejected', 'expired', 'cancelled']);
const VALID_TRANSITIONS = {
    pending: new Set(['approved', 'rejected', 'expired', 'cancelled']),
};
const DEFAULT_TIMEOUT_S = 300;
const MAX_TIMEOUT_S = 3600;
export class InvalidTransitionError extends Error {
    constructor(from, to) {
        super(`Invalid state transition: ${from} → ${to}`);
        this.name = 'InvalidTransitionError';
    }
}
export class ConfirmationStore {
    db;
    insertStmt;
    resolveStmt;
    getByIdStmt;
    constructor(storage) {
        this.db = storage.getDatabase();
        this.initTables();
        this.insertStmt = this.db.prepare(`
      INSERT INTO confirmations
        (id, correlationId, actionType, actionSummary, riskLevel, riskScore, riskFactors,
         status, expiresAt, idempotencyKey, createdAt)
      VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)
    `);
        this.resolveStmt = this.db.prepare(`
      UPDATE confirmations SET status = ?, respondedAt = ?, ownerResponse = ?
      WHERE id = ? AND status = 'pending'
    `);
        this.getByIdStmt = this.db.prepare('SELECT * FROM confirmations WHERE id = ?');
    }
    initTables() {
        this.db.exec(`
      CREATE TABLE IF NOT EXISTS confirmations (
        id TEXT PRIMARY KEY,
        correlationId TEXT NOT NULL,
        actionType TEXT NOT NULL,
        actionSummary TEXT NOT NULL,
        riskLevel TEXT NOT NULL,
        riskScore INTEGER NOT NULL,
        riskFactors TEXT NOT NULL,
        status TEXT NOT NULL DEFAULT 'pending'
          CHECK(status IN ('pending', 'approved', 'rejected', 'expired', 'cancelled')),
        channel TEXT,
        notifiedAt TEXT,
        respondedAt TEXT,
        ownerResponse TEXT,
        expiresAt TEXT NOT NULL,
        idempotencyKey TEXT UNIQUE,
        createdAt TEXT NOT NULL
      );
      CREATE INDEX IF NOT EXISTS idx_confirmation_status_expires ON confirmations(status, expiresAt);
      CREATE INDEX IF NOT EXISTS idx_confirmation_correlation ON confirmations(correlationId);
      CREATE INDEX IF NOT EXISTS idx_confirmation_idempotency ON confirmations(idempotencyKey);
    `);
    }
    create(input) {
        if (input.idempotencyKey) {
            const existing = this.getByIdempotencyKey(input.idempotencyKey);
            if (existing)
                return existing;
        }
        const id = uuidv7();
        const now = new Date().toISOString();
        const timeout = Math.min(input.timeoutSeconds ?? DEFAULT_TIMEOUT_S, MAX_TIMEOUT_S);
        const expiresAt = new Date(Date.now() + timeout * 1000).toISOString();
        this.insertStmt.run(id, input.correlationId, input.actionType, input.actionSummary, input.riskLevel, input.riskScore, JSON.stringify(input.riskFactors), expiresAt, input.idempotencyKey ?? null, now);
        return this.getById(id);
    }
    resolve(id, status, response) {
        const current = this.getById(id);
        if (!current) {
            throw new Error(`Confirmation not found: ${id}`);
        }
        const currentStatus = current.status;
        if (TERMINAL_STATUSES.has(currentStatus)) {
            throw new InvalidTransitionError(currentStatus, status);
        }
        const allowed = VALID_TRANSITIONS[currentStatus];
        if (!allowed || !allowed.has(status)) {
            throw new InvalidTransitionError(currentStatus, status);
        }
        const result = this.db.transaction(() => {
            const changes = this.resolveStmt.run(status, new Date().toISOString(), response ?? null, id);
            if (changes.changes === 0) {
                throw new InvalidTransitionError(currentStatus, status);
            }
            return this.getById(id);
        })();
        return result;
    }
    getById(id) {
        return this.getByIdStmt.get(id) ?? null;
    }
    getExpired() {
        const now = new Date().toISOString();
        return this.db
            .prepare("SELECT * FROM confirmations WHERE status = 'pending' AND expiresAt <= ?")
            .all(now);
    }
    getPending() {
        return this.db
            .prepare("SELECT * FROM confirmations WHERE status = 'pending' ORDER BY createdAt ASC")
            .all();
    }
    getByCorrelation(correlationId) {
        return this.db
            .prepare('SELECT * FROM confirmations WHERE correlationId = ? ORDER BY createdAt ASC')
            .all(correlationId);
    }
    updateChannel(id, channel) {
        this.db
            .prepare('UPDATE confirmations SET channel = ?, notifiedAt = ? WHERE id = ?')
            .run(channel, new Date().toISOString(), id);
    }
    getPendingCount() {
        return this.db
            .prepare("SELECT COUNT(*) as count FROM confirmations WHERE status = 'pending'")
            .get().count;
    }
    getByIdempotencyKey(key) {
        return this.db
            .prepare('SELECT * FROM confirmations WHERE idempotencyKey = ?')
            .get(key) ?? null;
    }
}
//# sourceMappingURL=ConfirmationStore.js.map