#!/usr/bin/env nu

# Pre-flight Check System
# Validates service prerequisites before operations

use manager.nu [load-service-registry is-service-running get-service-definition]
use dependencies.nu [resolve-dependencies get-startup-order]

# Check required services for operation
export def check-required-services [
    operation: string
] {
    let registry = (load-service-registry)

    # Find all services required for this operation
    let required_services = (
        $registry
        | transpose name config
        | where { |row|
            $operation in $row.config.required_for
        }
        | get name
    )

    if ($required_services | is-empty) {
        return {
            operation: $operation
            required_services: []
            all_running: true
            missing_services: []
            can_auto_start: true
            message: "No services required for this operation"
        }
    }

    # Check which services are running
    def partition-services [services: list, running: list, missing: list] {
        if ($services | is-empty) {
            return { running: $running, missing: $missing }
        }
        let first = ($services | get 0)
        let rest = ($services | skip 1)
        if (is-service-running $first) {
            partition-services $rest ($running | append $first) $missing
        } else {
            partition-services $rest $running ($missing | append $first)
        }
    }

    let service_status = (partition-services $required_services [] [])
    let running = $service_status.running
    let missing = $service_status.missing

    # Check if missing services can be auto-started
    let can_auto_start = (
        $missing
        | all { |service|
            let service_def = (get-service-definition $service)
            $service_def.startup.auto_start
        }
    )

    {
        operation: $operation
        required_services: $required_services
        running_services: $running
        missing_services: $missing
        all_running: ($missing | is-empty)
        can_auto_start: $can_auto_start
        message: (if ($missing | is-empty) {
            "All required services are running"
        } else if $can_auto_start {
            $"Services need to be started: ($missing | str join ', ')"
        } else {
            $"Required services not running and cannot auto-start: ($missing | str join ', ')"
        })
    }
}

# Validate service prerequisites
export def validate-service-prerequisites [
    service_name: string
] {
    let service_def = (get-service-definition $service_name)

    # Check deployment mode requirements
    let issues = (
        if $service_def.deployment.mode == "docker" or $service_def.deployment.mode == "docker-compose" {
            # Check if Docker is available
            let docker_check = (do {
                docker --version
            } | complete)

            if $docker_check.exit_code != 0 {
                ["Docker is not installed or not running"]
            } else {
                []
            }
        } else if $service_def.deployment.mode == "kubernetes" {
            # Check if kubectl is available
            let kubectl_check = (do {
                kubectl version --client
            } | complete)

            if $kubectl_check.exit_code != 0 {
                ["kubectl is not installed"]
            } else {
                []
            }
        } else if $service_def.deployment.mode == "binary" {
            # Check if binary exists
            let binary_path = ($service_def.deployment.binary.binary_path | str replace -a '${HOME}' $env.HOME)
            if not ($binary_path | path exists) {
                [$"Binary not found: ($binary_path)"]
            } else {
                []
            }
        } else {
            []
        }
    )

    # Check dependencies
    def check-deps [deps: list, warnings: list] {
        if ($deps | is-empty) {
            return $warnings
        }
        let first = ($deps | get 0)
        let rest = ($deps | skip 1)
        let new_warnings = if (is-service-running $first) {
            $warnings
        } else {
            $warnings | append $"Dependency '($first)' is not running"
        }
        check-deps $rest $new_warnings
    }

    let warnings = (check-deps $service_def.dependencies [])

    # Check conflicts
    def check-conflicts [conflicts: list, issues: list] {
        if ($conflicts | is-empty) {
            return $issues
        }
        let first = ($conflicts | get 0)
        let rest = ($conflicts | skip 1)
        let new_issues = if (is-service-running $first) {
            $issues | append $"Conflicting service '($first)' is running"
        } else {
            $issues
        }
        check-conflicts $rest $new_issues
    }

    let all_issues = (check-conflicts $service_def.conflicts $issues)

    {
        service: $service_name
        valid: ($all_issues | is-empty)
        issues: $all_issues
        warnings: $warnings
        can_start: ($all_issues | is-empty)
        message: (if ($all_issues | is-empty) {
            "All prerequisites met"
        } else {
            $"Prerequisites not met: ($all_issues | str join '; ')"
        })
    }
}

# Auto-start required services
export def auto-start-required-services [
    operation: string
] {
    let check = (check-required-services $operation)

    if $check.all_running {
        return {
            success: true
            message: "All required services already running"
            started_services: []
        }
    }

    if not $check.can_auto_start {
        return {
            success: false
            message: "Some required services cannot be auto-started"
            failed_services: $check.missing_services
        }
    }

    # Get startup order for missing services
    let startup_order = (get-startup-order $check.missing_services)

    print $"Starting required services in order: ($startup_order | str join ' -> ')"

    # Helper to start services in sequence
    def start-services-seq [services: list, started: list, failed: list] {
        if ($services | is-empty) {
            return { started: $started, failed: $failed }
        }
        let first = ($services | get 0)
        let rest = ($services | skip 1)

        print $"Starting [$first]..."

        use manager.nu start-service
        let start_result = (do {
            start-service $first
        } | complete)

        if $start_result.exit_code == 0 and ($start_result.stdout == "true" or $start_result.stdout == true) {
            print $"✅ [$first] started successfully"
            start-services-seq $rest ($started | append $first) $failed
        } else {
            print $"❌ Failed to start [$first]: ($start_result.stderr)"
            { started: $started, failed: ($failed | append $first) }
        }
    }

    let result = (start-services-seq $startup_order [] [])
    let started = $result.started
    let failed = $result.failed

    {
        success: ($failed | is-empty)
        message: (if ($failed | is-empty) {
            $"Successfully started ($started | length) services"
        } else {
            $"Failed to start some services: ($failed | str join ', ')"
        })
        started_services: $started
        failed_services: $failed
    }
}

# Check service conflicts
export def check-service-conflicts [
    service_name: string
] {
    let service_def = (get-service-definition $service_name)

    # Helper to check conflicts
    def find-conflicts [conflicts: list, result: list] {
        if ($conflicts | is-empty) {
            return $result
        }
        let first = ($conflicts | get 0)
        let rest = ($conflicts | skip 1)
        let new_result = if (is-service-running $first) {
            $result | append {
                service: $first
                status: "running"
                action_required: "stop"
            }
        } else {
            $result
        }
        find-conflicts $rest $new_result
    }

    let conflicts = (find-conflicts $service_def.conflicts [])

    {
        service: $service_name
        has_conflicts: (not ($conflicts | is-empty))
        conflicts: $conflicts
        can_start: ($conflicts | is-empty)
        message: (if ($conflicts | is-empty) {
            "No conflicting services running"
        } else {
            $"Conflicting services running: (($conflicts | get service) | str join ', ')"
        })
    }
}

# Validate all services
export def validate-all-services [] {
    let registry = (load-service-registry)

    let validation_results = (
        $registry
        | transpose name config
        | each { |row|
            validate-service-prerequisites $row.name
        }
    )

    let total = ($validation_results | length)
    let valid = ($validation_results | where valid | length)
    let invalid = ($validation_results | where { |r| not $r.valid } | length)

    {
        total_services: $total
        valid_services: $valid
        invalid_services: $invalid
        all_valid: ($invalid == 0)
        results: $validation_results
        message: $"($valid)/($total) services have valid configurations"
    }
}

# Pre-flight check for service start
export def preflight-start-service [
    service_name: string
] {
    print $"Running pre-flight checks for ($service_name)..."

    # 1. Validate prerequisites
    let prereq_check = (validate-service-prerequisites $service_name)
    if not $prereq_check.valid {
        return {
            can_start: false
            reason: "prerequisites"
            details: $prereq_check
        }
    }

    # 2. Check conflicts
    let conflict_check = (check-service-conflicts $service_name)
    if $conflict_check.has_conflicts {
        return {
            can_start: false
            reason: "conflicts"
            details: $conflict_check
        }
    }

    # 3. Check dependencies
    let service_def = (get-service-definition $service_name)

    # Helper to collect missing dependencies
    def collect-missing-deps [deps: list, missing: list] {
        if ($deps | is-empty) {
            return $missing
        }
        let first = ($deps | get 0)
        let rest = ($deps | skip 1)
        let new_missing = if (is-service-running $first) {
            $missing
        } else {
            let dep_def = (get-service-definition $first)
            $missing | append {
                service: $first
                auto_start: $dep_def.startup.auto_start
            }
        }
        collect-missing-deps $rest $new_missing
    }

    let missing_deps = (collect-missing-deps $service_def.dependencies [])

    if not ($missing_deps | is-empty) {
        let can_auto_start = ($missing_deps | all { |d| $d.auto_start })

        return {
            can_start: $can_auto_start
            reason: "dependencies"
            details: {
                missing_dependencies: $missing_deps
                can_auto_start: $can_auto_start
            }
        }
    }

    # All checks passed
    {
        can_start: true
        reason: "all_checks_passed"
        details: {
            message: "Pre-flight checks passed"
        }
    }
}

# Get service readiness report
export def get-readiness-report [] {
    let registry = (load-service-registry)

    let services = (
        $registry
        | transpose name config
        | each { |row|
            let is_running = (is-service-running $row.name)
            let prereq_check = (validate-service-prerequisites $row.name)

            {
                name: $row.name
                type: $row.config.type
                category: $row.config.category
                running: $is_running
                auto_start: $row.config.startup.auto_start
                valid_config: $prereq_check.valid
                can_start: $prereq_check.can_start
                issues: ($prereq_check.issues | length)
            }
        }
    )

    let total = ($services | length)
    let running = ($services | where running | length)
    let ready = ($services | where can_start | length)

    {
        total_services: $total
        running_services: $running
        ready_to_start: $ready
        services: $services
        summary: $"($running) running, ($ready) ready to start out of ($total) total"
    }
}
