from __future__ import annotations

import os
import time

from fixtures.log_helper import log
from fixtures.neon_fixtures import NeonEnv
from fixtures.utils import query_scalar


#
# Test compute node start after clog truncation
#
def test_clog_truncate(neon_simple_env: NeonEnv):
    env = neon_simple_env

    # set aggressive autovacuum to make sure that truncation will happen
    config = [
        "autovacuum_max_workers=10",
        "autovacuum_vacuum_threshold=0",
        "autovacuum_vacuum_insert_threshold=0",
        "autovacuum_vacuum_cost_delay=0",
        "autovacuum_vacuum_cost_limit=10000",
        "autovacuum_naptime =1s",
        "autovacuum_freeze_max_age=100000",
    ]

    endpoint = env.endpoints.create_start("main", config_lines=config)

    # Install extension containing function needed for test
    endpoint.safe_psql("CREATE EXTENSION neon_test_utils")

    # Consume many xids to advance clog
    with endpoint.cursor() as cur:
        cur.execute("select test_consume_xids(1000*1000*10);")
        log.info("xids consumed")

        # call a checkpoint to trigger TruncateSubtrans
        cur.execute("CHECKPOINT;")

        # ensure WAL flush
        cur.execute("select txid_current()")
        log.info(cur.fetchone())

    # wait for autovacuum to truncate the pg_xact
    # XXX Is it worth to add a timeout here?
    pg_xact_0000_path = os.path.join(endpoint.pg_xact_dir_path(), "0000")
    log.info(f"pg_xact_0000_path = {pg_xact_0000_path}")

    while os.path.isfile(pg_xact_0000_path):
        log.info(f"file exists. wait for truncation: {pg_xact_0000_path=}")
        time.sleep(5)

    # checkpoint to advance latest lsn
    with endpoint.cursor() as cur:
        cur.execute("CHECKPOINT;")
        lsn_after_truncation = query_scalar(cur, "select pg_current_wal_insert_lsn()")

    # create new branch after clog truncation and start a compute node on it
    log.info(f"create branch at lsn_after_truncation {lsn_after_truncation}")
    env.create_branch(
        "test_clog_truncate_new",
        ancestor_branch_name="main",
        ancestor_start_lsn=lsn_after_truncation,
    )
    endpoint2 = env.endpoints.create_start("test_clog_truncate_new")

    # check that new node doesn't contain truncated segment
    pg_xact_0000_path_new = os.path.join(endpoint2.pg_xact_dir_path(), "0000")
    log.info(f"pg_xact_0000_path_new = {pg_xact_0000_path_new}")
    assert os.path.isfile(pg_xact_0000_path_new) is False
