# CoreDNS Service Manager
# Start, stop, and manage CoreDNS service

# ../utils/log.nu does not exist — dangling import removed (ADR-025 L2).
use lib_provisioning/coredns/corefile.nu [generate-corefile write-corefile]
use zones.nu create-zone-file

# Start CoreDNS service
export def start-coredns [
    config: record           # CoreDNS configuration
    --foreground (-f)        # Run in foreground
    --check                  # Check mode (don't actually start)
] -> bool {
    log info "Starting CoreDNS service"

    if $check {
        log info "Check mode: Would start CoreDNS"
        return true
    }

    let mode = $config.mode? | default "local"

    if $mode == "disabled" {
        log warn "CoreDNS is disabled in configuration"
        return false
    }

    if $mode != "local" {
        log warn $"Cannot start CoreDNS in ($mode) mode"
        return false
    }

    let local_config = $config.local? | default {}
    let deployment_type = $local_config.deployment_type? | default "binary"

    match $deployment_type {
        "binary" => { start-coredns-binary $config $foreground }
        "docker" => { start-coredns-docker $config }
        _ => {
            log error $"Unknown deployment type: ($deployment_type)"
            false
        }
    }
}

# Start CoreDNS as binary
def start-coredns-binary [
    config: record
    foreground: bool
] -> bool {
    let local_config = $config.local? | default {}
    let binary_path = $local_config.binary_path? | default "~/.provisioning/bin/coredns" | path expand
    let config_path = $local_config.config_path? | default "~/.provisioning/coredns/Corefile" | path expand
    let zones_path = $local_config.zones_path? | default "~/.provisioning/coredns/zones" | path expand
    let pid_file = $local_config.pid_file? | default "~/.provisioning/coredns/coredns.pid" | path expand
    let log_file = $local_config.log_file? | default "~/.provisioning/coredns/coredns.log" | path expand

    # Check if CoreDNS binary exists
    if not ($binary_path | path exists) {
        log error $"CoreDNS binary not found at ($binary_path)"
        log info "Install CoreDNS with: provisioning dns install"
        return false
    }

    # Check if already running
    if is-coredns-running {
        log warn "CoreDNS is already running"
        return false
    }

    # Ensure directories exist
    let config_dir = $config_path | path dirname
    let zones_dir = $zones_path
    let log_dir = $log_file | path dirname

    mkdir $config_dir
    mkdir $zones_dir
    mkdir $log_dir

    # Generate Corefile
    log info "Generating Corefile"
    let corefile_content = generate-corefile $config
    write-corefile $config_path $corefile_content

    # Create zone files if they don't exist
    let zones = $local_config.zones? | default ["provisioning.local"]

    for zone in $zones {
        let zone_file = $"($zones_path)/($zone).zone"

        if not ($zone_file | path exists) {
            log info $"Creating zone file for ($zone)"
            create-zone-file $zone $zones_path --config $config
        }
    }

    # Start CoreDNS
    if $foreground {
        log info "Starting CoreDNS in foreground"

        let result = (do {
            ^$binary_path -conf $config_path
        } | complete)

        if $result.exit_code == 0 {
            true
        } else {
            log error "Failed to start CoreDNS"
            false
        }
    } else {
        log info "Starting CoreDNS in background"

        let result = (do {
            # Start in background and capture PID
            nu -c $"^($binary_path) -conf ($config_path) > ($log_file) 2>&1 & echo $\"($env.LAST_EXIT_CODE)\""
        } | complete)

        if $result.exit_code != 0 {
            log error "Failed to start CoreDNS"
            return false
        }

        # Give it a moment to start
        sleep 1sec

        # Check if process is running
        if is-coredns-running {
            log info "CoreDNS started successfully"
            true
        } else {
            log error "CoreDNS failed to start, check logs"
            false
        }
    }
}

# Start CoreDNS in Docker
def start-coredns-docker [
    config: record
] -> bool {
    log info "Starting CoreDNS in Docker"

    let local_config = $config.local? | default {}
    let docker_config = $local_config.docker? | default {}

    let image = $docker_config.image? | default "coredns/coredns:1.11.1"
    let container_name = $docker_config.container_name? | default "provisioning-coredns"
    let config_path = $local_config.config_path? | default "~/.provisioning/coredns/Corefile" | path expand
    let zones_path = $local_config.zones_path? | default "~/.provisioning/coredns/zones" | path expand
    let port = $local_config.port? | default 5353

    # Check if container already running
    let running = (docker ps --filter $"name=($container_name)" --format "{{.Names}}" | complete | get stdout | str trim)

    if ($running | str contains $container_name) {
        log warn "CoreDNS container is already running"
        return false
    }

    # Generate Corefile
    let corefile_content = generate-corefile $config
    write-corefile $config_path $corefile_content

    # Create zone files
    let zones = $local_config.zones? | default ["provisioning.local"]

    for zone in $zones {
        let zone_file = $"($zones_path)/($zone).zone"

        if not ($zone_file | path exists) {
            create-zone-file $zone $zones_path --config $config
        }
    }

    # Start Docker container
    let result = (do {
        docker run -d \
            --name $container_name \
            -p $"($port):53/udp" \
            -p $"($port):53/tcp" \
            -v $"($config_path):/Corefile:ro" \
            -v $"($zones_path):/zones:ro" \
            --restart unless-stopped \
            $image -conf /Corefile
    } | complete)

    if $result.exit_code == 0 {
        log info $"CoreDNS Docker container started: [$container_name]"
        true
    } else {
        log error "Failed to start CoreDNS Docker container"
        false
    }
}

# Stop CoreDNS service
export def stop-coredns [
    --config: record = {}
    --check
] -> bool {
    log info "Stopping CoreDNS service"

    if $check {
        log info "Check mode: Would stop CoreDNS"
        return true
    }

    let local_config = $config.local? | default {}
    let deployment_type = $local_config.deployment_type? | default "binary"

    match $deployment_type {
        "binary" => { stop-coredns-binary }
        "docker" => { stop-coredns-docker $config }
        _ => {
            log error $"Unknown deployment type: ($deployment_type)"
            false
        }
    }
}

# Stop CoreDNS binary
def stop-coredns-binary [] -> bool {
    if not (is-coredns-running) {
        log warn "CoreDNS is not running"
        return false
    }

    let pid = get-coredns-pid

    if $pid == null {
        log error "Cannot determine CoreDNS PID"
        return false
    }

    let result = (do {
        kill $pid
    } | complete)

    if $result.exit_code == 0 {
        log info "CoreDNS stopped"
        true
    } else {
        log error "Failed to stop CoreDNS"
        false
    }
}

# Stop CoreDNS Docker container
def stop-coredns-docker [
    config: record
] -> bool {
    let local_config = $config.local? | default {}
    let docker_config = $local_config.docker? | default {}
    let container_name = $docker_config.container_name? | default "provisioning-coredns"

    let result = (do {
        docker stop $container_name
        docker rm $container_name
    } | complete)

    if $result.exit_code == 0 {
        log info $"CoreDNS Docker container stopped: [$container_name]"
        true
    } else {
        log error "Failed to stop CoreDNS Docker container"
        false
    }
}

# Reload CoreDNS configuration
export def reload-coredns [
    --config: record = {}
] -> bool {
    log info "Reloading CoreDNS configuration"

    let local_config = $config.local? | default {}
    let deployment_type = $local_config.deployment_type? | default "binary"

    match $deployment_type {
        "binary" => { reload-coredns-binary }
        "docker" => { reload-coredns-docker $config }
        _ => {
            log error $"Unknown deployment type: ($deployment_type)"
            false
        }
    }
}

# Reload CoreDNS binary (SIGUSR1)
def reload-coredns-binary [] -> bool {
    if not (is-coredns-running) {
        log error "CoreDNS is not running"
        return false
    }

    let pid = get-coredns-pid

    if $pid == null {
        log error "Cannot determine CoreDNS PID"
        return false
    }

    let result = (do {
        kill -s USR1 $pid
    } | complete)

    if $result.exit_code == 0 {
        log info "CoreDNS reload signal sent"
        true
    } else {
        log error "Failed to reload CoreDNS"
        false
    }
}

# Reload CoreDNS Docker container
def reload-coredns-docker [
    config: record
] -> bool {
    let local_config = $config.local? | default {}
    let docker_config = $local_config.docker? | default {}
    let container_name = $docker_config.container_name? | default "provisioning-coredns"

    let result = (do {
        # Send SIGUSR1 to CoreDNS process inside container
        docker exec $container_name kill -USR1 1
    } | complete)

    if $result.exit_code == 0 {
        log info "CoreDNS Docker container reloaded"
        true
    } else {
        log error "Failed to reload CoreDNS Docker container"
        false
    }
}

# Get CoreDNS status
export def get-coredns-status [
    --config: record = {}
] -> record {
    let local_config = $config.local? | default {}
    let deployment_type = $local_config.deployment_type? | default "binary"

    let running = is-coredns-running
    let pid = if $running { get-coredns-pid } else { null }

    let health = if $running {
        check-coredns-health $config
    } else {
        false
    }

    {
        running: $running
        deployment_type: $deployment_type
        pid: $pid
        healthy: $health
        mode: ($config.mode? | default "local")
    }
}

# Check if CoreDNS is running
def is-coredns-running [] -> bool {
    let pid_file = "~/.provisioning/coredns/coredns.pid" | path expand

    # Check via PID file
    if ($pid_file | path exists) {
        let pid = open $pid_file | into int

        let ps_result = ps | where pid == $pid

        if ($ps_result | is-not-empty) {
            return true
        }
    }

    # Check via process name
    let ps_result = ps | where name =~ "coredns"

    ($ps_result | is-not-empty)
}

# Get CoreDNS PID
def get-coredns-pid [] -> int {
    let pid_file = "~/.provisioning/coredns/coredns.pid" | path expand

    if ($pid_file | path exists) {
        open $pid_file | into int
    } else {
        let ps_result = ps | where name =~ "coredns"

        if ($ps_result | is-not-empty) {
            $ps_result | get 0.pid
        } else {
            null
        }
    }
}

# Check CoreDNS health
export def check-coredns-health [
    config: record
] -> bool {
    log debug "Checking CoreDNS health"

    let local_config = $config.local? | default {}
    let port = $local_config.port? | default 5353

    # Try to query DNS
    let result = (do {
        dig @127.0.0.1 -p $port provisioning.local
    } | complete)

    if $result.exit_code == 0 {
        log debug "CoreDNS health check passed"
        true
    } else {
        log warn "CoreDNS health check failed"
        false
    }
}

# Install CoreDNS binary
export def install-coredns [
    version?: string = "latest"  # Version to install
    --check
] -> bool {
    log info $"Installing CoreDNS ($version)"

    if $check {
        log info "Check mode: Would install CoreDNS"
        return true
    }

    let binary_path = "~/.provisioning/bin/coredns" | path expand
    let bin_dir = $binary_path | path dirname

    # Create bin directory
    mkdir $bin_dir

    # Determine latest version if needed
    let install_version = if $version == "latest" {
        get-latest-coredns-version
    } else {
        $version
    }

    log info $"Installing CoreDNS version ($install_version)"

    # Detect OS and architecture
    let os = if (sys host | get name) =~ "Darwin" { "darwin" } else { "linux" }
    let arch = uname -m | complete | get stdout | str trim

    let arch_name = if $arch == "x86_64" {
        "amd64"
    } else if $arch == "aarch64" or $arch == "arm64" {
        "arm64"
    } else {
        $arch
    }

    # Download URL
    let download_url = $"https://github.com/coredns/coredns/releases/download/v($install_version)/coredns_($install_version)_($os)_($arch_name).tgz"

    log info $"Downloading from ($download_url)"

    let temp_dir = mktemp -d

    let result = (do {
        cd $temp_dir
        http get $download_url | save coredns.tgz
        tar xzf coredns.tgz
        mv coredns $binary_path
        chmod +x $binary_path
    } | complete)

    if $result.exit_code == 0 {
        log info $"CoreDNS installed to [$binary_path]"

        # Cleanup
        rm -rf $temp_dir

        true
    } else {
        log error "Failed to install CoreDNS"
        rm -rf $temp_dir
        false
    }
}

# Get latest CoreDNS version from GitHub
def get-latest-coredns-version [] -> string {
    let result = (do {
        http get "https://api.github.com/repos/coredns/coredns/releases/latest" | from json
    } | complete)

    if $result.exit_code == 0 {
        let release_info = $result.stdout | from json
        $release_info.tag_name | str replace "v" ""
    } else {
        log warn "Failed to get latest version, using default"
        "1.11.1"
    }
}

# Restart CoreDNS service
export def restart-coredns [
    config: record
    --check
] -> bool {
    log info "Restarting CoreDNS service"

    if $check {
        log info "Check mode: Would restart CoreDNS"
        return true
    }

    stop-coredns --config $config
    sleep 1sec
    start-coredns $config
}

# Show CoreDNS logs
export def show-coredns-logs [
    --lines: int = 50
    --follow (-f)
] -> nothing {
    let log_file = "~/.provisioning/coredns/coredns.log" | path expand

    if not ($log_file | path exists) {
        log error "CoreDNS log file not found"
        return
    }

    if $follow {
        tail -f -n $lines $log_file
    } else {
        tail -n $lines $log_file
    }
}
