/**
 * assertEnum - Validates closed-enum TEXT columns per db-schema §21.
 *
 * Validates every closed-enum TEXT column (18 rows per db-schema §21).
 * Throws AirError{kind:"system_error"} on violation.
 *
 * @module packages/runtime/src/storage/assertEnum
 */

import type { AirError } from '@aircoding/contracts'

// =============================================================================
// §21 Closed Enum Inventory - Column definitions
// =============================================================================

type EnumValues = readonly string[]

interface TableEnums {
  [column: string]: EnumValues
}

interface EnumColumnMap {
  [table: string]: TableEnums
}

export const ENUM_COLUMNS: EnumColumnMap = {
  // sessions (§3)
  sessions: {
    status: ['active', 'archived', 'deleted'],
  },
  // messages (§4)
  messages: {
    role: ['user', 'assistant', 'system', 'tool'],
    canonical_format: ['anthropic'],
  },
  // tasks (§7)
  tasks: {
    type: ['execute', 'review', 'debug', 'compact', 'mine_experience', 'docs'],
    status: ['pending', 'running', 'completed', 'failed', 'blocked', 'cancelled', 'interrupted'],
  },
  // task_dependencies (§8)
  task_dependencies: {
    dependency_type: ['hard', 'soft', 'conflict', 'serialization'],
  },
  // task_attempts (§9)
  task_attempts: {
    status: ['pending', 'running', 'completed', 'failed', 'cancelled'],
  },
  // agents (§10)
  agents: {
    status: ['starting', 'running', 'completed', 'failed', 'lost', 'cancelled'],
  },
  // tool_runs (§11)
  tool_runs: {
    status: ['running', 'ok', 'error', 'cancelled'],
  },
  // artifacts (§13)
  artifacts: {
    type: ['log', 'diff', 'screenshot', 'pcap', 'report', 'diagnostic', 'bundle', 'other'],
  },
  // diagnostics (§14)
  diagnostics: {
    severity: ['error', 'warning', 'info', 'hint'],
  },
  // evidence_refs (§15)
  evidence_refs: {
    kind: ['build_output', 'test_output', 'log', 'screenshot', 'diff', 'metric', 'other'],
  },
  // workspaces (§16)
  workspaces: {
    strategy: ['main', 'worktree', 'isolated_copy'],
    status: ['active', 'merged', 'conflicted', 'abandoned', 'cleaned'],
  },
  // summaries (§17)
  summaries: {
    type: ['compaction', 'checkpoint', 'review', 'other'],
  },
  // message_drafts (§5)
  message_drafts: {
    status: ['streaming', 'interrupted', 'error'],
  },
  // learned_memories (project-level DB §20.2)
  learned_memories: {
    memory_type: ['project_rule', 'toolchain_rule', 'skill_update', 'debug_experience'],
    status: ['candidate', 'promoted', 'archived', 'rejected'],
  },
}

// Type for table names
export type EnumTableName = keyof typeof ENUM_COLUMNS

// =============================================================================
// Error creation helpers
// =============================================================================

/**
 * Creates an AirError with kind "system_error" for enum validation failures.
 */
function createEnumViolationError(
  table: string,
  column: string,
  value: unknown,
): AirError {
  const validValues = ENUM_COLUMNS[table]?.[column]?.join(', ') || 'unknown values'
  return {
    error_id: `assert_enum_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
    kind: 'system_error',
    severity: 'error',
    message: `Invalid value for closed enum column ${table}.${column}`,
    detail: `Expected one of ${validValues}, got: ${JSON.stringify(value)}`,
    retryability: 'not_retryable',
    semantic_signature: `assert_enum:invalid_value:${table}.${column}`,
  }
}

// =============================================================================
// Validation functions
// =============================================================================

/**
 * Validates a single enum column value.
 * Throws AirError{kind:"system_error"} on violation.
 *
 * @param table - The table name (e.g., 'sessions', 'tasks')
 * @param column - The column name (e.g., 'status', 'type')
 * @param value - The value to validate (can be null/undefined - not validated)
 */
export function assertEnumValue(
  table: string,
  column: string,
  value: unknown,
): void {
  // Skip validation for null/undefined values (NOT NULL columns won't have these)
  if (value === null || value === undefined) {
    return
  }

  const tableEnums = ENUM_COLUMNS[table]
  if (!tableEnums) {
    // Unknown table - allow (may be new tables added after this file)
    return
  }

  const validValues = tableEnums[column]
  if (!validValues) {
    // Unknown column - allow (may be new columns)
    return
  }

  const stringValue = String(value)
  if (!validValues.includes(stringValue)) {
    const error = createEnumViolationError(table, column, value)
    throw error
  }
}

/**
 * Validates multiple enum column values at once.
 * Throws on first violation.
 *
 * @param table - The table name
 * @param columns - Record of column names to their values
 */
export function assertEnumValues(
  table: string,
  columns: Record<string, unknown>,
): void {
  for (const [column, value] of Object.entries(columns)) {
    assertEnumValue(table, column, value)
  }
}

/**
 * Validates a record against its expected enum columns.
 * Throws AirError{kind:"system_error"} on any violation.
 *
 * @param table - The table name
 * @param record - Record with column-value pairs to validate
 * @param columnsToValidate - Which columns to validate (defaults to all known enum columns)
 */
export function assertEnumRecord(
  table: string,
  record: Record<string, unknown>,
  columnsToValidate?: string[],
): void {
  const tableEnums = ENUM_COLUMNS[table]
  if (!tableEnums) {
    return // Unknown table
  }

  const columns = columnsToValidate || Object.keys(tableEnums)
  for (const column of columns) {
    if (column in record) {
      assertEnumValue(table, column, record[column])
    }
  }
}

// =============================================================================
// Utility functions for testing / introspection
// =============================================================================

/**
 * Returns true if the value is valid for the given enum column.
 */
export function isValidEnumValue(
  table: string,
  column: string,
  value: unknown,
): boolean {
  if (value === null || value === undefined) {
    return true
  }

  const tableEnums = ENUM_COLUMNS[table]
  if (!tableEnums) {
    return true // Unknown table - allow
  }

  const validValues = tableEnums[column]
  if (!validValues) {
    return true // Unknown column - allow
  }

  return validValues.includes(String(value))
}

/**
 * Returns all valid values for a given enum column.
 */
export function getValidEnumValues(
  table: string,
  column: string,
): readonly string[] {
  const tableEnums = ENUM_COLUMNS[table]
  if (!tableEnums) {
    return []
  }

  return tableEnums[column] || []
}

/**
 * Returns all known enum tables.
 */
export function getEnumTables(): string[] {
  return Object.keys(ENUM_COLUMNS)
}