package network

import (
	"fmt"
	"os/exec"
	"strings"
)

// LinkDown sets admin state down without deconfiguring (ip link set <name> down).
func LinkDown(name string) error {
	out, err := exec.Command("ip", "link", "set", name, "down").CombinedOutput()
	if err != nil {
		return fmt.Errorf("ip link set down %s: %s", name, strings.TrimSpace(string(out)))
	}
	return nil
}

// LinkUp sets admin state up without re-running ifup (ip link set <name> up).
func LinkUp(name string) error {
	out, err := exec.Command("ip", "link", "set", name, "up").CombinedOutput()
	if err != nil {
		return fmt.Errorf("ip link set up %s: %s", name, strings.TrimSpace(string(out)))
	}
	return nil
}

// IfDown brings an interface down via ifdown --force.
func IfDown(name string) error {
	out, err := exec.Command("ifdown", "--force", name).CombinedOutput()
	if err != nil {
		return fmt.Errorf("ifdown %s: %s", name, strings.TrimSpace(string(out)))
	}
	return nil
}

// IfUp brings an interface up via ifup --force.
func IfUp(name string) error {
	out, err := exec.Command("ifup", "--force", name).CombinedOutput()
	if err != nil {
		return fmt.Errorf("ifup %s: %s", name, strings.TrimSpace(string(out)))
	}
	return nil
}

// IfRestart brings an interface down then up.
func IfRestart(name string) error {
	_ = IfDown(name)
	return IfUp(name)
}

// ApplyPending merges pending configs into /etc/network/interfaces,
// writes the file, and restarts affected interfaces.
// Returns a per-interface error map (nil key = write error).
func ApplyPending() map[string]error {
	errs := map[string]error{}

	pending := GetAllPending()
	if len(pending) == 0 {
		return errs
	}

	// Read current file config
	configs, err := ParseConfig()
	if err != nil {
		errs["__parse__"] = err
		return errs
	}

	// Merge
	for name, cfg := range pending {
		configs[name] = cfg
	}

	// Write file
	if err := WriteConfig(configs); err != nil {
		errs["__write__"] = err
		return errs
	}

	// Restart each changed interface
	for name := range pending {
		if name == "lo" {
			ClearPendingConfig(name)
			continue
		}
		// For VLAN interfaces ensure the kernel interface exists before ifup.
		if IsVLAN(name) {
			if err := EnsureVLANExists(name); err != nil {
				errs[name] = err
				continue
			}
		}
		_ = IfDown(name)
		if cfg := configs[name]; cfg != nil && cfg.Auto {
			if err := IfUp(name); err != nil {
				errs[name] = err
				continue
			}
		}
		ClearPendingConfig(name)
	}

	return errs
}
