#!/bin/bash

replication_user='repl'
replication_user_pass='pass'
replication_channel='group_replication_recovery'

wait-mysql-container-initialized() {
    service=$1

    # The MySQL containers start up the MySQL instance to initialize the
    # database. It is then brought down and started again. If we do a
    # connectivity check during the initialization step, we might
    # assume the database is ready to go prematurely. To prevent this, we
    # will check for the log message indicating that initialization is complete.
    local init_msg="MySQL init process done. Ready for start up."
    local max_init_checks=40
    local init_check_interval=3
    for ((i = 1; i <= max_init_checks; i++)); do
        log-info "waiting for ${service} database initialization (${i} of ${max_init_checks} max)..."
        if docker compose logs "${service}" | grep "${init_msg}"; then
            return 1
        fi
        sleep "${init_check_interval}"
    done

    return 0
}

wait-mysql-container-ready() {
    service=$1
    # Wait up to two minutes for mysql to be available. It should come up
    # pretty quick on developer machines but CI/CD is slow.
    local max_ready_checks=40
    local ready_check_interval=3
    for ((i = 1; i <= max_ready_checks; i++)); do
        log-info "waiting for ${service} to be ready (${i} of ${max_ready_checks} max)..."
        if docker compose exec -T "${service}" mysql -uspire -ptest -e "show databases;" >/dev/null; then
            return 1
            break
        fi
        sleep "${ready_check_interval}"
    done

    return 0
}

wait-mysql-container-initialized-and-ready() {
    service=$1
    if wait-mysql-container-initialized "${service}"; then
        fail-now "timed out waiting for ${service} database to be initialized"
    fi

    if wait-mysql-container-ready "${service}"; then
        fail-now "timed out waiting for ${service} to be ready"
    fi
}

get_mysql_root_password() {
  container_name=$1
  root_password=$(docker logs "${container_name}" 2>/dev/null \
    | grep 'GENERATED ROOT PASSWORD' \
    | sed 's/^.*GENERATED ROOT PASSWORD: \([^[:space:]]\{1,\}\)[[:space:]]*$/\1/')

  if [ -z "${root_password}" ]; then
    fail-now "Could not find root password for MySQL container ${container_name}. Container may not have initialized correctly."
  fi

  echo "${root_password}"
}

# Setup a primary server with group replication.
configure-readwrite-group-replication() {
    service=$1
    mysql_root_password=$2

    replication_script="
SET @@GLOBAL.group_replication_bootstrap_group=1;
CREATE USER '${replication_user}'@'%';
GRANT REPLICATION SLAVE ON *.* TO ${replication_user}@'%';
FLUSH PRIVILEGES;
CHANGE MASTER TO MASTER_USER='${replication_user}' FOR CHANNEL '${replication_channel}';
START GROUP_REPLICATION;
SET @@GLOBAL.group_replication_bootstrap_group=0;
SELECT * FROM performance_schema.replication_group_members;
"
    docker compose exec -T "${service}" mysql -uroot "-p$mysql_root_password" -e "${replication_script}" 
}

# Setup a replica server with group replication.
configure-readonly-group-replication() {
    service=$1
    mysql_root_password=$2

    replication_script="
CHANGE MASTER TO MASTER_USER='${replication_user}' FOR CHANNEL '${replication_channel}';
START GROUP_REPLICATION;
"
    docker compose exec -T "${service}" mysql -uroot "-p$mysql_root_password" -e "${replication_script}" 
}

test-mysql-replication() {
    service_prefix=$1
    readwrite_service_name="${service_prefix}-readwrite"
    readonly_service_name="${service_prefix}-readonly"

    docker-up "${readwrite_service_name}" "${readonly_service_name}"
    wait-mysql-container-initialized-and-ready "${readwrite_service_name}"
    wait-mysql-container-initialized-and-ready "${readonly_service_name}"

    readwrite_root_password=$(get_mysql_root_password "${readwrite_service_name}")
    readonly_root_password=$(get_mysql_root_password "${readonly_service_name}")

    configure-readwrite-group-replication "${readwrite_service_name}" "${readwrite_root_password}"
    configure-readonly-group-replication "${readonly_service_name}" "${readonly_root_password}"

    log-info "running tests against ${readwrite_service_name} and ${readonly_service_name}..."
    ./mysql-replicated.test || fail-now "tests failed"
    docker-stop "${readwrite_service_name}" "${readonly_service_name}"
}

test-mysql-replication mysql-8-0 || exit 1
