use "../std/pg/transaction"
use "../std/pg/uri"

fn live_scope_insert(handle: Number, commit: Boolean, fail_query: Boolean) -> Result[Boolean, Text]:
    let scope: PgTransactionScope = pg_scope_begin(handle, 5000)?
    defer pg_scope_cleanup(scope)
    let values: PgParameters = pg_parameters_new()
    let encoded: Boolean = pg_parameters_add_text(values, "scoped-row")?
    let inserted: PgQueryResult = pg_scope_query(scope, "INSERT INTO pg_temp.yaza_transaction_live_smoke(label) VALUES ($1)", values)?
    if fail_query:
        let failed: PgQueryResult = pg_scope_query(scope, "SELECT 1 / 0", pg_parameters_new())?
    if commit:
        return pg_scope_commit(scope)
    return ok(false)

start:
    let database_url: Text = env_get("DATABASE_URL")
    assert database_url != ""
    assert pg_uri_configure_environment(database_url) == 0

    let host: Text = env_get("PGHOST")
    let port: Number = number_parse(env_get("PGPORT"))
    let username: Text = env_get("PGUSER")
    let database: Text = env_get("PGDATABASE")
    let password: Text = env_get("PGPASSWORD")
    let connected: Result[Number, Text] = pg_connect(host, port, username, database, password, 5000)
    assert result_is_ok(connected)
    let handle: Number = result_unwrap(connected)

    let began_for_table: Result[Boolean, Text] = pg_transaction_begin(handle, 5000)
    assert result_is_ok(began_for_table)
    let created: Result[PgQueryResult, Text] = pg_transaction_query(handle, "CREATE TEMP TABLE yaza_transaction_live_smoke (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, label TEXT NOT NULL) ON COMMIT PRESERVE ROWS", pg_parameters_new(), 5000)
    assert result_is_ok(created)
    let table_committed: Result[Boolean, Text] = pg_transaction_commit(handle, 5000)
    assert result_is_ok(table_committed)
    assert result_unwrap(table_committed)

    let began: Result[Boolean, Text] = pg_transaction_begin(handle, 5000)
    assert result_is_ok(began)
    let duplicate_begin: Result[Boolean, Text] = pg_transaction_begin(handle, 5000)
    assert not result_is_ok(duplicate_begin)

    let keep_parameters: PgParameters = pg_parameters_new()
    assert result_is_ok(pg_parameters_add_text(keep_parameters, "committed"))
    let keep_row: Result[PgQueryResult, Text] = pg_transaction_query(handle, "INSERT INTO pg_temp.yaza_transaction_live_smoke(label) VALUES ($1)", keep_parameters, 5000)
    assert result_is_ok(keep_row)

    let saved: Result[Boolean, Text] = pg_transaction_savepoint(handle, "rollback_marker", 5000)
    assert result_is_ok(saved)
    let discard_parameters: PgParameters = pg_parameters_new()
    assert result_is_ok(pg_parameters_add_text(discard_parameters, "discarded-at-savepoint"))
    let discard_row: Result[PgQueryResult, Text] = pg_transaction_query(handle, "INSERT INTO pg_temp.yaza_transaction_live_smoke(label) VALUES ($1)", discard_parameters, 5000)
    assert result_is_ok(discard_row)
    let rolled_to_savepoint: Result[Boolean, Text] = pg_transaction_rollback_to(handle, "rollback_marker", 5000)
    assert result_is_ok(rolled_to_savepoint)
    let released: Result[Boolean, Text] = pg_transaction_release_savepoint(handle, "rollback_marker", 5000)
    assert result_is_ok(released)
    let committed: Result[Boolean, Text] = pg_transaction_commit(handle, 5000)
    assert result_is_ok(committed)
    assert result_unwrap(committed)

    let count_after_savepoint: Result[PgQueryResult, Text] = pg_query(handle, "SELECT count(*)::bigint AS count FROM pg_temp.yaza_transaction_live_smoke", 5000)
    assert result_is_ok(count_after_savepoint)
    let saved_count: Result[Option[Number], Text] = pg_query_number(result_unwrap(count_after_savepoint), 0, 0)
    assert result_is_ok(saved_count)
    assert option_is_some(result_unwrap(saved_count))
    assert option_unwrap(result_unwrap(saved_count)) == 1

    let began_for_rollback: Result[Boolean, Text] = pg_transaction_begin(handle, 5000)
    assert result_is_ok(began_for_rollback)
    let rollback_parameters: PgParameters = pg_parameters_new()
    assert result_is_ok(pg_parameters_add_text(rollback_parameters, "discarded-at-rollback"))
    let rollback_row: Result[PgQueryResult, Text] = pg_transaction_query(handle, "INSERT INTO pg_temp.yaza_transaction_live_smoke(label) VALUES ($1)", rollback_parameters, 5000)
    assert result_is_ok(rollback_row)
    let rolled_back: Result[Boolean, Text] = pg_transaction_rollback(handle, 5000)
    assert result_is_ok(rolled_back)
    assert result_unwrap(rolled_back)

    let began_for_failure: Result[Boolean, Text] = pg_transaction_begin(handle, 5000)
    assert result_is_ok(began_for_failure)
    let failed_query: Result[PgQueryResult, Text] = pg_transaction_query(handle, "SELECT 1 / 0", pg_parameters_new(), 5000)
    assert not result_is_ok(failed_query)
    let rejected_while_failed: Result[PgQueryResult, Text] = pg_transaction_query(handle, "SELECT 1", pg_parameters_new(), 5000)
    assert not result_is_ok(rejected_while_failed)
    let recovered: Result[Boolean, Text] = pg_transaction_rollback(handle, 5000)
    assert result_is_ok(recovered)
    assert result_unwrap(recovered)

    let final_count: Result[PgQueryResult, Text] = pg_query(handle, "SELECT count(*)::bigint AS count FROM pg_temp.yaza_transaction_live_smoke", 5000)
    assert result_is_ok(final_count)
    let final_value: Result[Option[Number], Text] = pg_query_number(result_unwrap(final_count), 0, 0)
    assert result_is_ok(final_value)
    assert option_is_some(result_unwrap(final_value))
    assert option_unwrap(result_unwrap(final_value)) == 1

    assert result_is_ok(live_scope_insert(handle, false, false))
    assert not result_is_ok(live_scope_insert(handle, false, true))
    assert result_is_ok(live_scope_insert(handle, true, false))
    let scope_count: Result[PgQueryResult, Text] = pg_query(handle, "SELECT count(*)::bigint AS count FROM pg_temp.yaza_transaction_live_smoke", 5000)
    assert result_is_ok(scope_count)
    let scope_value: Result[Option[Number], Text] = pg_query_number(result_unwrap(scope_count), 0, 0)
    assert result_is_ok(scope_value)
    assert option_unwrap(result_unwrap(scope_value)) == 2

    assert pg_disconnect(handle) == 0
    say "Live PostgreSQL transaction and savepoint checks passed."
