# Provider-Agnostic Middleware
# Uses dynamic provider loading and interface-based dispatch
# Supports multi-provider infrastructure deployments

use ../../../core/nulib/lib_provisioning/config/accessor.nu *
use ../../../core/nulib/lib_provisioning/extensions/profiles.nu *
use ../../../core/nulib/lib_provisioning/providers/registry.nu *
use ../../../core/nulib/lib_provisioning/providers/loader.nu *
use ../../../core/nulib/lib_provisioning/providers/interface.nu *
use ../../../core/nulib/lib_provisioning/utils/logging.nu *

# Initialize middleware
export def init-middleware []: nothing -> nothing {
    init-provider-registry
    log-info "Provider-agnostic middleware initialized" "middleware"
}

# Check if provider is allowed by profile (safer version)
def is_provider_allowed [provider_name: string]: nothing -> bool {
    try {
        let profile = (load-profile)

        # If not restricted, allow everything
        if not $profile.restricted {
            return true
        }

        # Check if provider is explicitly blocked
        if ($profile.blocked.providers | where {|p| $p == $provider_name} | length) > 0 {
            return false
        }

        # If allowed list is empty, allow all (except blocked)
        if ($profile.allowed.providers | length) == 0 {
            return true
        }

        # Check if provider is in allowed list
        ($profile.allowed.providers | where {|p| $p == $provider_name} | length) > 0

    } catch {
        # If profile loading fails, default to allow
        true
    }
}

def provider_undefined [
    server: record
] {
    let available_providers = (list-providers --available-only | get name | str join ", ")
    log-error $"Provider ($server.provider) not found or not available" "middleware"
    print $"(_ansi blue_bold)($server.hostname)(_ansi reset) with provider " +
       $"(_ansi green_bold)($server.provider)(_ansi reset) ($server.zone?) "
    print $"Error 🛑 provider expected to be one of available providers [(_ansi green_italic)($available_providers)(_ansi reset)], " +
        $"got (_ansi green_bold)($server.provider)(_ansi reset)"
}

# Dynamic provider dispatch helper
def dispatch_provider_function [
    provider_name: string
    function_name: string
    ...args
]: nothing -> any {
    # Check if provider is allowed
    if not (is_provider_allowed $provider_name) {
        log-error $"Provider ($provider_name) blocked by profile" "middleware"
        return null
    }

    # Load provider if not already loaded
    let provider = (get-provider $provider_name)
    if ($provider | is-empty) {
        log-error $"Failed to load provider ($provider_name)" "middleware"
        return null
    }

    # Call provider function
    call-provider-function $provider_name $function_name ...$args
}

# === CORE MIDDLEWARE FUNCTIONS ===

# Query servers (supports multi-provider)
export def mw_query_servers [
    settings: record
    find?: string
    cols?: string
    --prov: string
    --serverpos: int
]: nothing -> list {
    if not ($env.PROVIDER_REGISTRY_INITIALIZED? | default false) {
        init-middleware | ignore
    }

    let str_find = if $find != null { $find } else { "" }
    let str_cols = if $cols != null { $cols } else { "" }

    $settings.data.servers | enumerate | each { |it|
        if $prov == null or $it.item.provider == $prov {
            if $serverpos == null or $serverpos == $it.index {
                let provider_name = $it.item.provider

                # Dynamic provider dispatch
                let res = try {
                    dispatch_provider_function $provider_name "query_servers" $str_find $str_cols
                } catch {
                    provider_undefined $it.item
                    []
                }

                if ($res != null) and ($res | length) > 0 {
                    let result = if $str_find != "" {
                        $res | find $str_find
                    } else {
                        $res
                    }
                    if $str_cols != "" {
                        let field_list = ($str_cols | split row ",")
                        ($result | select -o $field_list)
                    } else {
                        $result
                    }
                } else {
                    []
                }
            }
        }
    } | flatten
}

# Server information (provider-agnostic)
export def mw_server_info [
    server: record
    check: bool
    find?: string
    cols?: string
]: nothing -> record {
    let str_find = if $find != null { $find } else { "" }
    let str_cols = if $cols != null { $cols } else { "" }

    let res = try {
        dispatch_provider_function $server.provider "server_info" $server $check $str_find $str_cols
    } catch {
        provider_undefined $server
        {}
    }

    if ($res | describe | str starts-with "record") and $res.hostname? != null {
        let result = if $str_find != "" {
            $res | find $str_find
        } else {
            $res
        }
        let info = if $str_cols != "" {
            let field_list = ($str_cols | split row ",")
            ($result | select -o $field_list)
        } else {
            $result
        }

        # Handle provider-specific private IP formats
        let priv = if $server.provider == "aws" {
            ($info | get -o private_ips | default [] | each {|it| ($it | select Description PrivateIpAddress VpcId SubnetId Groups) })
        } else {
            ($info | get -o priv | default [])
        }

        let full_info = if ($priv | length) > 0 {
            ($info | merge { private_ips: $priv })
        } else {
            $info
        }

        let out = (get-provisioning-out)
        if ($out | is-empty) {
            print ($full_info | table -e)
        }
        if (not $check) {
            ($full_info | table -e)
        }
        $full_info
    } else {
        $res
    }
}

# Server creation (provider-agnostic with profile checking)
export def mw_create_server [
    settings: record
    server: record
    check: bool
    error_exit: bool
]: nothing -> bool {
    # Check if provider is allowed by profile
    if not (is_provider_allowed $server.provider) {
        log-error $"Provider ($server.provider) blocked by current profile" "middleware"
        if $error_exit { exit } else { return false }
    }

    let zone = $server.zone? | default ""

    # Dynamic provider dispatch for requirements check
    let res = try {
        dispatch_provider_function $server.provider "check_server_requirements" $settings $server $check
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { false }
    }

    if not $res {
        log-error $"($server.provider) check requirements error for server ($server.hostname)" "middleware"
        return false
    }

    # Show provider-specific server creation message
    try {
        let msg = (dispatch_provider_function $server.provider "on_prov_server" $server)
        if $msg != null { print $msg }
    } catch { }

    print ($"Create (_ansi blue_bold)($server.hostname)(_ansi reset) with provider " +
       $"(_ansi green_bold)($server.provider)(_ansi reset) ($zone) ")
    return true
}

# Server state management (provider-agnostic)
export def mw_server_state [
    server: record
    new_state: string
    error_exit: bool
    wait: bool
    settings: record
]: nothing -> bool {
    # Check if provider is allowed by profile
    if not (is_provider_allowed $server.provider) {
        log-error $"Provider ($server.provider) blocked by current profile" "middleware"
        if $error_exit { exit } else { return false }
    }

    try {
        let result = (dispatch_provider_function $server.provider "server_state" $server $new_state $error_exit $wait $settings)
        $result != null
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { return false }
    }
}

# Server existence check (provider-agnostic)
export def mw_server_exists [
    server: record
    error_exit: bool
]: nothing -> bool {
    try {
        let result = (dispatch_provider_function $server.provider "server_exists" $server $error_exit)
        $result != null
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { false }
    }
}

# Server running status (provider-agnostic)
export def mw_server_is_running [
    server: record
    error_exit: bool
]: nothing -> bool {
    try {
        let result = (dispatch_provider_function $server.provider "server_is_running" $server $error_exit)
        $result != null
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { false }
    }
}

# Get IP (provider-agnostic)
export def mw_get_ip [
    settings: record
    server: record
    ip_type: string
    error_exit: bool
]: nothing -> string {
    try {
        let result = (dispatch_provider_function $server.provider "get_ip" $settings $server $ip_type $error_exit)
        if $result != null {
            $result | str trim
        } else {
            ""
        }
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { "" }
    }
}

# Multi-provider infrastructure operations
export def mw_servers_ips [
    settings: record
    data: list
    prov?: string
    serverpos?: int
]: nothing -> list {
    mut result = []

    for srv in ($data | enumerate) {
        let settings_server = ($settings.data.servers | where {|it| $it.hostname == $srv.item.hostname})
        if ($settings_server | length) == 0 { continue }

        let provider = ($settings_server | get -o 0 | get -o provider | default "")
        if $prov != null and $provider != $prov { continue }
        if $serverpos != null and $serverpos != $srv.index { continue }

        try {
            let provider_result = (dispatch_provider_function $provider "servers_ips" $settings [$srv.item] $prov $serverpos)
            if $provider_result != null {
                $result = ($result | append $provider_result)
            }
        } catch {
            provider_undefined { provider: $provider, hostname: $srv.item.hostname }
        }
    }

    $result
}

# Multi-provider server deletion
export def mw_delete_server [
    settings: record
    server: record
    keep_storage: bool
    error_exit: bool
]: nothing -> bool {
    let zone = $server.zone? | default ""

    try {
        let msg = (dispatch_provider_function $server.provider "on_prov_server" $server)
        if $msg != null { print $msg }

        let result = (dispatch_provider_function $server.provider "delete_server" $settings $server $keep_storage $error_exit)
        $result != null
    } catch {
        provider_undefined $server
        if $error_exit { exit } else { false }
    }
}

# === ENHANCED MULTI-PROVIDER OPERATIONS ===

# Deploy multi-provider infrastructure (simplified)
export def mw_deploy_multi_provider_infra [
    settings: record
    deployment_plan: record
]: nothing -> record {
    log-section "Starting multi-provider deployment" "middleware"

    # Group servers by provider
    let provider_groups = ($settings.data.servers | group-by provider)
    let providers_used = ($provider_groups | columns)

    log-info $"Will deploy to providers: ($providers_used | str join ', ')" "middleware"

    # Return basic deployment info
    {
        providers_used: $providers_used
        total_servers: ($settings.data.servers | length)
        deployment_plan: $deployment_plan
        status: "planned"
        timestamp: (date now)
    }
}

# Get provider status with capabilities
export def mw_provider_status [
    --verbose
]: nothing -> table {
    if not ($env.PROVIDER_REGISTRY_INITIALIZED? | default false) {
        init-middleware | ignore
    }

    let providers = (list-providers --available-only)

    $providers | each {|provider|
        let allowed = (is_provider_allowed $provider.name)
        let capabilities = (get-provider-capabilities-for $provider.name)

        let basic_info = {
            name: $provider.name
            type: $provider.type
            available: $provider.available
            loaded: $provider.loaded
            profile_allowed: $allowed
            status: (if $allowed { "✅ Available" } else { "🛑 Blocked by profile" })
        }

        if $verbose {
            $basic_info | merge {
                capabilities: $provider.capabilities
                server_management: ($capabilities.server_management? | default false)
                multi_region: ($capabilities.multi_region? | default false)
                auto_scaling: ($capabilities.auto_scaling? | default false)
            }
        } else {
            $basic_info
        }
    }
}

# Get deployment recommendations for multi-provider setup
export def mw_suggest_deployment_strategy [
    requirements: record
]: nothing -> record {
    log-info "Analyzing requirements for deployment strategy" "middleware"

    let available_providers = (list-providers --available-only)

    mut recommendations = {
        strategy: "single-provider"  # default
        primary_provider: ""
        secondary_providers: []
        rationale: []
    }

    # Analyze requirements and suggest strategy
    if ($requirements.regions? | default [] | length) > 1 {
        $recommendations.strategy = "multi-provider"
        $recommendations.rationale = ($recommendations.rationale | append "Multi-region requirement detected")
    }

    if ($requirements.high_availability? | default false) {
        $recommendations.strategy = "multi-provider"
        $recommendations.rationale = ($recommendations.rationale | append "High availability requirement")
    }

    if ($requirements.cost_optimization? | default false) {
        $recommendations.rationale = ($recommendations.rationale | append "Cost optimization: consider mixing providers")
    }

    # Select primary provider based on capabilities
    let suitable_providers = ($available_providers | where {|p|
        let caps = (get-provider-capabilities-for $p.name)
        $caps.server_management == true
    })

    if ($suitable_providers | length) > 0 {
        $recommendations.primary_provider = ($suitable_providers | get 0 | get name)

        if $recommendations.strategy == "multi-provider" {
            $recommendations.secondary_providers = ($suitable_providers | skip 1 | get name)
        }
    }

    log-info $"Recommended strategy: ($recommendations.strategy)" "middleware"
    $recommendations
}

# Initialize middleware when loaded
export-env {
    # This will be set when middleware functions are first called
}