package networking

import (
	"bytes"
	"context"
	"fmt"
	"net"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/pocketbase/pocketbase/core"
	"github.com/seriousm4x/upsnap/logger"
)

func ShutdownDevice(device *core.Record) error {
	logger.Info.Println("Shutdown triggered for", device.GetString("name"))
	shutdown_cmd := device.GetString("shutdown_cmd")
	if shutdown_cmd == "" {
		return fmt.Errorf("%s: no shutdown_cmd definded", device.GetString("name"))
	}

	var shell string
	var shell_arg string
	if runtime.GOOS == "windows" {
		shell = "cmd"
		shell_arg = "/C"
	} else {
		shell = "/bin/sh"
		shell_arg = "-c"
	}

	// Validate IP and MAC addresses before replacing placeholders to prevent command injection
	deviceIP := device.GetString("ip")
	if net.ParseIP(deviceIP) == nil {
		return fmt.Errorf("invalid device IP address: %q", deviceIP)
	}
	deviceMAC := device.GetString("mac")
	if _, err := net.ParseMAC(deviceMAC); err != nil {
		return fmt.Errorf("invalid device MAC address: %q", deviceMAC)
	}

	shutdown_cmd = strings.ReplaceAll(shutdown_cmd, "{{ DEVICE_IP }}", device.GetString("ip"))
	shutdown_cmd = strings.ReplaceAll(shutdown_cmd, "{{ DEVICE_MAC }}", device.GetString("mac"))

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	cmd := exec.CommandContext(ctx, shell, shell_arg, shutdown_cmd)
	SetProcessAttributes(cmd)

	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return err
	}

	done := make(chan error, 1)
	go func() {
		done <- cmd.Wait()
	}()

	shutdownTimeout := device.GetInt("shutdown_timeout")
	if shutdownTimeout <= 0 {
		shutdownTimeout = 120
	}

	start := time.Now()

	for {
		select {
		case err := <-done:
			if err != nil {
				return fmt.Errorf("%s", stderr.String())
			} else {
				for {
					isOnline, err := PingDevice(device)
					if err != nil {
						logger.Error.Println(err)
						return err
					}
					if isOnline {
						if time.Since(start) >= time.Duration(shutdownTimeout)*time.Second {
							return fmt.Errorf("%s not offline after %d seconds", device.GetString("name"), shutdownTimeout)
						}
					}
					if !isOnline {
						return nil
					}
					time.Sleep(1 * time.Second)
				}
			}
		default:
			isOnline, err := PingDevice(device)
			if err != nil {
				logger.Error.Println(err)
				return err
			}
			if time.Since(start) >= time.Duration(shutdownTimeout)*time.Second {
				if err := KillProcess(cmd.Process); err != nil {
					logger.Error.Println(err)
				}
				return fmt.Errorf("%s not offline after %d seconds", device.GetString("name"), shutdownTimeout)
			}
			if !isOnline {
				if err := KillProcess(cmd.Process); err != nil {
					logger.Error.Println(err)
				}
				return nil
			}
			time.Sleep(1 * time.Second)
		}
	}
}
