/**
 * MessageRepository - CRUD + list_by_session for messages table (§4)
 *
 * Implements Repository<MessageRecord, MessageInsert, MessageUpdate>
 * per contracts §6.
 *
 * @module packages/runtime/src/storage/repositories/MessageRepository
 */

import type {
  Repository,
  TransactionHandle,
  SessionID,
  MessageID,
  ISOTimeString,
} from '@aircoding/contracts'

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

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

export interface MessageRecord {
  id: MessageID
  session_id: SessionID
  role: 'user' | 'assistant' | 'system' | 'tool'
  canonical_format: 'anthropic'
  content_json: string
  parent_message_id?: MessageID
  route_json?: string
  created_at: ISOTimeString
  token_estimate?: number
  metadata_json?: string
}

export type MessageInsert = Omit<MessageRecord, 'id'> & {
  id?: MessageID
}

export type MessageUpdate = Partial<Omit<MessageRecord, 'id' | 'session_id' | 'created_at'>>

// =============================================================================
// MessageRepository
// =============================================================================

export class MessageRepository implements Repository<MessageRecord, MessageInsert, MessageUpdate> {
  private db: DatabaseHandle

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

  /**
   * Get a message by ID.
   */
  async get(id: MessageID, tx?: TransactionHandle): Promise<MessageRecord | undefined> {
    const stmt = (tx?.db ?? this.db).prepare('SELECT * FROM messages WHERE id = ?')
    const row = stmt.get(id) as MessageRecord | undefined
    return row
  }

  /**
   * Insert a new message.
   */
  async insert(record: MessageInsert, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns
    assertEnumValues('messages', {
      role: record.role,
      canonical_format: record.canonical_format,
    })

    const stmt = (tx?.db ?? this.db).prepare(`
      INSERT INTO messages (
        id, session_id, role, canonical_format, content_json,
        parent_message_id, route_json, created_at,
        token_estimate, metadata_json
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `)

    stmt.run(
      record.id,
      record.session_id,
      record.role,
      record.canonical_format,
      record.content_json,
      record.parent_message_id ?? null,
      record.route_json ?? null,
      record.created_at,
      record.token_estimate ?? null,
      record.metadata_json ?? null,
    )
  }

  /**
   * Update an existing message.
   */
  async update(id: MessageID, patch: MessageUpdate, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns if present
    if (patch.role !== undefined) {
      assertEnumValues('messages', { role: patch.role })
    }
    if (patch.canonical_format !== undefined) {
      assertEnumValues('messages', { canonical_format: patch.canonical_format })
    }

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

    if (patch.content_json !== undefined) {
      fields.push('content_json = ?')
      values.push(patch.content_json)
    }
    if (patch.parent_message_id !== undefined) {
      fields.push('parent_message_id = ?')
      values.push(patch.parent_message_id)
    }
    if (patch.route_json !== undefined) {
      fields.push('route_json = ?')
      values.push(patch.route_json)
    }
    if (patch.token_estimate !== undefined) {
      fields.push('token_estimate = ?')
      values.push(patch.token_estimate)
    }
    if (patch.metadata_json !== undefined) {
      fields.push('metadata_json = ?')
      values.push(patch.metadata_json)
    }

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

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

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

  /**
   * List messages for a session, optionally filtered by timestamp.
   */
  async list_by_session(
    session_id: SessionID,
    since?: ISOTimeString,
  ): Promise<MessageRecord[]> {
    if (since) {
      const stmt = this.db.prepare(
        'SELECT * FROM messages WHERE session_id = ? AND created_at > ? ORDER BY created_at ASC',
      )
      return stmt.all(session_id, since) as MessageRecord[]
    } else {
      const stmt = this.db.prepare(
        'SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC',
      )
      return stmt.all(session_id) as MessageRecord[]
    }
  }
}