# Configuration interpolation - Substitutes variables and patterns in config
# NUSHELL 0.109 COMPLIANT - Using reduce --fold (Rule 3), do-complete (Rule 5), each (Rule 8)

# helpers/environment star-import was dead — dropped (ADR-025 Phase 3 Layer 2).

# Main interpolation entry point - interpolates all patterns in configuration
export def interpolate-config [config: record]: nothing -> record {
    let base_result = (do { $config | get paths.base } | complete)
    let base_path = if $base_result.exit_code == 0 { $base_result.stdout } else { "" }

    if ($base_path | is-not-empty) {
        # Convert config to JSON, apply all interpolations, convert back
        let json_str = ($config | to json)
        let interpolated_json = (interpolate-all-patterns $json_str $config)
        ($interpolated_json | from json)
    } else {
        $config
    }
}

# Interpolate a single string value with configuration context
export def interpolate-string [text: string, config: record]: nothing -> string {
    # Basic interpolation for {{paths.base}} pattern
    if ($text | str contains "{{paths.base}}") {
        let base_path = (get-config-value $config "paths.base" "")
        ($text | str replace --all "{{paths.base}}" $base_path)
    } else {
        $text
    }
}

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

    # Navigate to the value using the path
    let result = ($path_parts | reduce --fold $config {|part, current|
        let access_result = (do { $current | get $part } | complete)
        if $access_result.exit_code == 0 { $access_result.stdout } else { null }
    })

    if ($result | is-empty) { $default_value } else { $result }
}

# Apply all interpolation patterns to JSON string (Rule 3: using reduce --fold for sequence)
def interpolate-all-patterns [json_str: string, config: record]: nothing -> string {
    # Apply each interpolation pattern in sequence using reduce --fold
    # This ensures patterns are applied in order and mutations are immutable
    let patterns = [
        {name: "paths.base", fn: {|s, c| interpolate-base-path $s ($c | get paths.base | default "") }}
        {name: "env", fn: {|s, c| interpolate-env-variables $s}}
        {name: "datetime", fn: {|s, c| interpolate-datetime $s}}
        {name: "git", fn: {|s, c| interpolate-git-info $s}}
        {name: "sops", fn: {|s, c| interpolate-sops-config $s $c}}
        {name: "providers", fn: {|s, c| interpolate-provider-refs $s $c}}
        {name: "advanced", fn: {|s, c| interpolate-advanced-features $s $c}}
    ]

    $patterns | reduce --fold $json_str {|pattern, result|
        do { ($pattern.fn | call $result $config) } | complete | if $in.exit_code == 0 { $in.stdout } else { $result }
    }
}

# Interpolate base path pattern
def interpolate-base-path [text: string, base_path: string]: nothing -> string {
    if ($text | str contains "{{paths.base}}") {
        ($text | str replace --all "{{paths.base}}" $base_path)
    } else {
        $text
    }
}

# Interpolate environment variables with security validation (Rule 8: using reduce --fold)
def interpolate-env-variables [text: string]: nothing -> string {
    # Safe environment variables list (security allowlist)
    let safe_env_vars = [
        "HOME" "USER" "HOSTNAME" "PWD" "SHELL"
        "PROVISIONING" "PROVISIONING_WORKSPACE_PATH" "PROVISIONING_INFRA_PATH"
        "PROVISIONING_SOPS" "PROVISIONING_KAGE"
    ]

    # Apply each env var substitution using reduce --fold (Rule 3: no mutable variables)
    let with_env = ($safe_env_vars | reduce --fold $text {|env_var, result|
        let pattern = $"\\{\\{env\\.($env_var)\\}\\}"
        let env_result = (do { $env | get $env_var } | complete)
        let env_value = if $env_result.exit_code == 0 { $env_result.stdout } else { "" }

        if ($env_value | is-not-empty) {
            ($result | str replace --regex $pattern $env_value)
        } else {
            $result
        }
    })

    # Handle conditional environment variables
    interpolate-conditional-env $with_env
}

# Handle conditional environment variable interpolation
def interpolate-conditional-env [text: string]: nothing -> string {
    let conditionals = [
        {pattern: "{{env.HOME || \"/tmp\"}}", value: {|| ($env.HOME? | default "/tmp")}}
        {pattern: "{{env.USER || \"unknown\"}}", value: {|| ($env.USER? | default "unknown")}}
    ]

    $conditionals | reduce --fold $text {|cond, result|
        if ($result | str contains $cond.pattern) {
            let value = (($cond.value | call))
            ($result | str replace --all $cond.pattern $value)
        } else {
            $result
        }
    }
}

# Interpolate date and time values
def interpolate-datetime [text: string]: nothing -> string {
    let current_date = (date now | format date "%Y-%m-%d")
    let current_timestamp = (date now | format date "%s")
    let iso_timestamp = (date now | format date "%Y-%m-%dT%H:%M:%SZ")

    let with_date = ($text | str replace --all "{{now.date}}" $current_date)
    let with_timestamp = ($with_date | str replace --all "{{now.timestamp}}" $current_timestamp)
    ($with_timestamp | str replace --all "{{now.iso}}" $iso_timestamp)
}

# Interpolate git information (defaults to "unknown" to avoid hanging)
def interpolate-git-info [text: string]: nothing -> string {
    let patterns = [
        {pattern: "{{git.branch}}", value: "unknown"}
        {pattern: "{{git.commit}}", value: "unknown"}
        {pattern: "{{git.origin}}", value: "unknown"}
    ]

    $patterns | reduce --fold $text {|p, result|
        ($result | str replace --all $p.pattern $p.value)
    }
}

# Interpolate SOPS configuration references
def interpolate-sops-config [text: string, config: record]: nothing -> string {
    let sops_key_result = (do { $config | get sops.age_key_file } | complete)
    let sops_key_file = if $sops_key_result.exit_code == 0 { $sops_key_result.stdout } else { "" }

    let with_key = if ($sops_key_file | is-not-empty) {
        ($text | str replace --all "{{sops.key_file}}" $sops_key_file)
    } else {
        $text
    }

    let sops_cfg_result = (do { $config | get sops.config_path } | complete)
    let sops_config_path = if $sops_cfg_result.exit_code == 0 { $sops_cfg_result.stdout } else { "" }

    if ($sops_config_path | is-not-empty) {
        ($with_key | str replace --all "{{sops.config_path}}" $sops_config_path)
    } else {
        $with_key
    }
}

# Interpolate cross-section provider references
def interpolate-provider-refs [text: string, config: record]: nothing -> string {
    let providers_to_check = [
        {pattern: "{{providers.aws.region}}", path: "providers.aws.region"}
        {pattern: "{{providers.default}}", path: "providers.default"}
        {pattern: "{{providers.upcloud.zone}}", path: "providers.upcloud.zone"}
    ]

    $providers_to_check | reduce --fold $text {|prov, result|
        let value_result = (do {
            let parts = ($prov.path | split row ".")
            if ($parts | length) == 2 {
                $config | get ($parts | first) | get ($parts | last)
            } else {
                $config | get ($parts | first) | get ($parts | get 1) | get ($parts | last)
            }
        } | complete)

        let value = if $value_result.exit_code == 0 { $value_result.stdout } else { "" }

        if ($value | is-not-empty) {
            ($result | str replace --all $prov.pattern $value)
        } else {
            $result
        }
    }
}

# Interpolate advanced features (function calls, environment-aware paths)
def interpolate-advanced-features [text: string, config: record]: nothing -> string {
    let base_path_result = (do { $config | get paths.base } | complete)
    let base_path = if $base_path_result.exit_code == 0 { $base_path_result.stdout } else { "" }

    let with_path_join = if ($text | str contains "{{path.join(paths.base") {
        # Simple regex-based path.join replacement
        ($text | str replace --regex "\\{\\{path\\.join\\(paths\\.base,\\s*\"([^\"]+)\"\\)\\}\\}" $"($base_path)/$1")
    } else {
        $text
    }

    # Replace environment-aware paths
    let current_env_result = (do { $config | get current_environment } | complete)
    let current_env = if $current_env_result.exit_code == 0 { $current_env_result.stdout } else { "dev" }

    ($with_path_join | str replace --all "{{paths.base.\${env}}}" $"{{paths.base}}.($current_env)")
}

# Validate interpolation patterns and detect issues
export def validate-interpolation [
    config: record
    --detailed = false
]: nothing -> record {
    let json_str = ($config | to json)

    # Check for unresolved interpolation patterns
    let unresolved = (detect-unresolved-patterns $json_str)
    let unresolved_errors = if ($unresolved | length) > 0 {
        [{
            type: "unresolved_interpolation",
            severity: "error",
            patterns: $unresolved,
            message: $"Unresolved interpolation patterns found: ($unresolved | str join ', ')"
        }]
    } else {
        []
    }

    # Check for circular dependencies
    let circular = (detect-circular-dependencies $json_str)
    let circular_errors = if ($circular | length) > 0 {
        [{
            type: "circular_dependency",
            severity: "error",
            dependencies: $circular,
            message: $"Circular interpolation dependencies detected"
        }]
    } else {
        []
    }

    # Check for unsafe environment variable access
    let unsafe = (detect-unsafe-env-patterns $json_str)
    let unsafe_warnings = if ($unsafe | length) > 0 {
        [{
            type: "unsafe_env_access",
            severity: "warning",
            variables: $unsafe,
            message: $"Potentially unsafe environment variable access"
        }]
    } else {
        []
    }

    # Validate git context if needed
    let git_warnings = if ($json_str | str contains "{{git.") {
        let git_check = (do { ^git rev-parse --git-dir err> /dev/null } | complete)
        if ($git_check.exit_code != 0) {
            [{
                type: "git_context",
                severity: "warning",
                message: "Git interpolation patterns found but not in a git repository"
            }]
        } else {
            []
        }
    } else {
        []
    }

    # Combine all results
    let all_errors = ($unresolved_errors | append $circular_errors)
    let all_warnings = ($unsafe_warnings | append $git_warnings)

    if (not $detailed) and (($all_errors | length) > 0) {
        let error_messages = ($all_errors | each { |err| $err.message })
        error make {msg: ($error_messages | str join "; ")}
    }

    {
        valid: (($all_errors | length) == 0),
        errors: $all_errors,
        warnings: $all_warnings,
        summary: {
            total_errors: ($all_errors | length),
            total_warnings: ($all_warnings | length),
            interpolation_patterns_detected: (count-interpolation-patterns $json_str)
        }
    }
}

# Detect unresolved interpolation patterns
def detect-unresolved-patterns [text: string]: nothing -> list {
    # Known patterns that should be handled
    let known_prefixes = ["paths" "env" "now" "git" "sops" "providers" "path"]

    # Extract all {{...}} patterns and check if they match known types
    let all_patterns = (do {
        $text | str replace --regex "\\{\\{([^}]+)\\}\\}" "$1"
    } | complete)

    if ($all_patterns.exit_code != 0) {
        return []
    }

    # Check for unknown patterns (simplified detection)
    if ($text | str contains "{{unknown.") {
        ["unknown.*"]
    } else {
        []
    }
}

# Detect circular interpolation dependencies
def detect-circular-dependencies [text: string]: nothing -> list {
    if (($text | str contains "{{paths.base}}") and ($text | str contains "paths.base.*{{paths.base}}")) {
        ["paths.base -> paths.base"]
    } else {
        []
    }
}

# Detect unsafe environment variable patterns
def detect-unsafe-env-patterns [text: string]: nothing -> list {
    let dangerous_patterns = ["PATH" "LD_LIBRARY_PATH" "PYTHONPATH" "SHELL" "PS1"]

    # Use reduce --fold to find all unsafe patterns (Rule 3)
    $dangerous_patterns | reduce --fold [] {|pattern, unsafe_list|
        if ($text | str contains $"{{env.($pattern)}}") {
            ($unsafe_list | append $pattern)
        } else {
            $unsafe_list
        }
    }
}

# Count interpolation patterns in text for metrics
def count-interpolation-patterns [text: string]: nothing -> number {
    # Count {{...}} occurrences
    ($text | str replace --all --regex "\\{\\{[^}]+\\}\\}" "" | length) - ($text | length)
        | math abs
        | ($text | length) - .
        | . / 4  # Approximate based on {{ }} length
}
