// 存量库 schema 迁移策略:init_schema = IF NOT EXISTS 幂等建;
// 列变更策略 = user_version 逐版迁移,首个 schema 变更需求
// 触发实现;schema_version 列 = 事件 schema 版本(只预留)。

///|
/// SQLite 实现
pub struct SQLiteEventStore[E] {
  conn : @sqlite3.Connection
  serialize : (E) -> Bytes
  deserialize : (Bytes) -> E raise
  get_type_name : (E) -> String
}

// impl[E] SQLiteEventStore[E] {

///|

///|
/// 错误回原族(DuplicateVersion 原样上浮可分辨)
fn event_store_error_of(e : Error) -> EventStoreError {
  match e {
    EventStoreError::DuplicateVersion(v) => DuplicateVersion(v)
    EventStoreError::SerializationError(m) => SerializationError(m)
    _ => StorageError(e.to_string())
  }
}

///|
pub fn[E] SQLiteEventStore::new(
  db_path : String,
  serialize : (E) -> Bytes,
  deserialize : (Bytes) -> E raise,
  get_type_name : (E) -> String,
) -> SQLiteEventStore[E] raise {
  let conn = @sqlite3.Connection::open(db_path)
  let store = { conn, serialize, deserialize, get_type_name, }
  store.init_schema()
  store
}

///|
pub fn[E] SQLiteEventStore::init_schema(
  self : SQLiteEventStore[E],
) -> Unit raise {
  // 注意:prepare 只编译第一条语句,多条 DDL 必须逐条执行
  let create_table =
    #| CREATE TABLE IF NOT EXISTS event_stream (
    #|  id INTEGER PRIMARY KEY AUTOINCREMENT,
    #|  aggregate_id TEXT NOT NULL,
    #|   event_id TEXT NOT NULL,
    #|   event_type TEXT NOT NULL,
    #|   event_data BLOB NOT NULL,
    #|   version INTEGER NOT NULL,
    #|   correlation_id TEXT,
    #|   causation_id TEXT,
    #|   user_id TEXT,
    #|   timestamp INTEGER NOT NULL,
    #|   metadata TEXT,
    #|   schema_version INTEGER NOT NULL DEFAULT 1,
    #|   UNIQUE(aggregate_id, version)
    #|  )
  let idx_aggregate =
    #| CREATE INDEX IF NOT EXISTS idx_aggregate_version
    #| ON event_stream(aggregate_id, version)
  let idx_correlation =
    #| CREATE INDEX IF NOT EXISTS idx_correlation
    #| ON event_stream(correlation_id)

  for sql in [create_table, idx_aggregate, idx_correlation] {
    let stmt = self.conn.prepare(sql)
    ignore(stmt.step())
    stmt.finalize()
  }
}

///|
pub fn[E] SQLiteEventStore::append(
  self : SQLiteEventStore[E],
  aggregate_id : String,
  events : Array[@event.EventMessage[String, E]],
) -> Unit raise {
  // FR-MC-1 乐观并发守门:批次首事件版本必须 = 当前版本 + 1,否则 DuplicateVersion(拒写不落脏)
  if events.length() > 0 {
    let expected = events[0].aggregate_version
    let current = self.current_version(aggregate_id)
    if expected != current + 1 {
      raise EventStoreError::DuplicateVersion(current)
    }
  }
  let insert_sql =
    #|  INSERT INTO event_stream 
    #|  (aggregate_id, event_id, event_type, event_data, version, correlation_id, causation_id, user_id, timestamp, metadata, schema_version)
    #|  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

  let begin_stmt = self.conn.prepare("BEGIN TRANSACTION")
  ignore(begin_stmt.step())
  begin_stmt.finalize()
  // FR-MC-1/4:任一 INSERT 失败(含 UNIQUE 冲突竞态)⇒ ROLLBACK 后原样上抛,禁静默脏写/悬挂事务
  // (errdefer 形:错误路径自动回滚并重抛原错,消 [0092] fragile_catch_all)
  errdefer {
    let rollback_stmt = self.conn.prepare("ROLLBACK")
    ignore(rollback_stmt.step())
    rollback_stmt.finalize()
  }
  for event in events {
    let stmt = self.conn.prepare(insert_sql)
    stmt.bind(index=1, aggregate_id)
    stmt.bind(index=2, event.event_id)
    stmt.bind(index=3, (self.get_type_name)(event.event))
    stmt.bind(index=4, (self.serialize)(event.event))
    stmt.bind(index=5, event.aggregate_version)
    stmt.bind(index=6, event.correlation_id)
    stmt.bind(index=7, event.causation_id)
    // user_id 列保留在 schema 里;@event.EventMessage 不携带 user_id(命令面才有),落空串
    // user_id 链:CommandMessage.user_id → @event.EventMessage → 落列
    stmt.bind(
      index=8,
      match event.user_id {
        Some(u) => u
        None => ""
      },
    )
    stmt.bind(index=9, event.timestamp)
    stmt.bind(index=10, event.metadata.stringify())
    stmt.bind(index=11, event.schema_version)

    ignore(stmt.step())
    stmt.finalize()
  }

  let commit_stmt = self.conn.prepare("COMMIT")
  ignore(commit_stmt.step())
  commit_stmt.finalize()
}

///|
pub fn[E] SQLiteEventStore::load(
  self : SQLiteEventStore[E],
  aggregate_id : String,
  after_version : Int32,
) -> Array[@event.EventMessage[String, E]] raise {
  let sql =
    #| SELECT event_data, version, event_id, correlation_id, causation_id, timestamp, metadata, user_id, schema_version
    #| FROM event_stream
    #| WHERE aggregate_id = ? AND version > ?
    #| ORDER BY version ASC

  let stmt = self.conn.prepare(sql)
  stmt.bind(index=1, aggregate_id)
  stmt.bind(index=2, after_version.to_int64())

  let events : Array[@event.EventMessage[String, E]] = []

  while stmt.step() {
    let data : Bytes = stmt.column(index=0)
    let version : Int64 = stmt.column(index=1)
    let event_id : String = stmt.column(index=2)
    let correlation_id : String = stmt.column(index=3)
    let causation_id : String = stmt.column(index=4)
    let timestamp : Int64 = stmt.column(index=5)
    let metadata_str : String = stmt.column(index=6)
    let user_id : String = stmt.column(index=7)
    let schema_version : Int64 = stmt.column(index=8)

    let event = (self.deserialize)(data)
    let metadata = @json.parse(metadata_str)

    events.push(@event.EventMessage::{
      event,
      event_id,
      aggregate_id,
      aggregate_version: version.to_int(),
      correlation_id,
      causation_id,
      timestamp,
      metadata,
      schema_version: schema_version.to_int(),
      user_id: if user_id == "" {
        None
      } else {
        Some(user_id)
      },
    })
  }

  stmt.finalize()
  events
}

///|
pub fn[E] SQLiteEventStore::current_version(
  self : SQLiteEventStore[E],
  aggregate_id : String,
) -> Int32 raise {
  let sql =
    #|SELECT COALESCE(MAX(version), 0) FROM event_stream WHERE aggregate_id = ?
  let stmt = self.conn.prepare(sql)
  stmt.bind(index=1, aggregate_id)

  let version = if stmt.step() {
    let v : Int64 = stmt.column(index=0)
    stmt.finalize()
    v.to_int()
  } else {
    stmt.finalize()
    0
  }

  version
}

///|
/// EventStore 记录面适配器:SQLite 实现接入记录型函数面——
/// 错误穿透不抹平(EventStoreError 变体原样上浮,DuplicateVersion 可分辨)。
/// 聚合 id 域 = String(event_stream.aggregate_id 列 TEXT)。
pub fn[E] SQLiteEventStore::to_event_store(
  self : SQLiteEventStore[E],
) -> EventStore[String, E] {
  {
    append: (_store, aggregate_id, events) => {
      self.append(aggregate_id, events) catch {
        e => raise event_store_error_of(e)
      }
    },
    load: (_store, aggregate_id, after_version) => {
      self.load(aggregate_id, after_version) catch {
        e => raise event_store_error_of(e)
      }
    },
    current_version: (_store, aggregate_id) => {
      self.current_version(aggregate_id) catch {
        e => raise event_store_error_of(e)
      }
    },
  }
}