# Mode Configuration Validator
# Validates mode configurations against Nickel schemas and runtime requirements

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

# Validate complete mode configuration
export def validate-mode-config [
    mode_name: string   # Mode to validate
    --strict            # Strict validation (fail on warnings)
] -> record {
    log-info $"Validating mode configuration: ($mode_name)"

    let config_file = get-mode-file $mode_name

    if not ($config_file | path exists) {
        error make {
            msg: $"Mode configuration file not found: ($config_file)"
        }
    }

    let config = open $config_file

    mut errors = []
    mut warnings = []

    # Validate structure
    let struct_result = validate-structure $config
    $errors = ($errors | append $struct_result.errors)
    $warnings = ($warnings | append $struct_result.warnings)

    # Validate authentication
    let auth_result = validate-authentication-config $config.authentication
    $errors = ($errors | append $auth_result.errors)
    $warnings = ($warnings | append $auth_result.warnings)

    # Validate services
    let service_result = validate-services-config $config.services
    $errors = ($errors | append $service_result.errors)
    $warnings = ($warnings | append $service_result.warnings)

    # Validate extensions
    let ext_result = validate-extensions-config $config.extensions
    $errors = ($errors | append $ext_result.errors)
    $warnings = ($warnings | append $ext_result.warnings)

    # Validate workspaces
    let ws_result = validate-workspace-config $config.workspaces
    $errors = ($errors | append $ws_result.errors)
    $warnings = ($warnings | append $ws_result.warnings)

    # Validate security
    let sec_result = validate-security-config $config.security
    $errors = ($errors | append $sec_result.errors)
    $warnings = ($warnings | append $sec_result.warnings)

    # Determine success
    let has_errors = (($errors | length) > 0)
    let has_warnings = (($warnings | length) > 0)
    let success = if $strict {
        not $has_errors and not $has_warnings
    } else {
        not $has_errors
    }

    {
        mode: $mode_name
        success: $success
        errors: $errors
        warnings: $warnings
        error_count: ($errors | length)
        warning_count: ($warnings | length)
    }
}

# Validate mode structure
def validate-structure [config: record] -> record {
    mut errors = []
    mut warnings = []

    # Required top-level fields
    let required = ["mode", "description", "authentication", "services", "extensions", "workspaces", "security"]

    for field in $required {
        if not ($field in $config) {
            $errors = ($errors | append $"Missing required field: ($field)")
        }
    }

    # Validate mode name
    if "mode" in $config {
        let valid_modes = ["solo", "multi-user", "cicd", "enterprise"]
        if not ($config.mode in $valid_modes) {
            $errors = ($errors | append $"Invalid mode: ($config.mode). Must be one of: ($valid_modes | str join ', ')")
        }
    }

    {errors: $errors, warnings: $warnings}
}

# Validate authentication configuration
def validate-authentication-config [auth: record] -> record {
    mut errors = []
    mut warnings = []

    if not ("type" in $auth) {
        $errors = ($errors | append "Authentication type not specified")
        return {errors: $errors, warnings: $warnings}
    }

    let valid_types = ["none", "token", "mtls", "oauth", "kms"]
    if not ($auth.type in $valid_types) {
        $errors = ($errors | append $"Invalid authentication type: ($auth.type)")
    }

    # Type-specific validation
    match $auth.type {
        "token" => {
            if not ("token_path" in $auth) {
                $errors = ($errors | append "Token path required for token authentication")
            }
        }
        "mtls" => {
            if not ("mtls" in $auth) {
                $errors = ($errors | append "mTLS configuration required")
            } else {
                let mtls = $auth.mtls
                let required_certs = ["client_cert_path", "client_key_path", "ca_cert_path"]
                for cert in $required_certs {
                    if not ($cert in $mtls) {
                        $errors = ($errors | append $"Missing mTLS field: ($cert)")
                    }
                }
            }
        }
        "oauth" => {
            if not ("oauth" in $auth) {
                $errors = ($errors | append "OAuth configuration required")
            } else {
                let oauth = $auth.oauth
                if not ("provider_url" in $oauth) {
                    $errors = ($errors | append "OAuth provider_url required")
                }
                if not ("client_id" in $oauth) {
                    $errors = ($errors | append "OAuth client_id required")
                }
            }
        }
    }

    # SSH key storage validation
    if "ssh_key_storage" in $auth {
        let valid_storage = ["local", "kms", "vault"]
        if not ($auth.ssh_key_storage in $valid_storage) {
            $errors = ($errors | append $"Invalid ssh_key_storage: ($auth.ssh_key_storage)")
        }

        if ($auth.ssh_key_storage == "kms") and not ("kms" in $auth) {
            $warnings = ($warnings | append "SSH key storage set to KMS but no KMS configuration provided")
        }
    }

    {errors: $errors, warnings: $warnings}
}

# Validate services configuration
def validate-services-config [services: record] -> record {
    mut errors = []
    mut warnings = []

    # Required services
    let required_services = ["orchestrator", "oci-registry"]

    for service in $required_services {
        if not ($service in $services) {
            $errors = ($errors | append $"Missing required service: ($service)")
        } else {
            let svc = $services | get $service
            if not ("deployment" in $svc) {
                $errors = ($errors | append $"Service ($service) missing 'deployment' field")
            } else {
                let valid_deployments = ["local", "remote", "k8s", "disabled"]
                if not ($svc.deployment in $valid_deployments) {
                    $errors = ($errors | append $"Invalid deployment type for ($service): ($svc.deployment)")
                }

                # Deployment-specific validation
                match $svc.deployment {
                    "local" => {
                        if not ("data_dir" in $svc) and not ("local_config" in $svc) {
                            $warnings = ($warnings | append $"Local service ($service) should specify data_dir")
                        }
                        if not ("port" in $svc) {
                            $warnings = ($warnings | append $"Local service ($service) should specify port")
                        }
                    }
                    "remote" => {
                        if not ("endpoint" in $svc) and not ("remote_config" in $svc) {
                            $errors = ($errors | append $"Remote service ($service) must specify endpoint")
                        }
                    }
                    "k8s" => {
                        if not ("namespace" in $svc) and not ("k8s_config" in $svc) {
                            $errors = ($errors | append $"K8s service ($service) must specify namespace")
                        }
                        if not ("deployment_name" in $svc) and not ("k8s_config" in $svc) {
                            $errors = ($errors | append $"K8s service ($service) must specify deployment_name")
                        }
                    }
                }
            }
        }
    }

    # Validate OCI registry specific fields
    if "oci-registry" in $services {
        let oci = $services."oci-registry"
        if $oci.deployment != "disabled" {
            if not ("type" in $oci) {
                $errors = ($errors | append "OCI registry type not specified")
            } else {
                let valid_types = ["distribution", "zot", "harbor", "artifactory"]
                if not ($oci.type in $valid_types) {
                    $errors = ($errors | append $"Invalid OCI registry type: ($oci.type)")
                }
            }

            if not ("endpoint" in $oci) {
                $errors = ($errors | append "OCI registry endpoint not specified")
            }

            if "namespaces" in $oci {
                let ns = $oci.namespaces
                let required_ns = ["extensions", "nickel_packages", "platform_images", "test_images"]
                for n in $required_ns {
                    if not ($n in $ns) {
                        $warnings = ($warnings | append $"OCI registry namespace missing: ($n)")
                    }
                }
            } else {
                $warnings = ($warnings | append "OCI registry namespaces not configured")
            }
        }
    }

    {errors: $errors, warnings: $warnings}
}

# Validate extensions configuration
def validate-extensions-config [extensions: record] -> record {
    mut errors = []
    mut warnings = []

    if not ("source" in $extensions) {
        $errors = ($errors | append "Extension source not specified")
        return {errors: $errors, warnings: $warnings}
    }

    let valid_sources = ["local", "gitea", "oci", "mixed"]
    if not ($extensions.source in $valid_sources) {
        $errors = ($errors | append $"Invalid extension source: ($extensions.source)")
    }

    # Source-specific validation
    match $extensions.source {
        "local" => {
            if not ("local_path" in $extensions) {
                $warnings = ($warnings | append "Local extension source should specify local_path")
            }
        }
        "gitea" => {
            if not ("gitea_config" in $extensions) {
                $errors = ($errors | append "Gitea configuration required for gitea source")
            }
        }
        "oci" => {
            if not ("oci_registry" in $extensions) {
                $errors = ($errors | append "OCI registry configuration required for oci source")
            } else {
                if not $extensions.oci_registry.enabled {
                    $warnings = ($warnings | append "OCI source selected but OCI registry not enabled")
                }
            }
        }
    }

    {errors: $errors, warnings: $warnings}
}

# Validate workspace configuration
def validate-workspace-config [workspaces: record] -> record {
    mut errors = []
    mut warnings = []

    if not ("locking" in $workspaces) {
        $errors = ($errors | append "Workspace locking policy not specified")
        return {errors: $errors, warnings: $warnings}
    }

    let valid_locking = ["disabled", "enabled", "required"]
    if not ($workspaces.locking in $valid_locking) {
        $errors = ($errors | append $"Invalid locking policy: ($workspaces.locking)")
    }

    if ($workspaces.locking in ["enabled", "required"]) {
        if not ("lock_provider" in $workspaces) {
            $errors = ($errors | append "Lock provider required when locking is enabled")
        } else {
            let valid_providers = ["gitea", "etcd", "redis", "filesystem"]
            if not ($workspaces.lock_provider in $valid_providers) {
                $errors = ($errors | append $"Invalid lock provider: ($workspaces.lock_provider)")
            }
        }
    }

    if not ("git_integration" in $workspaces) {
        $warnings = ($warnings | append "Git integration policy not specified")
    } else {
        let valid_git = ["disabled", "optional", "required"]
        if not ($workspaces.git_integration in $valid_git) {
            $errors = ($errors | append $"Invalid git integration: ($workspaces.git_integration)")
        }
    }

    if not ("isolation" in $workspaces) {
        $warnings = ($warnings | append "Workspace isolation not specified, using default")
    }

    {errors: $errors, warnings: $warnings}
}

# Validate security configuration
def validate-security-config [security: record] -> record {
    mut errors = []
    mut warnings = []

    # Check encryption settings
    if not ("encryption_at_rest" in $security) {
        $warnings = ($warnings | append "Encryption at rest not specified")
    }

    if not ("encryption_in_transit" in $security) {
        $warnings = ($warnings | append "Encryption in transit not specified")
    }

    # Validate DNS modification
    if "dns_modification" in $security {
        let valid_dns = ["none", "coredns", "system"]
        if not ($security.dns_modification in $valid_dns) {
            $errors = ($errors | append $"Invalid dns_modification: ($security.dns_modification)")
        }
    }

    # Validate audit logging
    if "audit_logging" in $security and $security.audit_logging {
        if not ("audit_log_path" in $security) {
            $errors = ($errors | append "Audit log path required when audit logging enabled")
        }
    }

    # Validate secret provider
    if "secret_provider" in $security {
        let sp = $security.secret_provider
        if not ("type" in $sp) {
            $errors = ($errors | append "Secret provider type not specified")
        } else {
            let valid_types = ["sops", "kms"]
            if not ($sp.type in $valid_types) {
                $errors = ($errors | append $"Invalid secret provider type: ($sp.type)")
            }

            # Type-specific validation
            if ($sp.type == "sops") and not ("age_key_file" in $sp) {
                $warnings = ($warnings | append "SOPS configured but age_key_file not specified")
            }

            if ($sp.type == "kms") and not ("server_url" in $sp) {
                $errors = ($errors | append "KMS server_url required for KMS secret provider")
            }
        }
    }

    {errors: $errors, warnings: $warnings}
}

# Check runtime requirements for mode
export def check-runtime-requirements [
    mode_name: string
] -> record {
    let config_file = get-mode-file $mode_name
    let config = open $config_file

    mut checks = []

    # Check for required binaries
    match $config.services.orchestrator.deployment {
        "local" => {
            let orch_check = (which orchestrator | length) > 0
            $checks = ($checks | append {
                check: "Orchestrator binary"
                required: true
                available: $orch_check
                message: if $orch_check { "Found" } else { "Not found in PATH" }
            })
        }
    }

    # Check for OCI registry tools
    if $config.services.oci-registry.deployment == "local" {
        let registry_type = $config.services.oci-registry.type
        let bin_check = (which $registry_type | length) > 0
        $checks = ($checks | append {
            check: $"OCI registry binary \(($registry_type))"
            required: false
            available: $bin_check
            message: if $bin_check { "Found" } else { $"($registry_type) not found in PATH" }
        })
    }

    # Check for authentication requirements
    match $config.authentication.type {
        "token" => {
            let token_path = $config.authentication.token_path | str replace "~" $env.HOME
            let token_exists = ($token_path | path exists)
            $checks = ($checks | append {
                check: "Authentication token"
                required: true
                available: $token_exists
                message: if $token_exists { $"Found at ($token_path)" } else { $"Not found at ($token_path)" }
            })
        }
        "mtls" => {
            if "mtls" in $config.authentication {
                let cert_path = $config.authentication.mtls.client_cert_path
                let cert_exists = ($cert_path | path exists)
                $checks = ($checks | append {
                    check: "mTLS client certificate"
                    required: true
                    available: $cert_exists
                    message: if $cert_exists { $"Found at ($cert_path)" } else { $"Not found at ($cert_path)" }
                })
            }
        }
    }

    # Check for Kubernetes (if using k8s deployment)
    if ($config.services.orchestrator.deployment == "k8s") {
        let kubectl_check = (which kubectl | length) > 0
        $checks = ($checks | append {
            check: "kubectl"
            required: true
            available: $kubectl_check
            message: if $kubectl_check { "Found" } else { "Not found in PATH" }
        })

        if $kubectl_check {
            let cluster_check = (do { kubectl cluster-info } | complete).exit_code == 0
            $checks = ($checks | append {
                check: "Kubernetes cluster access"
                required: true
                available: $cluster_check
                message: if $cluster_check { "Connected" } else { "Cannot connect to cluster" }
            })
        }
    }

    let all_required_met = ($checks | where required | all {|c| $c.available })

    {
        mode: $mode_name
        all_requirements_met: $all_required_met
        checks: $checks
    }
}

# Helper: Get mode file path
def get-mode-file [mode: string] -> string {
    let modes_dir = $env.PROVISIONING_PROJECT_ROOT? | default (pwd) | path join "workspace" "config" "modes"
    $modes_dir | path join $"($mode).yaml"
}
