/**
 * ArtifactRepository - CRUD + list_by_entity, get_by_uri for artifacts table (§13)
 *
 * Implements Repository<ArtifactRecord, ArtifactInsert, ArtifactUpdate> per contracts §6.
 *
 * @module packages/runtime/src/storage/repositories/ArtifactRepository
 */

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

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

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

export type ArtifactType = 'log' | 'diff' | 'screenshot' | 'pcap' | 'report' | 'diagnostic' | 'bundle' | 'other'

export interface ArtifactRecord {
  id: ArtifactID
  session_id: SessionID
  type: ArtifactType
  uri: string
  path: string
  original_name?: string
  size_bytes?: number
  sha256?: string
  task_id?: TaskID
  agent_id?: AgentID
  tool_run_id?: ToolRunID
  command_run_id?: CommandRunID
  associated_entity_type?: string
  associated_entity_id?: string
  created_at: ISOTimeString
  metadata_json?: string
}

export type ArtifactInsert = Omit<ArtifactRecord, 'id'> & {
  id?: ArtifactID
}

export type ArtifactUpdate = Partial<Omit<ArtifactRecord, 'id' | 'session_id' | 'created_at'>>

// =============================================================================
// ArtifactRepository
// =============================================================================

export class ArtifactRepository implements Repository<ArtifactRecord, ArtifactInsert, ArtifactUpdate> {
  private db: DatabaseHandle

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

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

  /**
   * Insert a new artifact.
   */
  async insert(record: ArtifactInsert, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns
    assertEnumValues('artifacts', {
      type: record.type,
    })

    const stmt = (tx?.db ?? this.db).prepare(`
      INSERT INTO artifacts (
        id, session_id, type, uri, path, original_name,
        size_bytes, sha256,
        task_id, agent_id, tool_run_id, command_run_id,
        associated_entity_type, associated_entity_id,
        created_at, metadata_json
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `)

    stmt.run(
      record.id,
      record.session_id,
      record.type,
      record.uri,
      record.path,
      record.original_name ?? null,
      record.size_bytes ?? null,
      record.sha256 ?? null,
      record.task_id ?? null,
      record.agent_id ?? null,
      record.tool_run_id ?? null,
      record.command_run_id ?? null,
      record.associated_entity_type ?? null,
      record.associated_entity_id ?? null,
      record.created_at,
      record.metadata_json ?? null,
    )
  }

  /**
   * Update an existing artifact.
   */
  async update(id: ArtifactID, patch: ArtifactUpdate, tx?: TransactionHandle): Promise<void> {
    // Validate enum columns if present
    if (patch.type !== undefined) {
      assertEnumValues('artifacts', { type: patch.type })
    }

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

    if (patch.type !== undefined) {
      fields.push('type = ?')
      values.push(patch.type)
    }
    if (patch.uri !== undefined) {
      fields.push('uri = ?')
      values.push(patch.uri)
    }
    if (patch.path !== undefined) {
      fields.push('path = ?')
      values.push(patch.path)
    }
    if (patch.original_name !== undefined) {
      fields.push('original_name = ?')
      values.push(patch.original_name)
    }
    if (patch.size_bytes !== undefined) {
      fields.push('size_bytes = ?')
      values.push(patch.size_bytes)
    }
    if (patch.sha256 !== undefined) {
      fields.push('sha256 = ?')
      values.push(patch.sha256)
    }
    if (patch.associated_entity_type !== undefined) {
      fields.push('associated_entity_type = ?')
      values.push(patch.associated_entity_type)
    }
    if (patch.associated_entity_id !== undefined) {
      fields.push('associated_entity_id = ?')
      values.push(patch.associated_entity_id)
    }
    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 artifacts SET ${fields.join(', ')} WHERE id = ?`)
    stmt.run(...values)
  }

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

  /**
   * List artifacts for a specific entity (by associated_entity_type and associated_entity_id).
   */
  async list_by_entity(entity_type: string, entity_id: string): Promise<ArtifactRecord[]> {
    const stmt = this.db.prepare(
      'SELECT * FROM artifacts WHERE associated_entity_type = ? AND associated_entity_id = ? ORDER BY created_at DESC',
    )
    return stmt.all(entity_type, entity_id) as ArtifactRecord[]
  }

  /**
   * Get an artifact by its URI.
   */
  async get_by_uri(uri: string): Promise<ArtifactRecord | undefined> {
    const stmt = this.db.prepare('SELECT * FROM artifacts WHERE uri = ?')
    const row = stmt.get(uri) as ArtifactRecord | undefined
    return row
  }
}