# Configuration Loader for Provisioning System
# Implements hierarchical configuration loading with variable interpolation

use std log

# Main configuration loader - loads and merges all config sources
export def load-provisioning-config [
    --debug = false                 # Enable debug logging
    --validate = true               # Validate configuration
    --environment: string           # Override environment (dev/prod/test)
] {
    if $debug {
        # log debug "Loading provisioning configuration..."
    }

    # Define configuration sources in precedence order (lowest to highest)
    let config_sources = [
        # 1. System defaults (lowest precedence)
        {
            name: "defaults"
            path: "/Users/Akasha/repo-cnz/src/provisioning/config.defaults.toml"
            required: true
        }
        # 2. User configuration
        {
            name: "user"
            path: ($env.HOME | path join ".config" | path join "provisioning" | path join "config.toml")
            required: false
        }
        # 3. Project configuration
        {
            name: "project"
            path: ($env.PWD | path join "provisioning.toml")
            required: false
        }
        # 4. Infrastructure-specific configuration (highest precedence)
        {
            name: "infra"
            path: ($env.PWD | path join ".provisioning.toml")
            required: false
        }
    ]

    mut final_config = {}

    # Load and merge configurations
    for source in $config_sources {
        let config_data = (load-config-file $source.path $source.required $debug)
        if ($config_data | is-not-empty) {
            if $debug {
                # log debug $"Loaded ($source.name) config from ($source.path)"
            }
            $final_config = (deep-merge $final_config $config_data)
        }
    }

    # Apply environment-specific overrides if specified
    if ($environment | is-not-empty) {
        let env_config = ($final_config | get -o $"environments.($environment)" | default {})
        if ($env_config | is-not-empty) {
            if $debug {
                # log debug $"Applying environment overrides for: ($environment)"
            }
            $final_config = (deep-merge $final_config $env_config)
        }
    }

    # Interpolate variables in the final configuration
    $final_config = (interpolate-config $final_config)

    # Validate configuration if requested
    if $validate {
        validate-config $final_config
    }

    if $debug {
        # log debug "Configuration loading completed"
    }

    $final_config
}

# Load a single configuration file
export def load-config-file [
    file_path: string
    required = false
    debug = false
] {
    if not ($file_path | path exists) {
        if $required {
            error make {
                msg: $"Required configuration file not found: ($file_path)"
            }
        } else {
            if $debug {
                # log debug $"Optional config file not found: ($file_path)"
            }
            return {}
        }
    }

    if $debug {
        # log debug $"Loading config file: ($file_path)"
    }

    # Load the file - use error handling without complex try-catch
    if ($file_path | path exists) {
        open $file_path
    } else {
        if $required {
            error make {
                msg: $"Configuration file not found: ($file_path)"
            }
        } else {
            {}
        }
    }
}

# Deep merge two configuration records (right takes precedence)
export def deep-merge [
    base: record
    override: record
] {
    mut result = $base

    for key in ($override | columns) {
        let override_value = ($override | get $key)
        let base_value = ($base | get -o $key)

        if ($base_value | is-empty) {
            # Key doesn't exist in base, add it
            $result = ($result | insert $key $override_value)
        } else if (($base_value | describe) == "record") and (($override_value | describe) == "record") {
            # Both are records, merge recursively
            $result = ($result | upsert $key (deep-merge $base_value $override_value))
        } else {
            # Override the value
            $result = ($result | upsert $key $override_value)
        }
    }

    $result
}

# Interpolate variables in configuration values
export def interpolate-config [
    config: record
] {
    mut result = $config

    # First pass: resolve simple base values
    if ($config | get -o paths.base | is-not-empty) {
        let base_path = ($config | get paths.base)
        $result = ($result | update paths (
            $result | get paths |
            transpose key value |
            each {|item|
                if ($item.value | describe) == "string" and ($item.value | str contains "${paths.base}") {
                    {key: $item.key, value: ($item.value | str replace --all "${paths.base}" $base_path)}
                } else {
                    $item
                }
            } |
            transpose -r |
            into record
        ))
    }

    $result
}

# Interpolate variables in a string using ${path.to.value} syntax
export def interpolate-string [
    text: string
    config: record
] {
    mut result = $text

    # Simple interpolation for ${paths.base} pattern
    if ($result | str contains "${paths.base}") {
        let base_path = (get-config-value $config "paths.base" "")
        $result = ($result | str replace --all "${paths.base}" $base_path)
    }

    # Add more interpolation patterns as needed
    # This is a basic implementation - a full template engine would be more robust
    $result
}

# Get a nested configuration value using dot notation
export def get-config-value [
    config: record
    path: string
    default_value: any = null
] {
    let path_parts = ($path | split row ".")
    mut current = $config

    for part in $path_parts {
        let next_value = ($current | get -o $part)
        if ($next_value | is-empty) {
            return $default_value
        }
        $current = $next_value
    }

    $current
}

# Validate the final configuration
export def validate-config [
    config: record
] {
    # Check required top-level sections
    let required_sections = ["core", "paths", "debug"]

    for section in $required_sections {
        if ($config | get -o $section | is-empty) {
            error make {
                msg: $"Missing required configuration section: ($section)"
            }
        }
    }

    # Validate core section
    let core = ($config | get core)
    if ($core | get -o version | is-empty) {
        error make {
            msg: "Missing required core.version in configuration"
        }
    }

    # Validate paths section
    let paths = ($config | get paths)
    if ($paths | get -o base | is-empty) {
        error make {
            msg: "Missing required paths.base in configuration"
        }
    }

    # Additional validation can be added here
}

# Helper function to create directory structure for user config
export def init-user-config [] {
    let config_dir = ($env.HOME | path join ".config" | path join "provisioning")

    if not ($config_dir | path exists) {
        mkdir $config_dir
        print $"Created user config directory: ($config_dir)"
    }

    let user_config_path = ($config_dir | path join "config.toml")
    if not ($user_config_path | path exists) {
        let example_path = ($env.FILE_PWD | path join ".." | path join ".." | path join ".." | path join ".." | path join "config.user.toml.example")
        if ($example_path | path exists) {
            cp $example_path $user_config_path
            print $"Created user config file: ($user_config_path)"
            print "Edit this file to customize your provisioning settings"
        }
    }
}