#!/command/with-contenv sh

# AliasVault Container Initialization Script
# This script runs once at container startup and handles all initialization tasks

# Get verbosity level (0=minimal, 1=normal, 2=verbose)
VERBOSITY="${ALIASVAULT_VERBOSITY:-0}"

# Helper function for logging based on verbosity
log() {
    level=$1
    shift
    if [ "$VERBOSITY" -ge "$level" ]; then
        echo "$@"
    fi
}

# Always show header and critical messages (level 0)
echo ""
echo "=================================================="
echo "    _    _ _           __      __         _ _   "
echo "   / \\  | (_) __ _ ___ \\ \\    / /_ _ _   _| | |_"
echo "  / _ \\ | | |/ _\` / __| \\ \\/\\/ / _\` | | | | | __|"
echo " / ___ \\| | | (_| \\__ \\  \\  / / (_| | |_| | | |_ "
echo "/_/   \\_\\_|_|\\__,_|___/   \\/  \\__,__|\\__,_|_|\\__|"
echo ""
echo "=================================================="
echo ""

echo "[init] Starting AliasVault..."
echo ""

# Create required directories
if [ ! -d /database/postgres ] || [ ! -d /logs/postgres ] || [ ! -d /certificates ] || [ ! -d /secrets ] || [ ! -d /var/run/postgresql ]; then
    mkdir -p /database/postgres /logs/postgres /certificates /secrets /var/run/postgresql
    log 0 "[init] Required directories created"
else
    log 0 "[init] ✅ Required directories already exist"
fi

if [ ! -f /secrets/postgres_password ]; then
    log 0 "[init] → Generating PostgreSQL password..."
    openssl rand -base64 32 | tr -d "\n" > /secrets/postgres_password
    chmod 600 /secrets/postgres_password
else
    log 0 "[init] ✅ PostgreSQL password already exists "
fi

if [ ! -f /secrets/data_protection_cert_pass ]; then
    log 0 "[init] → Generating Data Protection Certificate password..."
    openssl rand -base64 32 | tr -d "\n" > /secrets/data_protection_cert_pass
    chmod 600 /secrets/data_protection_cert_pass
else
    log 0 "[init] ✅ Data Protection Certificate password already exists"
fi

if [ ! -f /secrets/jwt_key ]; then
    log 0 "[init] → Generating JWT key..."
    openssl rand -base64 32 | tr -d "\n" > /secrets/jwt_key
    chmod 600 /secrets/jwt_key
else
    log 0 "[init] ✅ JWT key already exists"
fi

# Admin password is not created by default
# Users must run reset-admin-password.sh to configure the admin password
log 0 "[init] ✅ Admin password configuration deferred to manual setup"

# Read PostgreSQL password for database initialization
POSTGRES_PASSWORD=$(cat /secrets/postgres_password)
export PGDATA="/database/postgres"

# Initialize PostgreSQL if needed
if [ ! -d "$PGDATA/base" ]; then
    log 0 ""
    log 0 "[init] PostgreSQL database not found, initializing..."

    # Set proper permissions
    chown -R postgres:postgres /database/postgres /logs/postgres /var/run/postgresql
    chmod 700 /database/postgres

    # Initialize database as postgres user
    log 1 "[init] → Running initdb..."
    if [ "$VERBOSITY" -ge 2 ]; then
        su - postgres -c "/usr/lib/postgresql/16/bin/initdb -D $PGDATA --locale=C.UTF-8 --encoding=UTF8" 2>&1 | tee /logs/postgres/initdb.log
    else
        su - postgres -c "/usr/lib/postgresql/16/bin/initdb -D $PGDATA --locale=C.UTF-8 --encoding=UTF8" > /logs/postgres/initdb.log 2>&1
    fi

    # Configure PostgreSQL
    log 0 "[init] → Configuring PostgreSQL..."

    # Listen on all container interfaces (covers localhost & Docker networks)
    echo "listen_addresses = '*'" >> "$PGDATA/postgresql.conf"
    # Store SCRAM hashes instead of MD5
    echo "password_encryption = scram-sha-256" >> "$PGDATA/postgresql.conf"
    # Allow local Unix-socket connections (processes inside the same container)
    echo "local   all   all                                   trust" >> "$PGDATA/pg_hba.conf"
    # Allow any client on the same subnet(s) as this container (bridge/overlay)
    # Prefer SCRAM if enabled above; otherwise MD5
    if grep -qi '^password_encryption *= *scram-sha-256' "$PGDATA/postgresql.conf"; then
        AUTH_METHOD="scram-sha-256"
    else
        AUTH_METHOD="md5"
    fi
    echo "host    all   all           samenet                  ${AUTH_METHOD}" >> "$PGDATA/pg_hba.conf"

    # Start PostgreSQL temporarily to create database and user
    log 0 "[init] → Starting PostgreSQL temporarily for setup..."
    if [ "$VERBOSITY" -ge 2 ]; then
        su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA -l /logs/postgres/postgres.log start"
    else
        su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA -l /logs/postgres/postgres.log start" >/dev/null 2>&1
    fi

    # Wait for PostgreSQL to be ready
    log 0 "[init] → Waiting for PostgreSQL to be ready..."
    i=1
    while [ $i -le 30 ]; do
        if su - postgres -c "/usr/lib/postgresql/16/bin/psql -c 'SELECT 1;'" >/dev/null 2>&1; then
            break
        fi
        sleep 1
        i=$((i + 1))
    done

    # Create database and user
    log 0 "[init] → Creating AliasVault database and user..."
    if [ "$VERBOSITY" -ge 2 ]; then
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE USER aliasvault WITH PASSWORD '$POSTGRES_PASSWORD'\""
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE DATABASE aliasvault OWNER aliasvault LOCALE 'C.UTF-8';\""
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"GRANT ALL PRIVILEGES ON DATABASE aliasvault TO aliasvault;\""
    else
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE USER aliasvault WITH PASSWORD '$POSTGRES_PASSWORD'\"" >/dev/null 2>&1
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"CREATE DATABASE aliasvault OWNER aliasvault LOCALE 'C.UTF-8';\"" >/dev/null 2>&1
        su - postgres -c "/usr/lib/postgresql/16/bin/psql -c \"GRANT ALL PRIVILEGES ON DATABASE aliasvault TO aliasvault;\"" >/dev/null 2>&1
    fi

    # Stop PostgreSQL
    log 0 "[init] → Stopping PostgreSQL..."
    if [ "$VERBOSITY" -ge 2 ]; then
        su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA stop"
    else
        su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA stop" >/dev/null 2>&1
    fi
    sleep 2

    log 0 "[init] PostgreSQL initialization complete"
else
    log 0 "[init] ✅ PostgreSQL database already initialized"

    # Just ensure permissions are correct
    chown -R postgres:postgres /database/postgres /logs/postgres /var/run/postgresql
    chmod 700 /database/postgres
fi

# Function to check if certificate needs regeneration
needs_cert_regeneration() {
    # If cert doesn't exist, need to generate
    if [ ! -f /certificates/ssl/cert.pem ] || [ ! -f /certificates/ssl/key.pem ]; then
        return 0
    fi

    # Check if we have a hostname marker file
    if [ -f /certificates/ssl/.hostname_marker ]; then
        STORED_HOSTNAME=$(cat /certificates/ssl/.hostname_marker)
        if [ "$STORED_HOSTNAME" != "${HOSTNAME:-localhost}" ]; then
            log 0 "[init] Hostname changed from '$STORED_HOSTNAME' to '${HOSTNAME:-localhost}', regenerating certificate..."
            return 0
        fi
    else
        # No marker file, regenerate to be safe
        return 0
    fi

    return 1
}

# Function to check if a string is an IP address (IPv4 or IPv6)
is_ip_address() {
    local value="$1"

    # Check for IPv4 pattern
    if echo "$value" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
        # Validate each octet is <= 255
        local valid=1
        # Use a simple approach to split the IP address
        local o1=$(echo "$value" | cut -d. -f1)
        local o2=$(echo "$value" | cut -d. -f2)
        local o3=$(echo "$value" | cut -d. -f3)
        local o4=$(echo "$value" | cut -d. -f4)
        for octet in "$o1" "$o2" "$o3" "$o4"; do
            if [ "$octet" -gt 255 ]; then
                valid=0
                break
            fi
        done
        if [ "$valid" -eq 1 ]; then
            return 0  # It's a valid IPv4
        fi
    fi

    # Check for IPv6 pattern (simplified check)
    if echo "$value" | grep -qE '^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$|^::1$|^::$'; then
        return 0  # It's likely IPv6
    fi

    return 1  # Not an IP address
}

# Generate SSL certificates if needed or hostname changed
if needs_cert_regeneration; then
    log 0 ""
    log 0 "[init] Generating SSL certificates..."
    mkdir -p /certificates/ssl

    HOSTNAME_VALUE="${HOSTNAME:-localhost}"

    if [ "$HOSTNAME_VALUE" = "localhost" ] || [ -z "$HOSTNAME_VALUE" ]; then
        # Default localhost-only configuration
        if [ "$VERBOSITY" -ge 2 ]; then
            openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
                -keyout /certificates/ssl/key.pem \
                -out /certificates/ssl/cert.pem \
                -subj "/C=US/ST=State/L=City/O=AliasVault/CN=localhost"
        else
            openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
                -keyout /certificates/ssl/key.pem \
                -out /certificates/ssl/cert.pem \
                -subj "/C=US/ST=State/L=City/O=AliasVault/CN=localhost" \
                >/dev/null 2>&1
        fi
    else
        # Determine if the hostname is an IP address or a DNS name
        if is_ip_address "$HOSTNAME_VALUE"; then
            # It's an IP address - use IP: prefix in SAN
            SAN_ENTRY="IP:${HOSTNAME_VALUE}"
            log 1 "[init] Detected IP address: ${HOSTNAME_VALUE}"
        else
            # It's a DNS name - use DNS: prefix in SAN
            SAN_ENTRY="DNS:${HOSTNAME_VALUE}"
            log 1 "[init] Detected hostname: ${HOSTNAME_VALUE}"
        fi

        # Generate certificate with the appropriate SAN entry
        if [ "$VERBOSITY" -ge 2 ]; then
            openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
                -keyout /certificates/ssl/key.pem \
                -out /certificates/ssl/cert.pem \
                -subj "/C=US/ST=State/L=City/O=AliasVault/CN=${HOSTNAME_VALUE}" \
                -addext "subjectAltName=${SAN_ENTRY},DNS:localhost,IP:127.0.0.1"
        else
            openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
                -keyout /certificates/ssl/key.pem \
                -out /certificates/ssl/cert.pem \
                -subj "/C=US/ST=State/L=City/O=AliasVault/CN=${HOSTNAME_VALUE}" \
                -addext "subjectAltName=${SAN_ENTRY},DNS:localhost,IP:127.0.0.1" \
                >/dev/null 2>&1
        fi
    fi

    chmod 600 /certificates/ssl/key.pem
    chmod 644 /certificates/ssl/cert.pem

    # Store current hostname for change detection
    echo "${HOSTNAME:-localhost}" > /certificates/ssl/.hostname_marker
    chmod 644 /certificates/ssl/.hostname_marker

    log 0 "[init] Self-signed SSL certificates generated"
else
    log 0 "[init] ✅ Self-signed SSL certificates already exist"
fi


echo ""
echo "[init] Waiting for all services to be ready... this can take a short while..."
echo ""

# Oneshot service exits successfully, dependencies can now start