# Enhanced Check Mode for Taskservs
# Provides dry-run capabilities with detailed validation and preview

# REMOVED: use lib_provisioning * - causes circular import
use utils.nu *
use deps_validator.nu *
use validate.nu *
use ../lib_provisioning/config/accessor.nu *
use ../lib_provisioning/utils/ssh.nu [scp_to, ssh_cmd]

# Preview taskserv configuration generation
def preview-config-generation [
    taskserv_name: string
    taskserv_profile: string
    settings: record
    server: record
    --verbose (-v)
] {
    let taskservs_path = (get-taskservs-path)
    let taskserv_dir = (find-taskserv-dir $taskservs_path $taskserv_name)
    let profile_path = if ($taskserv_dir | is-not-empty) { $taskserv_dir | path join $taskserv_profile } else { "" }

    if not ($profile_path | path exists) {
        return {
            valid: false
            errors: [$"Profile path not found: ($profile_path)"]
            warnings: []
            files: []
        }
    }

    # Find all template files
    let template_files = (glob ($profile_path | path join "**/*.j2"))

    # Find shell scripts
    let script_files = (glob ($profile_path | path join "**/*.sh"))

    # Find other config files
    let config_files = (do -i {
        ls $profile_path
        | where type == "file"
        | where name !~ ".j2$"
        | where name !~ ".sh$"
        | get name
    } | default [])

    mut preview_files = []

    # Preview templates
    for tpl in $template_files {
        let dest_name = ($tpl | path basename | str replace ".j2" "")
        $preview_files = ($preview_files | append {
            type: "template"
            source: ($tpl | path relative-to $profile_path)
            destination: $dest_name
            action: "render and upload"
        })
    }

    # Preview scripts
    for script in $script_files {
        $preview_files = ($preview_files | append {
            type: "script"
            source: ($script | path basename)
            destination: ($script | path basename)
            action: "upload and execute"
        })
    }

    # Preview config files
    for cfg in $config_files {
        $preview_files = ($preview_files | append {
            type: "config"
            source: ($cfg | path basename)
            destination: ($cfg | path basename)
            action: "upload"
        })
    }

    return {
        valid: true
        errors: []
        warnings: []
        files: $preview_files
        total_files: ($preview_files | length)
    }
}

# Check prerequisites on target server (without actually connecting in check mode)
def check-prerequisites [
    taskserv_name: string
    server: record
    settings: record
    check_mode: bool
] {
    mut checks = []

    # Check if server is accessible (in check mode, just validate config)
    if $check_mode {
        $checks = ($checks | append {
            check: "Server accessibility"
            status: "skipped"
            message: "Check mode - SSH not tested"
        })
    } else {
        # In real mode, this would test SSH connection
        $checks = ($checks | append {
            check: "Server accessibility"
            status: "pending"
            message: "Would test SSH connection"
        })
    }

    # Check if required directories exist (preview only in check mode)
    let required_dirs = ["/tmp", "/etc", "/usr/local/bin"]
    for dir in $required_dirs {
        $checks = ($checks | append {
            check: $"Directory ($dir)"
            status: "info"
            message: $"Would verify directory exists"
        })
    }

    # Check if required commands are available
    let required_commands = ["bash", "systemctl"]
    for cmd in $required_commands {
        $checks = ($checks | append {
            check: $"Command ($cmd)"
            status: "info"
            message: $"Would verify command is available"
        })
    }

    return {
        checks: $checks
        total_checks: ($checks | length)
    }
}

# Enhanced check mode handler
export def run-check-mode [
    taskserv_name: string
    taskserv_profile: string
    settings: record
    server: record
    --verbose (-v)
] {
    _print $"\n(_ansi cyan_bold)Check Mode: ($taskserv_name)(_ansi reset) on (_ansi green_bold)($server.hostname)(_ansi reset)"

    mut results = {
        taskserv: $taskserv_name
        profile: $taskserv_profile
        server: $server.hostname
        validations: []
        overall_valid: true
    }

    # 1. Static validation
    _print $"\n(_ansi yellow)→ Running static validation...(_ansi reset)"
    let static_validation = (run-static-validation $taskserv_name --verbose=$verbose)

    let static_valid = (
        $static_validation.nickel.valid and
        $static_validation.templates.valid and
        $static_validation.scripts.valid
    )

    if $static_valid {
        _print $"  (_ansi green)✓ Static validation passed(_ansi reset)"
    } else {
        _print $"  (_ansi red)✗ Static validation failed(_ansi reset)"
        $results.overall_valid = false
    }

    $results.validations = ($results.validations | append {
        level: "static"
        valid: $static_valid
        details: $static_validation
    })

    # 2. Dependency validation
    _print $"\n(_ansi yellow)→ Checking dependencies...(_ansi reset)"
    let deps_validation = (validate-dependencies $taskserv_name $settings --verbose=$verbose)

    if $deps_validation.valid {
        _print $"  (_ansi green)✓ Dependencies OK(_ansi reset)"
        if ($deps_validation.warnings | default [] | length) > 0 {
            _print $"    Warnings: (($deps_validation.warnings | str join ', '))"
        }
    } else {
        _print $"  (_ansi red)✗ Dependency issues found(_ansi reset)"
        for err in ($deps_validation.errors | default []) {
            _print $"    (_ansi red)✗(_ansi reset) ($err)"
        }
        $results.overall_valid = false
    }

    $results.validations = ($results.validations | append {
        level: "dependencies"
        valid: $deps_validation.valid
        details: $deps_validation
    })

    # 3. Preview configuration generation
    _print $"\n(_ansi yellow)→ Previewing configuration generation...(_ansi reset)"
    let config_preview = (preview-config-generation $taskserv_name $taskserv_profile $settings $server --verbose=$verbose)

    if $config_preview.valid {
        _print $"  (_ansi green)✓ Configuration preview generated(_ansi reset)"
        _print $"    Files to process: ($config_preview.total_files)"

        if $verbose and ($config_preview.files | length) > 0 {
            _print $"\n    Files to be deployed:"
            for file in $config_preview.files {
                _print $"      ($file.type): ($file.source) → ($file.destination)"
            }
        }
    } else {
        _print $"  (_ansi red)✗ Configuration preview failed(_ansi reset)"
        $results.overall_valid = false
    }

    $results.validations = ($results.validations | append {
        level: "configuration"
        valid: $config_preview.valid
        details: $config_preview
    })

    # 4. Prerequisites check
    _print $"\n(_ansi yellow)→ Checking prerequisites...(_ansi reset)"
    let prereq_check = (check-prerequisites $taskserv_name $server $settings true)
    let mode_label = "(preview mode)"
    _print $"  (_ansi blue)ℹ(_ansi reset) Prerequisite checks ($mode_label):"
    for check in $prereq_check.checks {
        let icon = match $check.status {
            "passed" => $"(_ansi green)✓(_ansi reset)"
            "failed" => $"(_ansi red)✗(_ansi reset)"
            "info" => $"(_ansi blue)ℹ(_ansi reset)"
            "skipped" => $"(_ansi yellow)⊘(_ansi reset)"
            _ => "•"
        }
        _print $"    ($icon) ($check.check): ($check.message)"
    }

    $results.validations = ($results.validations | append {
        level: "prerequisites"
        valid: true
        details: $prereq_check
    })

    # Summary
    _print $"\n(_ansi cyan_bold)Check Mode Summary(_ansi reset)"
    if $results.overall_valid {
        _print $"(_ansi green_bold)✓ All validations passed(_ansi reset)"
        _print $"\n💡 Taskserv can be deployed with: (_ansi cyan)provisioning taskserv create ($taskserv_name)(_ansi reset)"
    } else {
        _print $"(_ansi red_bold)✗ Validation failed(_ansi reset)"
        _print $"\n🛑 Fix the errors above before deploying"
    }

    return $results
}

# Print detailed check mode report
export def print-check-report [
    results: record
    --format: string = "text"
] {
    match $format {
        "json" => {
            $results | to json
        }
        "yaml" => {
            $results | to yaml
        }
        _ => {
            # Text format already printed by run-check-mode
            null
        }
    }
}

# Upload taskserv scripts to server for inspection WITHOUT executing them.
# defs must include: settings, server, taskserv, ip (real), taskserv_dir, taskserv_profile
export def run-upload-inspection [
    defs: record
    --verbose (-v)
]: nothing -> record {
    let name = $defs.taskserv.name
    let check_dir = $"/tmp/prvng-check/($name)"
    let ip = $defs.ip
    let profile_path = ($defs.taskserv_dir | path join $defs.taskserv_profile)

    _print $"\n(_ansi cyan_bold)Upload Inspection: ($name)(_ansi reset) → (_ansi green_bold)($defs.server.hostname)(_ansi reset) [($ip)]"

    if not ($profile_path | path exists) {
        _print $"  (_ansi red)✗(_ansi reset) Profile path not found: ($profile_path)"
        return {
            valid: false
            check_dir: $check_dir
            uploaded_files: []
            syntax_ok: false
            errors: [$"Profile path not found: ($profile_path)"]
        }
    }

    # Enumerate local files to report
    let file_list = (do -i { ls $profile_path | where type == "file" | get name } | default [])

    # Pack profile dir into local temp tar
    let tar_path = $"/tmp/prvng-check-($name).tar.gz"
    let pack_result = (do { ^tar -C $profile_path -czf $tar_path . } | complete)
    if $pack_result.exit_code != 0 {
        _print $"  (_ansi red)✗(_ansi reset) Failed to pack: ($pack_result.stderr)"
        return { valid: false, check_dir: $check_dir, uploaded_files: [], syntax_ok: false, errors: ["Pack failed"] }
    }

    # SSH: create inspection directory
    if not (ssh_cmd $defs.settings $defs.server false $"mkdir -p ($check_dir)" $ip) {
        rm -f $tar_path
        _print $"  (_ansi red)✗(_ansi reset) SSH connection failed — cannot create ($check_dir)"
        return { valid: false, check_dir: $check_dir, uploaded_files: [], syntax_ok: false, errors: ["SSH mkdir failed"] }
    }

    # SCP: upload tar to /tmp on server
    if not (scp_to $defs.settings $defs.server [$tar_path] "/tmp" $ip) {
        rm -f $tar_path
        _print $"  (_ansi red)✗(_ansi reset) SCP upload failed"
        return { valid: false, check_dir: $check_dir, uploaded_files: [], syntax_ok: false, errors: ["SCP failed"] }
    }
    rm -f $tar_path

    # SSH: extract bundle into check_dir — no execute
    let extract_cmd = $"cd ($check_dir) && tar -xzf /tmp/prvng-check-($name).tar.gz && rm -f /tmp/prvng-check-($name).tar.gz"
    if not (ssh_cmd $defs.settings $defs.server false $extract_cmd $ip) {
        _print $"  (_ansi red)✗(_ansi reset) Extraction on server failed"
        return { valid: false, check_dir: $check_dir, uploaded_files: ($file_list | each { |f| $f | path basename }), syntax_ok: false, errors: ["Extract failed"] }
    }

    # SSH: bash -n syntax check on all uploaded .sh files (no execution)
    let syntax_cmd = $"find ($check_dir) -name '*.sh' -exec bash -n \\{\\} \\;"
    let syntax_ok = (ssh_cmd $defs.settings $defs.server false $syntax_cmd $ip)

    let basenames = ($file_list | each { |f| $f | path basename })

    if $verbose {
        _print $"  Files uploaded from ($profile_path):"
        for f in $basenames {
            _print $"    ($f)"
        }
    }

    let syntax_label = if $syntax_ok {
        $"(_ansi green)✓(_ansi reset) bash -n syntax OK"
    } else {
        $"(_ansi red)✗(_ansi reset) Syntax errors found — see SSH output above"
    }

    _print $"  (_ansi green)✓(_ansi reset) Uploaded to (_ansi cyan)($check_dir)(_ansi reset) — not executed"
    _print $"  ($syntax_label)"
    _print $"  Inspect : (_ansi blue)ssh ($defs.server.installer_user)@($ip) ls -la ($check_dir)/(_ansi reset)"
    _print $"  Cleanup : (_ansi blue)ssh ($defs.server.installer_user)@($ip) rm -rf ($check_dir)(_ansi reset)"

    {
        valid: $syntax_ok
        check_dir: $check_dir
        server: $defs.server.hostname
        ip: $ip
        syntax_ok: $syntax_ok
        uploaded_files: $basenames
        errors: (if $syntax_ok { [] } else { ["Script syntax errors detected remotely"] })
    }
}
