# Schema Validator
# Handles validation of infrastructure configurations against defined schemas
# Error handling: Guard patterns (no try-catch for field access)

# Server configuration schema validation
export def validate_server_schema [config: record] {
    mut issues = []

    # Required fields for server configuration
    let required_fields = [
        "hostname"
        "provider"
        "zone"
        "plan"
    ]

    for field in $required_fields {
        # Guard: Check if field exists in config using columns
        let field_exists = ($field in ($config | columns))
        let field_value = if $field_exists { $config | get $field } else { null }

        if ($field_value | is-empty) {
            $issues = ($issues | append {
                field: $field
                message: $"Required field '($field)' is missing or empty"
                severity: "error"
            })
        }
    }

    # Validate specific field formats
    # Guard: Check if hostname field exists
    if ("hostname" in ($config | columns)) {
        let hostname = ($config | get hostname)
        if not ($hostname =~ '^[a-z0-9][a-z0-9\-]*[a-z0-9]$') {
            $issues = ($issues | append {
                field: "hostname"
                message: "Hostname must contain only lowercase letters, numbers, and hyphens"
                severity: "warning"
                current_value: $hostname
            })
        }
    }

    # Validate provider-specific requirements
    # Guard: Check if provider field exists
    if ("provider" in ($config | columns)) {
        let provider = ($config | get provider)
        let provider_validation = (validate_provider_config $provider $config)
        $issues = ($issues | append $provider_validation.issues)
    }

    # Validate network configuration
    # Guard: Check if network_private_ip field exists
    if ("network_private_ip" in ($config | columns)) {
        let ip = ($config | get network_private_ip)
        let ip_validation = (validate_ip_address $ip)
        if not $ip_validation.valid {
            $issues = ($issues | append {
                field: "network_private_ip"
                message: $ip_validation.message
                severity: "error"
                current_value: $ip
            })
        }
    }

    {
        valid: (($issues | where severity == "error" | length) == 0)
        issues: $issues
    }
}

# Provider-specific configuration validation
export def validate_provider_config [provider: string, config: record] {
    mut issues = []

    match $provider {
        "upcloud" => {
            # UpCloud specific validations
            let required_upcloud_fields = ["ssh_key_path", "storage_os"]
            for field in $required_upcloud_fields {
                # Guard: Check if field exists in config
                if not ($field in ($config | columns)) {
                    $issues = ($issues | append {
                        field: $field
                        message: $"UpCloud provider requires '($field)' field"
                        severity: "error"
                    })
                }
            }

            # Validate UpCloud zones
            let valid_zones = ["es-mad1", "fi-hel1", "fi-hel2", "nl-ams1", "sg-sin1", "uk-lon1", "us-chi1", "us-nyc1", "de-fra1"]
            # Guard: Check if zone field exists
            let zone = if ("zone" in ($config | columns)) { $config | get zone } else { null }
            if ($zone | is-not-empty) and ($zone not-in $valid_zones) {
                $issues = ($issues | append {
                    field: "zone"
                    message: $"Invalid UpCloud zone: ($zone)"
                    severity: "error"
                    current_value: $zone
                    suggested_values: $valid_zones
                })
            }
        }
        "aws" => {
            # AWS specific validations
            let required_aws_fields = ["instance_type", "ami_id"]
            for field in $required_aws_fields {
                # Guard: Check if field exists in config
                if not ($field in ($config | columns)) {
                    $issues = ($issues | append {
                        field: $field
                        message: $"AWS provider requires '($field)' field"
                        severity: "error"
                    })
                }
            }
        }
        "local" => {
            # Local provider specific validations
            # Generally more lenient
        }
        _ => {
            $issues = ($issues | append {
                field: "provider"
                message: $"Unknown provider: ($provider)"
                severity: "error"
                current_value: $provider
                suggested_values: ["upcloud", "aws", "local"]
            })
        }
    }

    { issues: $issues }
}

# Network configuration validation
export def validate_network_config [config: record] {
    mut issues = []

    # Validate CIDR blocks
    # Guard: Check if priv_cidr_block field exists
    if ("priv_cidr_block" in ($config | columns)) {
        let cidr = ($config | get priv_cidr_block)
        let cidr_validation = (validate_cidr_block $cidr)
        if not $cidr_validation.valid {
            $issues = ($issues | append {
                field: "priv_cidr_block"
                message: $cidr_validation.message
                severity: "error"
                current_value: $cidr
            })
        }
    }

    # Check for IP conflicts
    # Guard: Check if both fields exist in config
    if ("network_private_ip" in ($config | columns)) and ("priv_cidr_block" in ($config | columns)) {
        let ip = ($config | get network_private_ip)
        let cidr = ($config | get priv_cidr_block)

        if not (ip_in_cidr $ip $cidr) {
            $issues = ($issues | append {
                field: "network_private_ip"
                message: $"IP ($ip) is not within CIDR block ($cidr)"
                severity: "error"
            })
        }
    }

    {
        valid: (($issues | where severity == "error" | length) == 0)
        issues: $issues
    }
}

# TaskServ configuration validation
export def validate_taskserv_schema [taskserv: record] {
    mut issues = []

    let required_fields = ["name", "install_mode"]

    for field in $required_fields {
        # Guard: Check if field exists in taskserv
        if not ($field in ($taskserv | columns)) {
            $issues = ($issues | append {
                field: $field
                message: $"Required taskserv field '($field)' is missing"
                severity: "error"
            })
        }
    }

    # Validate install mode
    let valid_install_modes = ["library", "container", "binary"]
    # Guard: Check if install_mode field exists
    let install_mode = if ("install_mode" in ($taskserv | columns)) { $taskserv | get install_mode } else { null }
    if ($install_mode | is-not-empty) and ($install_mode not-in $valid_install_modes) {
        $issues = ($issues | append {
            field: "install_mode"
            message: $"Invalid install_mode: ($install_mode)"
            severity: "error"
            current_value: $install_mode
            suggested_values: $valid_install_modes
        })
    }

    # Validate taskserv name exists
    # Guard: Check if name field exists
    let taskserv_name = if ("name" in ($taskserv | columns)) { $taskserv | get name } else { null }
    if ($taskserv_name | is-not-empty) {
        let taskserv_exists = (taskserv_definition_exists $taskserv_name)
        if not $taskserv_exists {
            $issues = ($issues | append {
                field: "name"
                message: $"TaskServ definition not found: ($taskserv_name)"
                severity: "warning"
                current_value: $taskserv_name
            })
        }
    }

    {
        valid: (($issues | where severity == "error" | length) == 0)
        issues: $issues
    }
}

# Helper validation functions

export def validate_ip_address [ip: string] {
    # Basic IP address validation (IPv4)
    if ($ip =~ '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$') {
        let parts = ($ip | split row ".")
        let valid_parts = ($parts | all {|part|
            let num = ($part | into int)
            $num >= 0 and $num <= 255
        })

        if $valid_parts {
            { valid: true, message: "" }
        } else {
            { valid: false, message: "IP address octets must be between 0 and 255" }
        }
    } else {
        { valid: false, message: "Invalid IP address format" }
    }
}

export def validate_cidr_block [cidr: string] {
    if ($cidr =~ '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})$') {
        let parts = ($cidr | split row "/")
        let ip_part = ($parts | get 0)
        let prefix = ($parts | get 1 | into int)

        let ip_valid = (validate_ip_address $ip_part)
        if not $ip_valid.valid {
            return $ip_valid
        }

        if $prefix >= 0 and $prefix <= 32 {
            { valid: true, message: "" }
        } else {
            { valid: false, message: "CIDR prefix must be between 0 and 32" }
        }
    } else {
        { valid: false, message: "Invalid CIDR block format (should be x.x.x.x/y)" }
    }
}

export def ip_in_cidr [ip: string, cidr: string] {
    # Simplified IP in CIDR check
    # This is a basic implementation - a more robust version would use proper IP arithmetic
    let cidr_parts = ($cidr | split row "/")
    let network = ($cidr_parts | get 0)
    let prefix = ($cidr_parts | get 1 | into int)

    # For basic validation, check if IP starts with the same network portion
    # This is simplified and should be enhanced for production use
    if $prefix >= 24 {
        let network_base = ($network | split row "." | take 3 | str join ".")
        let ip_base = ($ip | split row "." | take 3 | str join ".")
        $network_base == $ip_base
    } else {
        # For smaller networks, more complex logic would be needed
        true  # Simplified for now
    }
}

export def taskserv_definition_exists [name: string] {
    # Check if taskserv definition exists in the system
    let taskserv_path = $"taskservs/($name)"
    ($taskserv_path | path exists)
}

# Schema definitions for different resource types
export def get_server_schema [] {
    {
        required_fields: ["hostname", "provider", "zone", "plan"]
        optional_fields: [
            "title", "labels", "ssh_key_path", "storage_os",
            "network_private_ip", "priv_cidr_block", "time_zone",
            "taskservs", "storages"
        ]
        field_types: {
            hostname: "string"
            provider: "string"
            zone: "string"
            plan: "string"
            network_private_ip: "ip_address"
            priv_cidr_block: "cidr"
            taskservs: "list"
        }
    }
}

export def get_taskserv_schema [] {
    {
        required_fields: ["name", "install_mode"]
        optional_fields: ["profile", "target_save_path"]
        field_types: {
            name: "string"
            install_mode: "string"
            profile: "string"
            target_save_path: "string"
        }
    }
}
