# List available recipes
list:
    @just --list --justfile {{ source_file() }}

fmt_flags := '--all'

# Run formatter
fmt *FLAGS:
    cargo +nightly fmt {{ fmt_flags }} {{ FLAGS }}

check_flags := '--all-targets'
v2_lints:= '-D warnings -Aunused -Aclippy::todo -Aclippy::diverging_sub_expression'

alias c := check

# Check compilation of Rust code and catch common mistakes
# We cannot run --all-features because v1 and v2 are mutually exclusive features
# Create a list of features by excluding certain features
# redis_interface_backend: "redis-rs" (default) or "fred"
clippy redis_interface_backend="redis-rs" *FLAGS:
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ ( .workspace_members | sort ) as $package_ids # Store workspace crate package IDs in `package_ids` array
            | .packages[] | select( IN(.id; $package_ids[]) ) | .features | keys[] ] | unique # Select all unique features from all workspace crates
            | del( .[] | select( any( . ; test("(([a-z_]+)_)?v2") ) ) ) # Exclude v2 features
            | del( .[] | select( . == ("default", "fred", "redis-rs") ) ) # Exclude default and both backend flags
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo clippy {{ check_flags }} --no-default-features --features "${FEATURES}" {{ FLAGS }}
    set +x

# redis_interface_backend: "redis-rs" (default) or "fred"
clippy_v2 redis_interface_backend="redis-rs" *FLAGS:
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ ( .workspace_members | sort ) as $package_ids # Store workspace crate package IDs in `package_ids` array
            | .packages[] | select( IN(.id; $package_ids[]) ) | .features | keys[] ] | unique # Select all unique features from all workspace crates
            | del( .[] | select( . == ("default", "v1", "deja", "fred", "redis-rs") ) ) # Exclude default, v1, deja (v1-only feature), and both backend flags
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo clippy {{ check_flags }} --no-default-features --features "${FEATURES}" -- {{ v2_lints }} {{ FLAGS }}
    set +x

# redis_interface_backend: "redis-rs" (default) or "fred"
check_v2 redis_interface_backend="redis-rs" *FLAGS:
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ ( .workspace_members | sort ) as $package_ids # Store workspace crate package IDs in `package_ids` array
            | .packages[] | select( IN(.id; $package_ids[]) ) | .features | keys[] ] | unique # Select all unique features from all workspace crates
            | del( .[] | select( . == ("default", "v1", "deja", "fred", "redis-rs") ) ) # Exclude default, v1, deja (v1-only feature), and both backend flags
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo check {{ check_flags }} --no-default-features --features "${FEATURES}" -- {{ FLAGS }}
    set +x

# redis_interface_backend: "redis-rs" (default) or "fred"
build_v2 redis_interface_backend="redis-rs" *FLAGS:
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ .packages[] | select(.name == "router") | .features | keys[] # Obtain features of `router` package
            | select( any( . ; test("(([a-z_]+)_)?v2") ) ) ] # Select v2 features
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo build --package router --bin router --no-default-features --features "${FEATURES}" {{ FLAGS }}
    set +x

# redis_interface_backend: "redis-rs" (default) or "fred"
run_v2 redis_interface_backend="redis-rs":
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ .packages[] | select(.name == "router") | .features | keys[] # Obtain features of `router` package
            | select( any( . ; test("(([a-z_]+)_)?v2") ) ) ] # Select v2 features
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo run --package router --no-default-features --features "${FEATURES}"
    set +x

# redis_interface_backend: "redis-rs" (default) or "fred"
check redis_interface_backend="redis-rs" *FLAGS:
    #! /usr/bin/env bash
    set -euo pipefail

    FEATURES="$(cargo metadata --all-features --format-version 1 --no-deps | \
        jq -r '
            [ ( .workspace_members | sort ) as $package_ids # Store workspace crate package IDs in `package_ids` array
            | .packages[] | select( IN(.id; $package_ids[]) ) | .features | keys[] ] | unique # Select all unique features from all workspace crates
            | del( .[] | select( any( . ; test("(([a-z_]+)_)?v2") ) ) ) # Exclude v2 features
            | del( .[] | select( . == ("default", "fred", "redis-rs") ) ) # Exclude default and both backend flags
            | join(",") # Construct a comma-separated string of features for passing to `cargo`
    ')"
    FEATURES="${FEATURES},{{ redis_interface_backend }}"

    set -x
    cargo check {{ check_flags }} --no-default-features --features "${FEATURES}" {{ FLAGS }}
    set +x

alias cl := clippy

# Build binaries
build *FLAGS:
    cargo build {{ FLAGS }}

alias b := build

# Build release (optimized) binaries
build-release *FLAGS:
    cargo build --release --features release {{ FLAGS }}

alias br := build-release

# Run server
run *FLAGS:
    cargo run {{ FLAGS }}

alias r := run

doc_flags := '--all-features --all-targets --exclude-features "v2"'

# Generate documentation
doc *FLAGS:
    cargo doc {{ doc_flags }} {{ FLAGS }}

alias d := doc

# Build the `euclid_wasm` crate
euclid-wasm features='dummy_connector':
    wasm-pack build \
        --target web \
        --out-dir {{ source_directory() }}/wasm \
        --out-name euclid \
        {{ source_directory() }}/crates/euclid_wasm \
        --features '{{ features }}'

# Build the `payment_link` crate as WASM
# Usage: just payment-link-wasm [features=''] [version='v1'] [environment='development']
payment-link-wasm features='' version='v1' environment='development':
    ENVIRONMENT={{ environment }} ~/.cargo/bin/wasm-pack build \
        --target web \
        --out-dir {{ source_directory() }}/wasm_output \
        --out-name payment_link \
        {{ source_directory() }}/crates/payment_link \
        -- --features wasm,{{ version }},{{ features }}

# Run pre-commit checks
precommit: fmt clippy

# Run drainer with v1 features enabled
drainer redis_interface_backend="redis-rs" *FLAGS:
    DRAINER__SERVER__PORT=8084 cargo run --package drainer --no-default-features --features "{{ redis_interface_backend }},v1" {{ FLAGS }}

# Run drainer with v2 features enabled
drainer_v2 redis_interface_backend="redis-rs" *FLAGS:
    DRAINER__SERVER__PORT=8084 cargo run --package drainer --no-default-features --features "{{ redis_interface_backend }},v2" {{ FLAGS }}

# Use the env variables if present, or fallback to default values

db_user := env_var_or_default('DB_USER', 'db_user')
db_password := env_var_or_default('DB_PASSWORD', 'db_pass')
db_host := env_var_or_default('DB_HOST', 'localhost')
db_port := env_var_or_default('DB_PORT', '5432')
db_name := env_var_or_default('DB_NAME', 'hyperswitch_db')
default_db_url := 'postgresql://' + db_user + ':' + db_password + '@' + db_host + ':' + db_port / db_name
database_url := env_var_or_default('DATABASE_URL', default_db_url)
default_migration_params := ''
v2_migration_dir := source_directory() / 'v2_migrations'
v2_compatible_migrations := source_directory() / 'v2_compatible_migrations'
v1_migration_dir := source_directory() / 'migrations'
resultant_dir := source_directory() / 'final-migrations'

# Copy migrations in {{dir_1}} and {{dir_2}} to a single directory {{resultant_dir}} after prefixing the subdirectories of {{dir_2}} with {{prefix}}
[private]
prefix_and_copy_migrations dir_1 dir_2 prefix resultant_dir:
    #! /usr/bin/env bash
    mkdir -p {{ resultant_dir }}
    cp -r {{ dir_1 }}/* {{ resultant_dir }}/ > /dev/null 2>&1

    # Prefix v2 migrations with {{ prefix }}
    sh -c '
    for dir in "{{ dir_2 }}"/*; do
        if [ -d "${dir}" ]; then
            base_name=$(basename "${dir}")
            new_name="{{ prefix }}${base_name}"
            cp -r "${dir}" "{{ resultant_dir }}/${new_name}"
        fi
    done
    '
    echo "Created {{ resultant_dir }}"

# Delete the newly created directory
[private]
delete_dir_if_exists dir=resultant_dir:
    @rm -rf {{ dir }}

v1_config_file_dir := source_directory() / 'diesel.toml'
default_operation := 'run'

[private]
run_migration operation=default_operation migration_dir=v1_migration_dir config_file_dir=v1_config_file_dir url=database_url *other_params=default_migration_params:
    diesel migration \
        --database-url '{{ url }}' \
        {{ operation }} \
        --migration-dir '{{ migration_dir }}' \
        --config-file '{{ config_file_dir }}' \
        {{ other_params }}

# Run database migrations for v1
migrate operation=default_operation *args='': (run_migration operation v1_migration_dir v1_config_file_dir database_url args)

v2_config_file_dir := source_directory() / 'diesel_v2.toml'

# Run database migrations for v2
migrate_v2 operation=default_operation *args='':
    #! /usr/bin/env bash
    set -euo pipefail

    EXIT_CODE=0
    just prefix_and_copy_migrations {{ v1_migration_dir }} {{ v2_compatible_migrations }} 8 {{ resultant_dir }}
    just prefix_and_copy_migrations {{ resultant_dir }} {{ v2_migration_dir }} 9 {{ resultant_dir }}
    just run_migration {{ operation }} {{ resultant_dir }} {{ v2_config_file_dir }} {{ database_url }} {{ args }} || EXIT_CODE=$?
    just delete_dir_if_exists
    exit $EXIT_CODE

# Run database migrations compatible with both v1 and v2
migrate_v2_compatible:
    #! /usr/bin/env bash
    set -euo pipefail

    EXIT_CODE=0
    just prefix_and_copy_migrations {{ v1_migration_dir }} {{ v2_compatible_migrations }} 8 {{ resultant_dir }}

    # Run the compatible migrations
    just run_migration run {{ resultant_dir }} {{ database_url }} || EXIT_CODE=$?

    just delete_dir_if_exists
    exit $EXIT_CODE

# Drop database if exists and then create a new 'hyperswitch_db' Database
resurrect database_name=db_name:
    psql -U postgres -c 'DROP DATABASE IF EXISTS  {{ database_name }}';
    psql -U postgres -c 'CREATE DATABASE {{ database_name }}';

ci_hack:
    scripts/ci-checks.sh

# Start superposition in docker (auto-detects docker or podman)
superposition-up:
    #! /usr/bin/env bash
    set -euo pipefail

    # Detect container runtime
    if command -v docker &> /dev/null; then
        COMPOSE="docker-compose"
    elif command -v podman-compose &> /dev/null; then
        COMPOSE="podman-compose"
    else
        echo "Error: Neither docker-compose nor podman-compose found"
        exit 1
    fi

    echo "Using $COMPOSE..."
    $COMPOSE -f docker-compose-development.yml up -d superposition

    echo "Waiting for superposition to be ready..."
    for i in {1..30}; do
        if curl -s http://localhost:8081/health > /dev/null 2>&1; then
            echo "Superposition is ready!"
            break
        fi
        echo "Waiting for superposition... ($i/30)"
        sleep 2
    done

# Stop superposition and dependencies
superposition-down:
    #! /usr/bin/env bash
    set -euo pipefail

    if command -v docker &> /dev/null; then
        docker-compose -f docker-compose-development.yml down
    elif command -v podman-compose &> /dev/null; then
        podman-compose -f docker-compose-development.yml down
    fi

# Seed Superposition with default dimensions and configs
superposition-seed:
    #! /usr/bin/env bash
    set -euo pipefail

    if [ ! -f "./config/superposition_seed.toml" ]; then
        echo "Error: Seed file not found at ./config/superposition_seed.toml"
        exit 1
    fi

    bash ./scripts/seed_superposition.sh
