// Extracted from src/db/repository.ts (mechanical split — bodies unchanged).
// reflection repository domain. Facade: src/db/repository.ts (Repository class).
import Database from 'better-sqlite3';

  // ── Reflection piece-edit cooldown ──────────────────────────────────────────

  /**
   * Records that the reflection pipeline wrote a new version of pieceName for
   * userId.  snapshotId ties the edit back to the snapshot that triggered it.
   */
  export function recordPieceEdit(db: Database.Database, userId: string, pieceName: string, snapshotId: string): void {
    db.prepare(
      `INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at)
       VALUES (?, ?, ?, ?)`
    ).run(userId, pieceName, snapshotId, Date.now());
  }


  /**
   * Returns the number of piece edits for (userId, pieceName) that occurred
   * within the last sinceMs milliseconds.  Used by the cooldown gate in
   * piece-writer.ts to prevent over-editing the same piece.
   */
  export function countRecentPieceEdits(db: Database.Database, userId: string, pieceName: string, sinceMs: number): number {
    return (db.prepare(
      `SELECT COUNT(*) AS c FROM reflection_piece_edits
       WHERE user_id = ? AND piece_name = ? AND created_at > ?`
    ).get(userId, pieceName, Date.now() - sinceMs) as { c: number }).c;
  }


  // ── Reflection metrics ───────────────────────────────────────────────────────

  /**
   * Insert one row into reflection_metrics, optionally bundling a
   * reflection_piece_edits row in the same transaction.
   *
   * When pieceEdit is supplied the two inserts are wrapped in a single
   * db.transaction() so the tables stay consistent even if the process
   * crashes between them.
   */
  export function recordReflectionRun(db: Database.Database, metric: {
      reflection_job_id: string;
      original_job_id: string | null;
      user_id: string;
      piece_name: string | null;
      outcome: 'applied' | 'partial' | 'abstained' | 'rejected' | 'failed';
      memory_changes: number;
      piece_edited: 0 | 1;
      tokens_in: number;
      tokens_out: number;
      duration_ms: number;
    }, pieceEdit?: { pieceName: string; snapshotId: string }): void {
    const now = Date.now();
    const insertMetric = db.prepare(`
      INSERT INTO reflection_metrics
        (reflection_job_id, original_job_id, user_id, piece_name, outcome,
         memory_changes, piece_edited, tokens_in, tokens_out, duration_ms, created_at)
      VALUES
        (@reflection_job_id, @original_job_id, @user_id, @piece_name, @outcome,
         @memory_changes, @piece_edited, @tokens_in, @tokens_out, @duration_ms, @created_at)
    `);

    if (pieceEdit) {
      const insertEdit = db.prepare(`
        INSERT INTO reflection_piece_edits (user_id, piece_name, snapshot_id, created_at)
        VALUES (?, ?, ?, ?)
      `);
      db.transaction(() => {
        insertMetric.run({ ...metric, created_at: now });
        insertEdit.run(metric.user_id, pieceEdit.pieceName, pieceEdit.snapshotId, now);
      })();
    } else {
      insertMetric.run({ ...metric, created_at: now });
    }
  }


  /**
   * Convenience alias for callers that don't need the bundled pieceEdit path.
   */
  export function recordReflectionMetric(db: Database.Database, row: {
      reflection_job_id: string;
      original_job_id: string | null;
      user_id: string;
      piece_name: string | null;
      outcome: 'applied' | 'partial' | 'abstained' | 'rejected' | 'failed';
      memory_changes: number;
      piece_edited: 0 | 1;
      tokens_in: number;
      tokens_out: number;
      duration_ms: number;
    }): void {
    recordReflectionRun(db, row);
  }


  /**
   * Aggregate reflection metrics for a user since sinceMs (epoch ms).
   * Returns counts per outcome and totals for tokens + piece edits.
   */
  export function aggregateReflectionMetrics(db: Database.Database, userId: string, sinceMs: number): {
    applied: number;
    partial: number;
    abstained: number;
    rejected: number;
    failed: number;
    tokensIn: number;
    tokensOut: number;
    pieceEdits: number;
    totalRuns: number;
  } {
    const rows = db
      .prepare(
        `SELECT outcome, COUNT(*) AS cnt,
                SUM(tokens_in) AS ti, SUM(tokens_out) AS to_,
                SUM(piece_edited) AS pe
         FROM reflection_metrics
         WHERE user_id = ? AND created_at >= ?
         GROUP BY outcome`,
      )
      .all(userId, sinceMs) as Array<{
        outcome: string;
        cnt: number;
        ti: number;
        to_: number;
        pe: number;
      }>;

    const result = {
      applied: 0, partial: 0, abstained: 0, rejected: 0, failed: 0,
      tokensIn: 0, tokensOut: 0, pieceEdits: 0, totalRuns: 0,
    };
    for (const r of rows) {
      const o = r.outcome as keyof Pick<typeof result, 'applied' | 'partial' | 'abstained' | 'rejected' | 'failed'>;
      if (o in result) (result as Record<string, number>)[o] = r.cnt;
      result.tokensIn += r.ti ?? 0;
      result.tokensOut += r.to_ ?? 0;
      result.pieceEdits += r.pe ?? 0;
      result.totalRuns += r.cnt;
    }
    return result;
  }
