///|
fn print_help() -> Unit {
  println(@lib.banner())
  println("Read-only SQLite database and WAL analysis")
  println("")
  println("Usage:")
  println("  pagelens inspect  [--json]")
  println("  pagelens wal  [--json]")
  println("  pagelens snapshot  [--wal ] [--json]")
  println("  pagelens check  [--wal ] [--json]")
  println("")
  println("Options:")
  println("  --json        emit one machine-readable JSON value")
  println("  --wal   use an explicit WAL path (default: -wal)")
  println("  -h, --help    show this help")
  println("  --version     show the PageLens version")
  println("")
  println("PageLens never executes SQL and never writes to the input files.")
}

///|
fn has_flag(args : Array[String], flag : String) -> Bool {
  for arg in args {
    if arg == flag {
      return true
    }
  }
  false
}

///|
fn option_value(args : Array[String], option : String) -> String? {
  for index = 0; index < args.length(); index = index + 1 {
    if args[index] == option && index + 1 < args.length() {
      return Some(args[index + 1])
    }
  }
  None
}

///|
fn read_file(path : String) -> Bytes? {
  Some(@fs.read_file_to_bytes(path)) catch {
    @fs.IOError(message) => {
      println("PageLens error: cannot read '" + path + "': " + message)
      None
    }
  }
}

///|
fn read_optional_wal(path : String) -> Bytes? {
  if @fs.path_exists(path) {
    read_file(path)
  } else {
    None
  }
}

///|
fn run_inspect(args : Array[String], json : Bool) -> Int {
  guard args.length() >= 3 else {
    println("PageLens error: inspect requires a database path")
    print_help()
    return 2
  }
  guard read_file(args[2]) is Some(data) else { return 2 }
  try {
    let inspection = @lib.inspect_database(data)
    println(
      if json {
        @lib.render_database_json(inspection)
      } else {
        @lib.render_database_text(inspection)
      },
    )
    0
  } catch {
    error => {
      println("PageLens error: " + error.message())
      2
    }
  }
}

///|
fn run_wal(args : Array[String], json : Bool) -> Int {
  guard args.length() >= 3 else {
    println("PageLens error: wal requires a WAL file path")
    print_help()
    return 2
  }
  guard read_file(args[2]) is Some(data) else { return 2 }
  try {
    let wal = @lib.parse_wal(data)
    println(
      if json {
        @lib.render_wal_json(wal)
      } else {
        @lib.render_wal_text(wal)
      },
    )
    if wal.diagnostics.length() == 0 {
      0
    } else {
      1
    }
  } catch {
    error => {
      println("PageLens error: " + error.message())
      2
    }
  }
}

///|
fn selected_wal_path(args : Array[String], database_path : String) -> String {
  match option_value(args, "--wal") {
    Some(path) => path
    None => database_path + "-wal"
  }
}

///|
fn run_snapshot(args : Array[String], json : Bool) -> Int {
  guard args.length() >= 3 else {
    println("PageLens error: snapshot requires a database path")
    print_help()
    return 2
  }
  let database_path = args[2]
  let wal_path = selected_wal_path(args, database_path)
  guard read_file(database_path) is Some(database_data) else { return 2 }
  guard read_file(wal_path) is Some(wal_data) else { return 2 }
  try {
    let database = @lib.DatabaseImage::open(database_data)
    let wal = @lib.parse_wal(wal_data)
    let snapshot = @lib.open_snapshot(database, wal)
    println(
      if json {
        @lib.render_snapshot_json(snapshot)
      } else {
        @lib.render_snapshot_text(snapshot)
      },
    )
    0
  } catch {
    error => {
      println("PageLens error: " + error.message())
      2
    }
  }
}

///|
fn run_check(args : Array[String], json : Bool) -> Int {
  guard args.length() >= 3 else {
    println("PageLens error: check requires a database path")
    print_help()
    return 2
  }
  let database_path = args[2]
  guard read_file(database_path) is Some(database_data) else { return 2 }
  let wal_path = selected_wal_path(args, database_path)
  let wal_data = read_optional_wal(wal_path)
  let report = match wal_data {
    Some(bytes) => @lib.check_database(database_data, wal_data=bytes)
    None => @lib.check_database(database_data)
  }
  println(
    if json {
      @lib.render_integrity_json(report)
    } else {
      @lib.render_integrity_text(report)
    },
  )
  if report.is_ok() {
    0
  } else {
    1
  }
}

///|
fn run(args : Array[String]) -> Int {
  if args.length() <= 1 || has_flag(args, "--help") || has_flag(args, "-h") {
    print_help()
    return 0
  }
  if args[1] == "--version" {
    println(@lib.version)
    return 0
  }
  let json = has_flag(args, "--json")
  match args[1] {
    "inspect" => run_inspect(args, json)
    "wal" => run_wal(args, json)
    "snapshot" => run_snapshot(args, json)
    "check" => run_check(args, json)
    command => {
      println("PageLens error: unknown command '" + command + "'")
      print_help()
      2
    }
  }
}

///|
fn main {
  @sys.exit(run(@env.args()))
}