# Integration Test Helpers
# Common utilities for integration testing

use std log

# Test configuration
export def load-test-config [] {
    let config_path = $"($env.PWD)/provisioning/tests/integration/test_config.yaml"

    if not ($config_path | path exists) {
        error make {
            msg: "Test configuration not found"
            label: {
                text: $"Config file not found: ($config_path)"
                span: (metadata $config_path).span
            }
        }
    }

    open $config_path
}

# Assertion helpers
export def assert-eq [actual: any, expected: any, message: string = ""] {
    if $actual != $expected {
        let error_msg = if ($message | is-empty) {
            $"Assertion failed: expected ($expected), got ($actual)"
        } else {
            $"($message): expected ($expected), got ($actual)"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $actual).span
            }
        }
    }
}

export def assert-true [condition: bool, message: string = ""] {
    if not $condition {
        let error_msg = if ($message | is-empty) {
            "Assertion failed: expected true, got false"
        } else {
            $"($message): expected true, got false"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $condition).span
            }
        }
    }
}

export def assert-false [condition: bool, message: string = ""] {
    if $condition {
        let error_msg = if ($message | is-empty) {
            "Assertion failed: expected false, got true"
        } else {
            $"($message): expected false, got true"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $condition).span
            }
        }
    }
}

export def assert-contains [haystack: list, needle: any, message: string = ""] {
    if not ($needle in $haystack) {
        let error_msg = if ($message | is-empty) {
            $"Assertion failed: ($haystack) does not contain ($needle)"
        } else {
            $"($message): ($haystack) does not contain ($needle)"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $haystack).span
            }
        }
    }
}

export def assert-not-contains [haystack: list, needle: any, message: string = ""] {
    if $needle in $haystack {
        let error_msg = if ($message | is-empty) {
            $"Assertion failed: ($haystack) contains ($needle)"
        } else {
            $"($message): ($haystack) contains ($needle)"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $haystack).span
            }
        }
    }
}

export def assert-not-empty [value: any, message: string = ""] {
    let is_empty = match ($value | describe) {
        "string" => { ($value | str length) == 0 }
        "list" => { ($value | length) == 0 }
        "record" => { ($value | columns | length) == 0 }
        _ => { false }
    }

    if $is_empty {
        let error_msg = if ($message | is-empty) {
            "Assertion failed: value is empty"
        } else {
            $"($message): value is empty"
        }

        error make {
            msg: $error_msg
            label: {
                text: "Assertion failed"
                span: (metadata $value).span
            }
        }
    }
}

export def assert-http-success [response: record, message: string = ""] {
    let status = $response.status

    if $status < 200 or $status >= 300 {
        let error_msg = if ($message | is-empty) {
            $"HTTP request failed with status ($status)"
        } else {
            $"($message): HTTP request failed with status ($status)"
        }

        error make {
            msg: $error_msg
            label: {
                text: "HTTP assertion failed"
                span: (metadata $response).span
            }
        }
    }
}

# Test fixture helpers
export def create-test-workspace [name: string, config: record] {
    let test_config = (load-test-config)
    let workspace_path = $"($test_config.test_workspace.path)-($name)"

    # Create workspace directory structure
    mkdir $workspace_path
    mkdir $"($workspace_path)/config"
    mkdir $"($workspace_path)/infra"
    mkdir $"($workspace_path)/extensions"
    mkdir $"($workspace_path)/runtime"

    # Write workspace config
    let workspace_config = {
        workspace: {
            name: $name
            version: "1.0.0"
            created: (date now | format date "%Y-%m-%d")
        }
        settings: $config
    }

    $workspace_config | save -f $"($workspace_path)/config/provisioning.yaml"

    {
        name: $name
        path: $workspace_path
        config: $workspace_config
    }
}

export def cleanup-test-workspace [workspace: record] {
    if ($workspace.path | path exists) {
        rm -rf $workspace.path
        log info $"Cleaned up test workspace: ($workspace.name)"
    }
}

export def create-test-server [
    name: string
    provider: string = "local"
    config: record = {}
] {
    let test_config = (load-test-config)

    # Prepare server configuration
    let server_config = {
        hostname: $name
        provider: $provider
        cores: ($config.cores? | default 2)
        memory: ($config.memory? | default 4096)
        storage: ($config.storage? | default 50)
        zone: ($config.zone? | default "local")
    }

    # Call orchestrator API to create server
    let orchestrator_url = $"http://($test_config.services.orchestrator.host):($test_config.services.orchestrator.port)"

    let response = (http post $"($orchestrator_url)/workflows/servers/create" {
        server: $server_config
        check: true
    })

    assert-http-success $response "Server creation failed"

    $response.body
}

export def delete-test-server [server_id: string] {
    let test_config = (load-test-config)
    let orchestrator_url = $"http://($test_config.services.orchestrator.host):($test_config.services.orchestrator.port)"

    let response = (http delete $"($orchestrator_url)/servers/($server_id)")

    assert-http-success $response "Server deletion failed"

    $response.body
}

# Retry helper for flaky operations
export def with-retry [
    --max-attempts: int = 3
    --delay: int = 5
    --backoff-multiplier: float = 2.0
    operation: closure
] {
    mut attempts = 0
    mut current_delay = $delay
    mut last_error = null

    while $attempts < $max_attempts {
        try {
            return (do $operation)
        } catch { |err|
            $attempts = $attempts + 1
            $last_error = $err

            if $attempts < $max_attempts {
                log warning $"Attempt ($attempts) failed: ($err.msg). Retrying in ($current_delay)s..."
                sleep ($current_delay * 1sec)
                $current_delay = ($current_delay * $backoff_multiplier | into int)
            }
        }
    }

    error make {
        msg: $"Operation failed after ($max_attempts) attempts: ($last_error.msg)"
        label: {
            text: "Retry exhausted"
            span: (metadata $operation).span
        }
    }
}

# Wait for condition with timeout
export def wait-for-condition [
    --timeout: int = 60
    --interval: int = 5
    condition: closure
    description: string = "condition"
] {
    let start_time = (date now)
    let timeout_duration = ($timeout * 1sec)

    while true {
        try {
            let result = (do $condition)
            if $result {
                return true
            }
        } catch { |err|
            # Ignore errors, keep waiting
        }

        let elapsed = ((date now) - $start_time)
        if $elapsed > $timeout_duration {
            error make {
                msg: $"Timeout waiting for ($description) after ($timeout)s"
                label: {
                    text: "Timeout"
                    span: (metadata $condition).span
                }
            }
        }

        sleep ($interval * 1sec)
    }
}

# Service health check helpers
export def check-service-health [service_name: string] {
    let test_config = (load-test-config)

    let service_config = match $service_name {
        "orchestrator" => { $test_config.services.orchestrator }
        "coredns" => { $test_config.services.coredns }
        "gitea" => { $test_config.services.gitea }
        "postgres" => { $test_config.services.postgres }
        "prometheus" => { $test_config.services.prometheus }
        "grafana" => { $test_config.services.grafana }
        _ => {
            error make {
                msg: $"Unknown service: ($service_name)"
            }
        }
    }

    match $service_name {
        "orchestrator" => {
            let url = $"http://($service_config.host):($service_config.port)($service_config.health_endpoint)"
            try {
                let response = (http get $url)
                $response.status == 200
            } catch {
                false
            }
        }
        "coredns" => {
            # Check DNS resolution
            try {
                dig @$service_config.host test.local | complete | get exit_code | $in == 0
            } catch {
                false
            }
        }
        "gitea" => {
            let url = $"http://($service_config.host):($service_config.port)/api/v1/version"
            try {
                let response = (http get $url)
                $response.status == 200
            } catch {
                false
            }
        }
        "postgres" => {
            try {
                psql -h $service_config.host -p $service_config.port -U $service_config.username -d $service_config.database -c "SELECT 1" | complete | get exit_code | $in == 0
            } catch {
                false
            }
        }
        _ => {
            let url = $"http://($service_config.host):($service_config.port)/"
            try {
                let response = (http get $url)
                $response.status == 200
            } catch {
                false
            }
        }
    }
}

export def wait-for-service [service_name: string, timeout: int = 60] {
    wait-for-condition --timeout $timeout --interval 5 {
        check-service-health $service_name
    } $"service ($service_name) to be healthy"
}

# Test result tracking
export def create-test-result [
    test_name: string
    status: string  # "passed", "failed", "skipped"
    duration_ms: int
    error_message: string = ""
] {
    {
        test_name: $test_name
        status: $status
        duration_ms: $duration_ms
        error_message: $error_message
        timestamp: (date now | format date "%Y-%m-%dT%H:%M:%S%.3fZ")
    }
}

# Test runner helpers
export def run-test [
    test_name: string
    test_fn: closure
] {
    log info $"Running test: ($test_name)"

    let start_time = (date now)

    try {
        do $test_fn

        let duration = ((date now) - $start_time | into int) / 1000000

        log info $"✓ Test passed: ($test_name) \(($duration)ms\)"

        create-test-result $test_name "passed" $duration
    } catch { |err|
        let duration = ((date now) - $start_time | into int) / 1000000

        log error $"✗ Test failed: ($test_name) - ($err.msg)"

        create-test-result $test_name "failed" $duration $err.msg
    }
}

# Cleanup helpers
export def cleanup-on-exit [cleanup_fn: closure] {
    # Register cleanup function to run on exit
    # Note: Nushell doesn't have built-in exit hooks, so this is a best-effort approach
    try {
        do $cleanup_fn
    } catch { |err|
        log warning $"Cleanup failed: ($err.msg)"
    }
}
