/**
 * EventRepository - CRUD + insert with transaction, query for events table (§6)
 *
 * Implements Repository<EventRecord, EventInsert, EventUpdate>
 * per contracts §6.
 *
 * @module packages/runtime/src/storage/repositories/EventRepository
 */

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

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

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

export interface EventRecord {
  id: UUID
  session_id: SessionID
  type: string
  version: number
  timestamp: ISOTimeString

  source_kind: 'main' | 'architecture_designer' | 'scheduler' | 'agent' | 'tool' | 'system'
  source_id?: string
  agent_type?: string

  task_id?: TaskID
  agent_id?: AgentID
  tool_run_id?: ToolRunID
  command_run_id?: CommandRunID

  route_json: string
  route_text: string

  payload_json: string
}

export type EventInsert = Omit<EventRecord, 'id'> & {
  id?: UUID
}

export type EventUpdate = Partial<Omit<EventRecord, 'id' | 'session_id' | 'timestamp'>>

// Event filter for queries
export interface EventFilter {
  session_id?: SessionID
  types?: string[]
  task_id?: TaskID
  agent_id?: AgentID
  tool_run_id?: ToolRunID
  command_run_id?: CommandRunID
  route_prefix?: string[]
  since?: ISOTimeString
}

// =============================================================================
// EventRepository
// =============================================================================

export class EventRepository implements Repository<EventRecord, EventInsert, EventUpdate> {
  private db: DatabaseHandle

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

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

  /**
   * Insert a new event.
   */
  async insert(record: EventInsert, tx?: TransactionHandle): Promise<void> {
    const stmt = (tx?.db ?? this.db).prepare(`
      INSERT INTO events (
        id, session_id, type, version, timestamp,
        source_kind, source_id, agent_type,
        task_id, agent_id, tool_run_id, command_run_id,
        route_json, route_text, payload_json
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `)

    stmt.run(
      record.id,
      record.session_id,
      record.type,
      record.version,
      record.timestamp,
      record.source_kind,
      record.source_id ?? null,
      record.agent_type ?? null,
      record.task_id ?? null,
      record.agent_id ?? null,
      record.tool_run_id ?? null,
      record.command_run_id ?? null,
      record.route_json,
      record.route_text,
      record.payload_json,
    )
  }

  /**
   * Update an existing event.
   */
  async update(_id: UUID, _patch: EventUpdate, tx?: TransactionHandle): Promise<void> {
    // Events are immutable - no updates allowed
    // This method exists to satisfy the Repository interface
    throw new Error('Events are immutable and cannot be updated')
  }

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

  /**
   * Insert an event within a transaction.
   * This is the primary method for event insertion since events are immutable.
   */
  async insert_in_transaction(record: EventInsert, tx: TransactionHandle): Promise<void> {
    // Call the standard insert - the transaction is handled by the caller
    await this.insert(record, tx)
  }

  /**
   * Query events with optional filters.
   */
  async query(filter: EventFilter): Promise<EventRecord[]> {
    const conditions: string[] = []
    const params: unknown[] = []

    if (filter.session_id) {
      conditions.push('session_id = ?')
      params.push(filter.session_id)
    }

    if (filter.types && filter.types.length > 0) {
      conditions.push(`type IN (${filter.types.map(() => '?').join(', ')})`)
      params.push(...filter.types)
    }

    if (filter.task_id) {
      conditions.push('task_id = ?')
      params.push(filter.task_id)
    }

    if (filter.agent_id) {
      conditions.push('agent_id = ?')
      params.push(filter.agent_id)
    }

    if (filter.tool_run_id) {
      conditions.push('tool_run_id = ?')
      params.push(filter.tool_run_id)
    }

    if (filter.command_run_id) {
      conditions.push('command_run_id = ?')
      params.push(filter.command_run_id)
    }

    if (filter.since) {
      conditions.push('timestamp > ?')
      params.push(filter.since)
    }

    if (filter.route_prefix && filter.route_prefix.length > 0) {
      const prefix = filter.route_prefix.join('/')
      conditions.push('route_text LIKE ?')
      params.push(`${prefix}%`)
    }

    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
    const stmt = this.db.prepare(
      `SELECT * FROM events ${whereClause} ORDER BY timestamp ASC`,
    )
    return stmt.all(...params) as EventRecord[]
  }
}