# Module: Environment Detection & Management
# Purpose: Detects current environment (dev/prod/test) and applies environment-specific configuration overrides.
# Dependencies: None (core functions)

# Environment Detection and Configuration Functions
# Handles environment detection, validation, and environment-specific overrides

# Detect current environment from various sources
export def detect-current-environment [] {
    # Priority order for environment detection:
    # 1. PROVISIONING_ENV environment variable
    # 2. Environment-specific markers
    # 3. Directory-based detection
    # 4. Default fallback

    # Check explicit environment variable
    if ($env.PROVISIONING_ENV? | is-not-empty) {
        return $env.PROVISIONING_ENV
    }

    # Check CI/CD environments
    if ($env.CI? | is-not-empty) {
        if ($env.GITHUB_ACTIONS? | is-not-empty) { return "ci" }
        if ($env.GITLAB_CI? | is-not-empty) { return "ci" }
        if ($env.JENKINS_URL? | is-not-empty) { return "ci" }
        return "test"  # Default for CI environments
    }

    # Check for development indicators
    if (($env.PWD | path join ".git" | path exists) or
        ($env.PWD | path join "development" | path exists) or
        ($env.PWD | path join "dev" | path exists)) {
        return "dev"
    }

    # Check for production indicators
    if (($env.HOSTNAME? | default "" | str contains "prod") or
        ($env.NODE_ENV? | default "" | str downcase) == "production" or
        ($env.ENVIRONMENT? | default "" | str downcase) == "production") {
        return "prod"
    }

    # Check for test indicators
    if (($env.NODE_ENV? | default "" | str downcase) == "test" or
        ($env.ENVIRONMENT? | default "" | str downcase) == "test") {
        return "test"
    }

    # Default to development for interactive usage
    if ($env.TERM? | is-not-empty) {
        return "dev"
    }

    # Fallback
    return "dev"
}

# Get available environments from configuration
export def get-available-environments [
    config: record
] {
    let environments_section = ($config | get -o "environments" | default {})
    $environments_section | columns
}

# Validate environment name
export def validate-environment [
    environment: string
    config: record
] {
    let valid_environments = ["dev" "test" "prod" "ci" "staging" "local"]
    let configured_environments = (get-available-environments $config)
    let all_valid = ($valid_environments | append $configured_environments | uniq)

    if ($environment in $all_valid) {
        { valid: true, message: "" }
    } else {
        {
            valid: false,
            message: $"Invalid environment '($environment)'. Valid options: ($all_valid | str join ', ')"
        }
    }
}

# Apply environment variable overrides to configuration
export def apply-environment-variable-overrides [
    config: record
    debug = false
] {
    mut result = $config

    # Map of environment variables to config paths with type conversion
    let env_mappings = {
        "PROVISIONING_DEBUG": { path: "debug.enabled", type: "bool" },
        "PROVISIONING_LOG_LEVEL": { path: "debug.log_level", type: "string" },
        "PROVISIONING_NO_TERMINAL": { path: "debug.no_terminal", type: "bool" },
        "PROVISIONING_CHECK": { path: "debug.check", type: "bool" },
        "PROVISIONING_METADATA": { path: "debug.metadata", type: "bool" },
        "PROVISIONING_OUTPUT_FORMAT": { path: "output.format", type: "string" },
        "PROVISIONING_FILE_VIEWER": { path: "output.file_viewer", type: "string" },
        "PROVISIONING_USE_SOPS": { path: "sops.use_sops", type: "bool" },
        "PROVISIONING_PROVIDER": { path: "providers.default", type: "string" },
        "PROVISIONING_WORKSPACE_PATH": { path: "paths.workspace", type: "string" },
        "PROVISIONING_INFRA_PATH": { path: "paths.infra", type: "string" },
        "PROVISIONING_SOPS": { path: "sops.config_path", type: "string" },
        "PROVISIONING_KAGE": { path: "sops.age_key_file", type: "string" }
    }

    for env_var in ($env_mappings | columns) {
        let env_value = ($env | get -o $env_var | default null)
        if ($env_value | is-not-empty) {
            let mapping = ($env_mappings | get $env_var)
            let config_path = $mapping.path
            let config_type = $mapping.type

            # Convert value to appropriate type
            let converted_value = match $config_type {
                "bool" => {
                    if ($env_value | describe) == "string" {
                        match ($env_value | str downcase) {
                            "true" | "1" | "yes" | "on" => true
                            "false" | "0" | "no" | "off" => false
                            _ => false
                        }
                    } else {
                        $env_value | into bool
                    }
                }
                "string" => $env_value
                _ => $env_value
            }

            if $debug {
                # log debug $"Applying env override: ($env_var) -> ($config_path) = ($converted_value)"
            }
            $result = (set-config-value $result $config_path $converted_value)
        }
    }

    $result
}

# Helper function to set nested config value using dot notation
def set-config-value [
    config: record
    path: string
    value: any
] {
    let path_parts = ($path | split row ".")
    mut current = $config
    mut result = $current

    # Navigate to parent of target
    # Use drop instead of range for Nushell 0.109+ compatibility
    let parent_parts = ($path_parts | drop)
    let leaf_key = ($path_parts | last)

    for part in $parent_parts {
        # Use upsert instead of insert to avoid column_already_exists error
        if ($result | get -o $part) == null {
            $result = ($result | upsert $part {})
        }
        $current = ($result | get $part)
        # Update parent in result would go here (mutable record limitation)
    }

    # Set the value at the leaf
    if ($parent_parts | length) == 0 {
        # Top level
        $result | upsert $leaf_key $value
    } else {
        # Need to navigate back and update
        # This is a simplified approach - for deep nesting, a more complex function would be needed
        $result | upsert $leaf_key $value
    }
}
