#!/bin/bash

# Exit on any error, treat unset variables as errors, and propagate errors in pipelines
set -euo pipefail

load_env() {
    local paths=(
        "$(pwd)/.env"
        "$(dirname "$(realpath "$0")")/.env"
        "$(dirname "$(realpath "$0")")/.env.$(basename "$(realpath "$0")")"
        "$(dirname "$(readlink "$0")")/.env.$(basename "$0")"
        "/opt/skyfritt-tools-env/.env.$(basename "$0")"
    )

    for path in "${paths[@]}"; do
        if [[ -f "$path" ]]; then
            source "$path" && return 0
        fi
    done

    echo "Error: .env file not found in any location" >&2
    exit 1
}

load_env || exit 1

AUTO_MODE="false"
FORCE_UPDATE="false"
FULLCHAIN_PEM="${FULLCHAIN_PEM:-true}"

while [[ $# -gt 0 ]]; do
    case $1 in
        --silent)
            AUTO_MODE="true"
            shift
            ;;
        --force)
            FORCE_UPDATE="true"
            shift
            ;;
        --disable-pem)
            FULLCHAIN_PEM="false"
            shift
            ;;
        *)
            shift
            ;;
    esac
done

GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT

check_requirements() {
    local required_commands=("curl" "jq" "openssl")
    for cmd in "${required_commands[@]}"; do
        if ! command -v "$cmd" &> /dev/null; then
            echo -e "${RED}Error: Required command '$cmd' is not installed.${NC}"
            exit 1
        fi
    done
}

validate_api_key() {
    local api_key=$1
    [[ $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]
}

setup_directories() {
    local dirs=("$CERT_PATH" "$KEY_PATH" "$TEMP_PATH")
    for dir in "${dirs[@]}"; do
        if ! mkdir -p "$dir"; then
            echo -e "${RED}Error: Failed to create directory: $dir${NC}"
            exit 1
        fi
    done
}

download_and_verify_cert() {
    local domain=$1
    local cert_api_key=$2
    local key_api_key=$3
    local temp_cert="$TEMP_DIR/$domain.crt"
    local temp_key="$TEMP_DIR/$domain.key"
    local temp_pem="$TEMP_DIR/$domain.pem"


    echo -e "${BLUE}Processing certificate for $domain${NC}"

    # Download certificate
    if ! curl -s -fL -o "$temp_cert" -H "X-API-Key: $cert_api_key" \
        "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$domain"; then
        echo -e "${RED}Failed to download certificate for $domain${NC}"
        return 1
    fi

    # Download private key
    if ! curl -s -fL -o "$temp_key" -H "X-API-Key: $key_api_key" \
        "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$domain"; then
        echo -e "${RED}Failed to download private key for $domain${NC}"
        return 1
    fi

    # Download fullchain PEM file
        if ! curl -s -fL -o "$temp_pem" -H "X-API-Key: $cert_api_key.$key_api_key" \
            "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatecertchains/$domain"; then
            echo -e "${RED}Failed to download fullchain PEM file for $domain${NC}"
            return 1
        fi

    # Verify files are not empty
    if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ] || [ ! -s "$temp_pem" ]; then
        echo -e "${RED}Downloaded files are empty for $domain${NC}"
        return 1
    fi

    # Validate certificate and key match
    local cert_fingerprint
    cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey |
        openssl pkey -pubin -outform DER 2>/dev/null |
        openssl dgst -sha256)
    local key_fingerprint
    key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null |
        openssl dgst -sha256)
    local pem_fingerprint
    pem_fingerprint=$(openssl x509 -in "$temp_pem" -noout -pubkey |
        openssl pkey -pubin -outform DER 2>/dev/null |
        openssl dgst -sha256)

    if [ "$cert_fingerprint" != "$key_fingerprint" ]; then
        echo -e "${RED}Certificate and key do not match for $domain${NC}"
        return 1
    fi

    if [[ "$cert_fingerprint" != "$pem_fingerprint" ]]; then
        echo -e "${RED}Certificate and PEM file do not match for $domain${NC}"
        return 1
    fi

    return 0
}

install_certificate() {
    local domain=$1
    local final_cert="$CERT_PATH/$domain.crt"
    local final_key="$KEY_PATH/$domain.key"
    local final_pem="$KEY_PATH/$domain.pem"
    local temp_cert="$TEMP_DIR/$domain.crt"
    local temp_key="$TEMP_DIR/$domain.key"
    local temp_pem="$TEMP_DIR/$domain.pem"
    local needs_reload=0

    # Check if certificate needs updating
    if [ "$FORCE_UPDATE" = "true" ]; then
        needs_reload=1
    elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_pem" ]; then
        if ! cmp -s "$final_pem" "$temp_cert_pem"; then
            needs_reload=1
        fi
    elif [ -f "$final_cert" ]; then
        if ! cmp -s "$final_cert" "$temp_cert"; then
            needs_reload=1
        fi
    else
        needs_reload=1
    fi

    # Install new certificate and key
    if [ $needs_reload -eq 1 ]; then
        if [ "$FULLCHAIN_PEM" = "true" ]; then
            if ! cp -f "$temp_pem" "$final_pem"; then
                echo -e "${RED}Failed to install PEM file for $domain${NC}"
                return 1
            fi
            local files=("$final_pem")
        else
            if ! cp -f "$temp_cert" "$final_cert" || ! cp -f "$temp_key" "$final_key"; then
                echo -e "${RED}Failed to install certificate files for $domain${NC}"
                return 1
            fi
            local files=("$final_cert" "$final_key")
        fi

        # Set permissions and ownership
        for file in "${files[@]}"; do
            if ! chown "$CERT_OWNER:$CERT_GROUP" "$file" || \
               ! chmod "$CERT_PERMISSIONS" "$file"; then
                echo -e "${RED}Failed to set permissions for $file${NC}"
                return 1
            fi
        done

        echo -e "${GREEN}Certificate updated for $domain${NC}"
        return 0
    fi

    echo -e "${BLUE}Certificate already up to date for $domain${NC}"
    return 2
}

process_certificates() {
    local service_reloaded=0
    local certs_array
    certs_array=$(echo "$CERTIFICATES" | jq -c '.[]')

    while IFS= read -r cert; do
        local domain cert_api_key key_api_key
        domain=$(echo "$cert" | jq -r '.domain')
        cert_api_key=$(echo "$cert" | jq -r '.cert_api_key')
        key_api_key=$(echo "$cert" | jq -r '.key_api_key')

        if download_and_verify_cert "$domain" "$cert_api_key" "$key_api_key"; then
            if install_certificate "$domain"; then
                service_reloaded=1
            fi
        fi
    done <<< "$certs_array"

    # Reload service if needed
    if [ $service_reloaded -eq 1 ]; then
        echo -e "${BLUE}Reloading $SERVICE_NAME service...${NC}"
        if systemctl reload "$SERVICE_NAME"; then
            echo -e "${GREEN}Service reloaded successfully${NC}"
        else
            echo -e "${RED}Failed to reload service${NC}"
            return 1
        fi
    fi
}

check_cert_expiration() {
    echo -e "\n${BLUE}Certificate Expiration Dates:${NC}"
    for cert in "$CERT_PATH"/*.crt; do
        if [ -f "$cert" ]; then
            echo -n "$(basename "$cert"): "
            openssl x509 -enddate -noout -in "$cert"
        fi
    done
}

main_menu() {
    if [ "$AUTO_MODE" = "true" ]; then
        process_certificates
        exit 0
    fi

    echo -e "\n${BLUE}Certificate Management Menu${NC}"
    echo "1. Process all certificates"
    echo "2. List installed certificates"
    echo "3. Check certificate expiration"
    echo "4. Force update all certificates"

    read -r -p "Select an option (1-4): " choice

    case $choice in
        1) FORCE_UPDATE="false"; process_certificates ;;
        2) ls -l "$CERT_PATH"/*.crt 2>/dev/null || echo "No certificates found" ;;
        3) check_cert_expiration ;;
        4) FORCE_UPDATE="true"; process_certificates; FORCE_UPDATE="false" ;;
        *) echo -e "${RED}Invalid option${NC}" ;;
    esac
}

# Script initialization
check_requirements
setup_directories
main_menu