/**
 * EvidenceRepository - CRUD + list_for_entity for evidence_refs table (§15)
 *
 * Implements Repository<EvidenceRefRecord, EvidenceRefInsert, EvidenceRefUpdate> per contracts §6.
 *
 * @module packages/runtime/src/storage/repositories/EvidenceRepository
 */

import type {
  Repository,
  TransactionHandle,
  SessionID,
  TaskID,
  AgentID,
  ToolRunID,
  CommandRunID,
  ArtifactID,
  EvidenceRefID,
  MessageID,
  ISOTimeString,
} from '@aircoding/contracts'

import { DatabaseHandle } from '../MigrationRunner.js'
import { assertEnumValues } from '../assertEnum.js'

// =============================================================================
// Types - per db-schema §15
// =============================================================================

export type EvidenceRefKind = 'build_output' | 'test_output' | 'log' | 'screenshot' | 'diff' | 'metric' | 'other'

export interface EvidenceRefRecord {
  id: EvidenceRefID
  session_id: SessionID
  task_id?: TaskID
  agent_id?: AgentID
  tool_run_id?: ToolRunID
  command_run_id?: CommandRunID
  artifact_id?: ArtifactID
  diagnostic_id?: string
  message_id?: MessageID
  kind: EvidenceRefKind
  ref: string
  location_json?: string
  claim: string
  created_at: ISOTimeString
}

export type EvidenceRefInsert = Omit<EvidenceRefRecord, 'id'> & {
  id?: EvidenceRefID
}

export type EvidenceRefUpdate = Partial<Omit<EvidenceRefRecord, 'id' | 'session_id' | 'created_at'>>

// =============================================================================
// EvidenceRepository
// =============================================================================

export class EvidenceRepository implements Repository<EvidenceRefRecord, EvidenceRefInsert, EvidenceRefUpdate> {
  private db: DatabaseHandle

  constructor(db: DatabaseHandle) {
    this.db = db
  }

  /**
   * Get an evidence ref by ID.
   */
  async get(id: EvidenceRefID, tx?: TransactionHandle): Promise<EvidenceRefRecord | undefined> {
    const stmt = (tx?.db ?? this.db).prepare('SELECT * FROM evidence_refs WHERE id = ?')
    const row = stmt.get(id) as EvidenceRefRecord | undefined
    return row
  }

  /**
   * Insert a new evidence ref.
   */
  async insert(record: EvidenceRefInsert, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns
    assertEnumValues('evidence_refs', {
      kind: record.kind,
    })

    const stmt = (tx?.db ?? this.db).prepare(`
      INSERT INTO evidence_refs (
        id, session_id,
        task_id, agent_id, tool_run_id, command_run_id, artifact_id, diagnostic_id, message_id,
        kind, ref, location_json, claim,
        created_at
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `)

    stmt.run(
      record.id,
      record.session_id,
      record.task_id ?? null,
      record.agent_id ?? null,
      record.tool_run_id ?? null,
      record.command_run_id ?? null,
      record.artifact_id ?? null,
      record.diagnostic_id ?? null,
      record.message_id ?? null,
      record.kind,
      record.ref,
      record.location_json ?? null,
      record.claim,
      record.created_at,
    )
  }

  /**
   * Update an existing evidence ref.
   */
  async update(id: EvidenceRefID, patch: EvidenceRefUpdate, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns if present
    if (patch.kind !== undefined) {
      assertEnumValues('evidence_refs', { kind: patch.kind })
    }

    const fields: string[] = []
    const values: unknown[] = []

    if (patch.ref !== undefined) {
      fields.push('ref = ?')
      values.push(patch.ref)
    }
    if (patch.location_json !== undefined) {
      fields.push('location_json = ?')
      values.push(patch.location_json)
    }
    if (patch.claim !== undefined) {
      fields.push('claim = ?')
      values.push(patch.claim)
    }

    if (fields.length === 0) {
      return // Nothing to update
    }

    values.push(id)
    const stmt = (tx?.db ?? this.db).prepare(`UPDATE evidence_refs SET ${fields.join(', ')} WHERE id = ?`)
    stmt.run(...values)
  }

  // =============================================================================
  // Extra methods
  // =============================================================================

  /**
   * List evidence refs for a specific entity.
   * Entity can be identified by any of: task_id, agent_id, tool_run_id, command_run_id, artifact_id, diagnostic_id, message_id
   */
  async list_for_entity(entity_type: string, entity_id: string): Promise<EvidenceRefRecord[]> {
    const validTypes = ['task_id', 'agent_id', 'tool_run_id', 'command_run_id', 'artifact_id', 'diagnostic_id', 'message_id']
    if (!validTypes.includes(entity_type)) {
      throw new Error(`Invalid entity_type: ${entity_type}`)
    }

    const stmt = this.db.prepare(
      `SELECT * FROM evidence_refs WHERE ${entity_type} = ? ORDER BY created_at DESC`,
    )
    return stmt.all(entity_id) as EvidenceRefRecord[]
  }
}