package system

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/atlasos/calypso/internal/common/logger"
)

// NTPSettings represents NTP configuration
type NTPSettings struct {
	Timezone   string   `json:"timezone"`
	NTPServers []string `json:"ntp_servers"`
}

// Service handles system management operations
type Service struct {
	logger     *logger.Logger
	rrdService *RRDService
}

// detectPrimaryInterface detects the primary network interface (first non-loopback with IP)
func detectPrimaryInterface(ctx context.Context) string {
	// Try to get default route interface
	cmd := exec.CommandContext(ctx, "ip", "route", "show", "default")
	output, err := cmd.Output()
	if err == nil {
		lines := strings.Split(string(output), "\n")
		for _, line := range lines {
			if strings.Contains(line, "dev ") {
				parts := strings.Fields(line)
				for i, part := range parts {
					if part == "dev" && i+1 < len(parts) {
						iface := parts[i+1]
						if iface != "lo" {
							return iface
						}
					}
				}
			}
		}
	}

	// Fallback: get first non-loopback interface with IP
	cmd = exec.CommandContext(ctx, "ip", "-4", "addr", "show")
	output, err = cmd.Output()
	if err == nil {
		lines := strings.Split(string(output), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			// Look for interface name line (e.g., "2: ens18: <BROADCAST...")
			if len(line) > 0 && line[0] >= '0' && line[0] <= '9' && strings.Contains(line, ":") {
				parts := strings.Fields(line)
				if len(parts) >= 2 {
					iface := strings.TrimSuffix(parts[1], ":")
					if iface != "" && iface != "lo" {
						// Check if this interface has an IP (next lines will have "inet")
						// For simplicity, return first non-loopback interface
						return iface
					}
				}
			}
		}
	}

	// Final fallback
	return "eth0"
}

// NewService creates a new system service
func NewService(log *logger.Logger) *Service {
	// Initialize RRD service for network monitoring
	rrdDir := "/var/lib/calypso/rrd"

	// Auto-detect primary interface
	ctx := context.Background()
	interfaceName := detectPrimaryInterface(ctx)
	log.Info("Detected primary network interface", "interface", interfaceName)

	rrdService := NewRRDService(log, rrdDir, interfaceName)

	return &Service{
		logger:     log,
		rrdService: rrdService,
	}
}

// StartNetworkMonitoring starts the RRD collector for network monitoring
func (s *Service) StartNetworkMonitoring(ctx context.Context) error {
	return s.rrdService.StartCollector(ctx, 10*time.Second)
}

// GetNetworkThroughput fetches network throughput data from RRD
func (s *Service) GetNetworkThroughput(ctx context.Context, duration time.Duration) ([]NetworkDataPoint, error) {
	endTime := time.Now()
	startTime := endTime.Add(-duration)

	// Use 10 second resolution for recent data
	return s.rrdService.FetchRRDData(ctx, startTime, endTime, "10")
}

// ServiceStatus represents a systemd service status
type ServiceStatus struct {
	Name        string    `json:"name"`
	ActiveState string    `json:"active_state"`
	SubState    string    `json:"sub_state"`
	LoadState   string    `json:"load_state"`
	Description string    `json:"description"`
	Since       time.Time `json:"since,omitempty"`
}

// GetServiceStatus retrieves the status of a systemd service
func (s *Service) GetServiceStatus(ctx context.Context, serviceName string) (*ServiceStatus, error) {
	status := &ServiceStatus{
		Name: serviceName,
	}

	// Get each property individually to ensure correct parsing
	properties := map[string]*string{
		"ActiveState": &status.ActiveState,
		"SubState":    &status.SubState,
		"LoadState":   &status.LoadState,
		"Description": &status.Description,
	}

	for prop, target := range properties {
		cmd := exec.CommandContext(ctx, "systemctl", "show", serviceName, "--property", prop, "--value", "--no-pager")
		output, err := cmd.Output()
		if err != nil {
			s.logger.Warn("Failed to get property", "service", serviceName, "property", prop, "error", err)
			continue
		}
		*target = strings.TrimSpace(string(output))
	}

	// Get timestamp if available
	cmd := exec.CommandContext(ctx, "systemctl", "show", serviceName, "--property", "ActiveEnterTimestamp", "--value", "--no-pager")
	output, err := cmd.Output()
	if err == nil {
		timestamp := strings.TrimSpace(string(output))
		if timestamp != "" {
			if t, err := time.Parse("Mon 2006-01-02 15:04:05 MST", timestamp); err == nil {
				status.Since = t
			}
		}
	}

	return status, nil
}

// ListServices lists all Calypso-related services
func (s *Service) ListServices(ctx context.Context) ([]ServiceStatus, error) {
	services := []string{
		"ssh",
		"sshd",
		"smbd",
		"iscsi-scst",
		"nfs-server",
		"nfs",
		"mhvtl",
		"calypso-api",
		"scst",
		"postgresql",
	}

	var statuses []ServiceStatus
	for _, serviceName := range services {
		status, err := s.GetServiceStatus(ctx, serviceName)
		if err != nil {
			s.logger.Warn("Failed to get service status", "service", serviceName, "error", err)
			continue
		}
		statuses = append(statuses, *status)
	}

	return statuses, nil
}

// RestartService restarts a systemd service
func (s *Service) RestartService(ctx context.Context, serviceName string) error {
	cmd := exec.CommandContext(ctx, "systemctl", "restart", serviceName)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to restart service: %s: %w", string(output), err)
	}

	s.logger.Info("Service restarted", "service", serviceName)
	return nil
}

// GetJournalLogs retrieves journald logs for a service
func (s *Service) GetJournalLogs(ctx context.Context, serviceName string, lines int) ([]map[string]interface{}, error) {
	cmd := exec.CommandContext(ctx, "journalctl",
		"-u", serviceName,
		"-n", fmt.Sprintf("%d", lines),
		"-o", "json",
		"--no-pager")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to get logs: %w", err)
	}

	var logs []map[string]interface{}
	linesOutput := strings.Split(strings.TrimSpace(string(output)), "\n")
	for _, line := range linesOutput {
		if line == "" {
			continue
		}
		var logEntry map[string]interface{}
		if err := json.Unmarshal([]byte(line), &logEntry); err == nil {
			logs = append(logs, logEntry)
		}
	}

	return logs, nil
}

// SystemLogEntry represents a parsed system log entry
type SystemLogEntry struct {
	Time    string `json:"time"`
	Level   string `json:"level"`
	Source  string `json:"source"`
	Message string `json:"message"`
}

// GetSystemLogs retrieves recent system logs from journalctl
func (s *Service) GetSystemLogs(ctx context.Context, limit int) ([]SystemLogEntry, error) {
	if limit <= 0 || limit > 100 {
		limit = 30 // Default to 30 logs
	}

	cmd := exec.CommandContext(ctx, "journalctl",
		"-n", fmt.Sprintf("%d", limit),
		"-o", "json",
		"--no-pager",
		"--since", "1 hour ago") // Only get logs from last hour
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to get system logs: %w", err)
	}

	var logs []SystemLogEntry
	linesOutput := strings.Split(strings.TrimSpace(string(output)), "\n")
	for _, line := range linesOutput {
		if line == "" {
			continue
		}
		var logEntry map[string]interface{}
		if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
			continue
		}

		// Parse timestamp (__REALTIME_TIMESTAMP is in microseconds)
		var timeStr string
		if timestamp, ok := logEntry["__REALTIME_TIMESTAMP"].(float64); ok {
			// Convert microseconds to nanoseconds for time.Unix (1 microsecond = 1000 nanoseconds)
			t := time.Unix(0, int64(timestamp)*1000)
			timeStr = t.Format("15:04:05")
		} else if timestamp, ok := logEntry["_SOURCE_REALTIME_TIMESTAMP"].(float64); ok {
			t := time.Unix(0, int64(timestamp)*1000)
			timeStr = t.Format("15:04:05")
		} else {
			timeStr = time.Now().Format("15:04:05")
		}

		// Parse log level (priority)
		level := "INFO"
		if priority, ok := logEntry["PRIORITY"].(float64); ok {
			switch int(priority) {
			case 0: // emerg
				level = "EMERG"
			case 1, 2, 3: // alert, crit, err
				level = "ERROR"
			case 4: // warning
				level = "WARN"
			case 5: // notice
				level = "NOTICE"
			case 6: // info
				level = "INFO"
			case 7: // debug
				level = "DEBUG"
			}
		}

		// Parse source (systemd unit or syslog identifier)
		source := "system"
		if unit, ok := logEntry["_SYSTEMD_UNIT"].(string); ok && unit != "" {
			// Remove .service suffix if present
			source = strings.TrimSuffix(unit, ".service")
		} else if ident, ok := logEntry["SYSLOG_IDENTIFIER"].(string); ok && ident != "" {
			source = ident
		} else if comm, ok := logEntry["_COMM"].(string); ok && comm != "" {
			source = comm
		}

		// Parse message
		message := ""
		if msg, ok := logEntry["MESSAGE"].(string); ok {
			message = msg
		}

		if message != "" {
			logs = append(logs, SystemLogEntry{
				Time:    timeStr,
				Level:   level,
				Source:  source,
				Message: message,
			})
		}
	}

	// Reverse to get newest first
	for i, j := 0, len(logs)-1; i < j; i, j = i+1, j-1 {
		logs[i], logs[j] = logs[j], logs[i]
	}

	return logs, nil
}

// GenerateSupportBundle generates a diagnostic support bundle
func (s *Service) GenerateSupportBundle(ctx context.Context, outputPath string) error {
	// Create bundle directory
	cmd := exec.CommandContext(ctx, "mkdir", "-p", outputPath)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to create bundle directory: %w", err)
	}

	// Collect system information
	commands := map[string][]string{
		"system_info": {"uname", "-a"},
		"disk_usage":  {"df", "-h"},
		"memory":      {"free", "-h"},
		"scst_status": {"scstadmin", "-list_target"},
		"services":    {"systemctl", "list-units", "--type=service", "--state=running"},
	}

	for name, cmdArgs := range commands {
		cmd := exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...)
		output, err := cmd.CombinedOutput()
		if err != nil {
			s.logger.Warn("Failed to collect info", "command", name, "error", err)
			continue
		}

		// Write to file
		filePath := fmt.Sprintf("%s/%s.txt", outputPath, name)
		if err := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("cat > %s", filePath)).Run(); err == nil {
			exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo '%s' > %s", string(output), filePath)).Run()
		}
	}

	// Collect journal logs
	services := []string{"calypso-api", "scst", "iscsi-scst"}
	for _, service := range services {
		cmd := exec.CommandContext(ctx, "journalctl", "-u", service, "-n", "1000", "--no-pager")
		output, err := cmd.CombinedOutput()
		if err == nil {
			filePath := fmt.Sprintf("%s/journal_%s.log", outputPath, service)
			exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("echo '%s' > %s", string(output), filePath)).Run()
		}
	}

	s.logger.Info("Support bundle generated", "path", outputPath)
	return nil
}

// NetworkInterface represents a network interface
type NetworkInterface struct {
	Name      string `json:"name"`
	IPAddress string `json:"ip_address"`
	Subnet    string `json:"subnet"`
	Status    string `json:"status"` // "Connected" or "Down"
	Speed     string `json:"speed"`  // e.g., "10 Gbps", "1 Gbps"
	Role      string `json:"role"`   // "Management", "ISCSI", or empty
	Gateway   string `json:"gateway,omitempty"`
	DNS1      string `json:"dns1,omitempty"`
	DNS2      string `json:"dns2,omitempty"`
}

// ListNetworkInterfaces lists all network interfaces
func (s *Service) ListNetworkInterfaces(ctx context.Context) ([]NetworkInterface, error) {
	// First, get all interface names and their states
	cmd := exec.CommandContext(ctx, "ip", "link", "show")
	output, err := cmd.Output()
	if err != nil {
		s.logger.Error("Failed to list interfaces", "error", err)
		return nil, fmt.Errorf("failed to list interfaces: %w", err)
	}

	interfaceMap := make(map[string]*NetworkInterface)
	lines := strings.Split(string(output), "\n")

	s.logger.Debug("Parsing network interfaces", "output_lines", len(lines))

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		// Parse interface name and state
		// Format: "2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000"
		// Look for lines that start with a number followed by ":" (interface definition line)
		// Simple check: line starts with digit, contains ":", and contains "state"
		if len(line) > 0 && line[0] >= '0' && line[0] <= '9' && strings.Contains(line, ":") && strings.Contains(line, "state") {
			parts := strings.Fields(line)
			if len(parts) < 2 {
				continue
			}

			// Extract interface name (e.g., "ens18:" or "lo:")
			ifaceName := strings.TrimSuffix(parts[1], ":")
			if ifaceName == "" || ifaceName == "lo" {
				continue // Skip loopback
			}

			// Extract state - look for "state UP" or "state DOWN" in the line
			state := "Down"
			if strings.Contains(line, "state UP") {
				state = "Connected"
			} else if strings.Contains(line, "state DOWN") {
				state = "Down"
			}

			s.logger.Info("Found interface", "name", ifaceName, "state", state)

			interfaceMap[ifaceName] = &NetworkInterface{
				Name:   ifaceName,
				Status: state,
				Speed:  "Unknown",
			}
		}
	}

	s.logger.Debug("Found interfaces from ip link", "count", len(interfaceMap))

	// Get IP addresses for each interface
	cmd = exec.CommandContext(ctx, "ip", "-4", "addr", "show")
	output, err = cmd.Output()
	if err != nil {
		s.logger.Warn("Failed to get IP addresses", "error", err)
	} else {
		lines = strings.Split(string(output), "\n")
		var currentIfaceName string
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			// Parse interface name (e.g., "2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP>")
			if strings.Contains(line, ":") && !strings.Contains(line, "inet") && !strings.HasPrefix(line, "valid_lft") && !strings.HasPrefix(line, "altname") {
				parts := strings.Fields(line)
				if len(parts) >= 2 {
					currentIfaceName = strings.TrimSuffix(parts[1], ":")
					s.logger.Debug("Processing interface for IP", "name", currentIfaceName)
				}
				continue
			}

			// Parse IP address (e.g., "inet 10.10.14.16/24 brd 10.10.14.255 scope global ens18")
			if strings.HasPrefix(line, "inet ") && currentIfaceName != "" && currentIfaceName != "lo" {
				parts := strings.Fields(line)
				if len(parts) >= 2 {
					ipWithSubnet := parts[1] // e.g., "10.10.14.16/24"
					ipParts := strings.Split(ipWithSubnet, "/")
					if len(ipParts) == 2 {
						ip := ipParts[0]
						subnet := ipParts[1]

						// Find or create interface
						iface, exists := interfaceMap[currentIfaceName]
						if !exists {
							s.logger.Debug("Creating new interface entry", "name", currentIfaceName)
							iface = &NetworkInterface{
								Name:   currentIfaceName,
								Status: "Down",
								Speed:  "Unknown",
							}
							interfaceMap[currentIfaceName] = iface
						}

						iface.IPAddress = ip
						iface.Subnet = subnet
						s.logger.Debug("Set IP for interface", "name", currentIfaceName, "ip", ip, "subnet", subnet)
					}
				}
			}
		}
	}

	// Get default gateway for each interface
	cmd = exec.CommandContext(ctx, "ip", "route", "show")
	output, err = cmd.Output()
	if err == nil {
		lines = strings.Split(string(output), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			// Parse default route: "default via 10.10.14.1 dev ens18"
			if strings.HasPrefix(line, "default via ") {
				parts := strings.Fields(line)
				// Find "via" and "dev" in the parts
				var gateway string
				var ifaceName string
				for i, part := range parts {
					if part == "via" && i+1 < len(parts) {
						gateway = parts[i+1]
					}
					if part == "dev" && i+1 < len(parts) {
						ifaceName = parts[i+1]
					}
				}
				if gateway != "" && ifaceName != "" {
					if iface, exists := interfaceMap[ifaceName]; exists {
						iface.Gateway = gateway
						s.logger.Info("Set default gateway for interface", "name", ifaceName, "gateway", gateway)
					}
				}
			} else if strings.Contains(line, " via ") && strings.Contains(line, " dev ") {
				// Parse network route: "10.10.14.0/24 via 10.10.14.1 dev ens18"
				// Or: "192.168.1.0/24 via 192.168.1.1 dev eth0"
				parts := strings.Fields(line)
				var gateway string
				var ifaceName string
				for i, part := range parts {
					if part == "via" && i+1 < len(parts) {
						gateway = parts[i+1]
					}
					if part == "dev" && i+1 < len(parts) {
						ifaceName = parts[i+1]
					}
				}
				// Only set gateway if it's not already set (prefer default route)
				if gateway != "" && ifaceName != "" {
					if iface, exists := interfaceMap[ifaceName]; exists {
						if iface.Gateway == "" {
							iface.Gateway = gateway
							s.logger.Info("Set gateway from network route for interface", "name", ifaceName, "gateway", gateway)
						}
					}
				}
			}
		}
	} else {
		s.logger.Warn("Failed to get routes", "error", err)
	}

	// Get DNS servers from systemd-resolved or /etc/resolv.conf
	// Try systemd-resolved first
	cmd = exec.CommandContext(ctx, "systemd-resolve", "--status")
	output, err = cmd.Output()
	dnsServers := []string{}
	if err == nil {
		// Parse DNS from systemd-resolve output
		lines = strings.Split(string(output), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "DNS Servers:") {
				// Format: "DNS Servers: 8.8.8.8 8.8.4.4"
				parts := strings.Fields(line)
				if len(parts) >= 3 {
					dnsServers = parts[2:]
				}
				break
			}
		}
	} else {
		// Fallback to /etc/resolv.conf
		data, err := os.ReadFile("/etc/resolv.conf")
		if err == nil {
			lines = strings.Split(string(data), "\n")
			for _, line := range lines {
				line = strings.TrimSpace(line)
				if strings.HasPrefix(line, "nameserver ") {
					dns := strings.TrimPrefix(line, "nameserver ")
					dns = strings.TrimSpace(dns)
					if dns != "" {
						dnsServers = append(dnsServers, dns)
					}
				}
			}
		}
	}

	// Convert map to slice
	var interfaces []NetworkInterface
	s.logger.Debug("Converting interface map to slice", "map_size", len(interfaceMap))
	for _, iface := range interfaceMap {
		// Get speed for each interface using ethtool
		if iface.Name != "" && iface.Name != "lo" {
			cmd := exec.CommandContext(ctx, "ethtool", iface.Name)
			output, err := cmd.Output()
			if err == nil {
				// Parse speed from ethtool output
				ethtoolLines := strings.Split(string(output), "\n")
				for _, ethtoolLine := range ethtoolLines {
					if strings.Contains(ethtoolLine, "Speed:") {
						parts := strings.Fields(ethtoolLine)
						if len(parts) >= 2 {
							iface.Speed = parts[1]
						}
						break
					}
				}
			}

			// Set DNS servers (use first two if available)
			if len(dnsServers) > 0 {
				iface.DNS1 = dnsServers[0]
			}
			if len(dnsServers) > 1 {
				iface.DNS2 = dnsServers[1]
			}

			// Determine role based on interface name or IP (simple heuristic)
			// You can enhance this with configuration file or database lookup
			if strings.Contains(iface.Name, "eth") || strings.Contains(iface.Name, "ens") {
				// Default to Management for first interface, ISCSI for others
				if iface.Name == "eth0" || iface.Name == "ens18" {
					iface.Role = "Management"
				} else {
					// Check if IP is in typical iSCSI range (10.x.x.x)
					if strings.HasPrefix(iface.IPAddress, "10.") && iface.IPAddress != "" {
						iface.Role = "ISCSI"
					}
				}
			}
		}
		interfaces = append(interfaces, *iface)
	}

	// If no interfaces found, return empty slice
	if len(interfaces) == 0 {
		s.logger.Warn("No network interfaces found")
		return []NetworkInterface{}, nil
	}

	s.logger.Info("Listed network interfaces", "count", len(interfaces))
	return interfaces, nil
}

// GetManagementIPAddress returns the IP address of the management interface
func (s *Service) GetManagementIPAddress(ctx context.Context) (string, error) {
	interfaces, err := s.ListNetworkInterfaces(ctx)
	if err != nil {
		return "", fmt.Errorf("failed to list network interfaces: %w", err)
	}

	// First, try to find interface with Role "Management"
	for _, iface := range interfaces {
		if iface.Role == "Management" && iface.IPAddress != "" && iface.Status == "Connected" {
			s.logger.Info("Found management interface", "interface", iface.Name, "ip", iface.IPAddress)
			return iface.IPAddress, nil
		}
	}

	// Fallback: use interface with default route (primary interface)
	for _, iface := range interfaces {
		if iface.Gateway != "" && iface.IPAddress != "" && iface.Status == "Connected" {
			s.logger.Info("Using primary interface as management", "interface", iface.Name, "ip", iface.IPAddress)
			return iface.IPAddress, nil
		}
	}

	// Final fallback: use first connected interface with IP
	for _, iface := range interfaces {
		if iface.IPAddress != "" && iface.Status == "Connected" && iface.Name != "lo" {
			s.logger.Info("Using first connected interface as management", "interface", iface.Name, "ip", iface.IPAddress)
			return iface.IPAddress, nil
		}
	}

	return "", fmt.Errorf("no management interface found")
}

// UpdateNetworkInterfaceRequest represents the request to update a network interface
type UpdateNetworkInterfaceRequest struct {
	IPAddress string `json:"ip_address"`
	Subnet    string `json:"subnet"`
	Gateway   string `json:"gateway,omitempty"`
	DNS1      string `json:"dns1,omitempty"`
	DNS2      string `json:"dns2,omitempty"`
	Role      string `json:"role,omitempty"`
}

// UpdateNetworkInterface updates network interface configuration
func (s *Service) UpdateNetworkInterface(ctx context.Context, ifaceName string, req UpdateNetworkInterfaceRequest) (*NetworkInterface, error) {
	// Validate interface exists
	cmd := exec.CommandContext(ctx, "ip", "link", "show", ifaceName)
	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("interface %s not found: %w", ifaceName, err)
	}

	// Remove existing IP address if any
	cmd = exec.CommandContext(ctx, "ip", "addr", "flush", "dev", ifaceName)
	cmd.Run() // Ignore error, interface might not have IP

	// Set new IP address and subnet
	ipWithSubnet := fmt.Sprintf("%s/%s", req.IPAddress, req.Subnet)
	cmd = exec.CommandContext(ctx, "ip", "addr", "add", ipWithSubnet, "dev", ifaceName)
	output, err := cmd.CombinedOutput()
	if err != nil {
		s.logger.Error("Failed to set IP address", "interface", ifaceName, "error", err, "output", string(output))
		return nil, fmt.Errorf("failed to set IP address: %w", err)
	}

	// Remove existing default route if any
	cmd = exec.CommandContext(ctx, "ip", "route", "del", "default")
	cmd.Run() // Ignore error, might not exist

	// Set gateway if provided
	if req.Gateway != "" {
		cmd = exec.CommandContext(ctx, "ip", "route", "add", "default", "via", req.Gateway, "dev", ifaceName)
		output, err = cmd.CombinedOutput()
		if err != nil {
			s.logger.Error("Failed to set gateway", "interface", ifaceName, "error", err, "output", string(output))
			return nil, fmt.Errorf("failed to set gateway: %w", err)
		}
	}

	// Update DNS in systemd-resolved or /etc/resolv.conf
	if req.DNS1 != "" || req.DNS2 != "" {
		// Try using systemd-resolve first
		cmd = exec.CommandContext(ctx, "systemd-resolve", "--status")
		if cmd.Run() == nil {
			// systemd-resolve is available, use it
			dnsServers := []string{}
			if req.DNS1 != "" {
				dnsServers = append(dnsServers, req.DNS1)
			}
			if req.DNS2 != "" {
				dnsServers = append(dnsServers, req.DNS2)
			}
			if len(dnsServers) > 0 {
				// Use resolvectl to set DNS (newer systemd)
				cmd = exec.CommandContext(ctx, "resolvectl", "dns", ifaceName, strings.Join(dnsServers, " "))
				if cmd.Run() != nil {
					// Fallback to systemd-resolve
					cmd = exec.CommandContext(ctx, "systemd-resolve", "--interface", ifaceName, "--set-dns", strings.Join(dnsServers, " "))
					output, err = cmd.CombinedOutput()
					if err != nil {
						s.logger.Warn("Failed to set DNS via systemd-resolve", "error", err, "output", string(output))
					}
				}
			}
		} else {
			// Fallback: update /etc/resolv.conf
			resolvContent := "# Generated by Calypso\n"
			if req.DNS1 != "" {
				resolvContent += fmt.Sprintf("nameserver %s\n", req.DNS1)
			}
			if req.DNS2 != "" {
				resolvContent += fmt.Sprintf("nameserver %s\n", req.DNS2)
			}

			tmpPath := "/tmp/resolv.conf." + fmt.Sprintf("%d", time.Now().Unix())
			if err := os.WriteFile(tmpPath, []byte(resolvContent), 0644); err != nil {
				s.logger.Warn("Failed to write temporary resolv.conf", "error", err)
			} else {
				cmd = exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("mv %s /etc/resolv.conf", tmpPath))
				output, err = cmd.CombinedOutput()
				if err != nil {
					s.logger.Warn("Failed to update /etc/resolv.conf", "error", err, "output", string(output))
				}
			}
		}
	}

	// Bring interface up
	cmd = exec.CommandContext(ctx, "ip", "link", "set", ifaceName, "up")
	output, err = cmd.CombinedOutput()
	if err != nil {
		s.logger.Warn("Failed to bring interface up", "interface", ifaceName, "error", err, "output", string(output))
	}

	// Return updated interface
	updatedIface := &NetworkInterface{
		Name:      ifaceName,
		IPAddress: req.IPAddress,
		Subnet:    req.Subnet,
		Gateway:   req.Gateway,
		DNS1:      req.DNS1,
		DNS2:      req.DNS2,
		Role:      req.Role,
		Status:    "Connected",
		Speed:     "Unknown", // Will be updated on next list
	}

	s.logger.Info("Updated network interface", "interface", ifaceName, "ip", req.IPAddress, "subnet", req.Subnet)
	return updatedIface, nil
}

// SaveNTPSettings saves NTP configuration to the OS
func (s *Service) SaveNTPSettings(ctx context.Context, settings NTPSettings) error {
	// Set timezone using timedatectl
	if settings.Timezone != "" {
		cmd := exec.CommandContext(ctx, "timedatectl", "set-timezone", settings.Timezone)
		output, err := cmd.CombinedOutput()
		if err != nil {
			s.logger.Error("Failed to set timezone", "timezone", settings.Timezone, "error", err, "output", string(output))
			return fmt.Errorf("failed to set timezone: %w", err)
		}
		s.logger.Info("Timezone set", "timezone", settings.Timezone)
	}

	// Configure NTP servers in systemd-timesyncd
	if len(settings.NTPServers) > 0 {
		configPath := "/etc/systemd/timesyncd.conf"

		// Build config content
		configContent := "[Time]\n"
		configContent += "NTP="
		for i, server := range settings.NTPServers {
			if i > 0 {
				configContent += " "
			}
			configContent += server
		}
		configContent += "\n"

		// Write to temporary file first, then move to final location (requires root)
		tmpPath := "/tmp/timesyncd.conf." + fmt.Sprintf("%d", time.Now().Unix())
		if err := os.WriteFile(tmpPath, []byte(configContent), 0644); err != nil {
			s.logger.Error("Failed to write temporary NTP config", "error", err)
			return fmt.Errorf("failed to write temporary NTP configuration: %w", err)
		}

		// Move to final location using sudo (requires root privileges)
		cmd := exec.CommandContext(ctx, "sh", "-c", fmt.Sprintf("mv %s %s", tmpPath, configPath))
		output, err := cmd.CombinedOutput()
		if err != nil {
			s.logger.Error("Failed to move NTP config", "error", err, "output", string(output))
			os.Remove(tmpPath) // Clean up temp file
			return fmt.Errorf("failed to move NTP configuration: %w", err)
		}

		// Restart systemd-timesyncd to apply changes
		cmd = exec.CommandContext(ctx, "systemctl", "restart", "systemd-timesyncd")
		output, err = cmd.CombinedOutput()
		if err != nil {
			s.logger.Error("Failed to restart systemd-timesyncd", "error", err, "output", string(output))
			return fmt.Errorf("failed to restart systemd-timesyncd: %w", err)
		}

		s.logger.Info("NTP servers configured", "servers", settings.NTPServers)
	}

	return nil
}

// GetNTPSettings retrieves current NTP configuration from the OS
func (s *Service) GetNTPSettings(ctx context.Context) (*NTPSettings, error) {
	settings := &NTPSettings{
		NTPServers: []string{},
	}

	// Get current timezone using timedatectl
	cmd := exec.CommandContext(ctx, "timedatectl", "show", "--property=Timezone", "--value")
	output, err := cmd.Output()
	if err != nil {
		s.logger.Warn("Failed to get timezone", "error", err)
		settings.Timezone = "Etc/UTC" // Default fallback
	} else {
		settings.Timezone = strings.TrimSpace(string(output))
		if settings.Timezone == "" {
			settings.Timezone = "Etc/UTC"
		}
	}

	// Read NTP servers from systemd-timesyncd config
	configPath := "/etc/systemd/timesyncd.conf"
	data, err := os.ReadFile(configPath)
	if err != nil {
		s.logger.Warn("Failed to read NTP config", "error", err)
		// Default NTP servers if config file doesn't exist
		settings.NTPServers = []string{"pool.ntp.org", "time.google.com"}
	} else {
		// Parse NTP servers from config file
		lines := strings.Split(string(data), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "NTP=") {
				ntpLine := strings.TrimPrefix(line, "NTP=")
				if ntpLine != "" {
					servers := strings.Fields(ntpLine)
					settings.NTPServers = servers
					break
				}
			}
		}
		// If no NTP servers found in config, use defaults
		if len(settings.NTPServers) == 0 {
			settings.NTPServers = []string{"pool.ntp.org", "time.google.com"}
		}
	}

	return settings, nil
}

// ExecuteCommand executes a shell command and returns the output
// service parameter is optional and can be: system, scst, storage, backup, tape
func (s *Service) ExecuteCommand(ctx context.Context, command string, service string) (string, error) {
	// Sanitize command - basic security check
	command = strings.TrimSpace(command)
	if command == "" {
		return "", fmt.Errorf("command cannot be empty")
	}

	// Block dangerous commands that could harm the system
	dangerousCommands := []string{
		"rm -rf /",
		"dd if=",
		":(){ :|:& };:",
		"mkfs",
		"fdisk",
		"parted",
		"format",
		"> /dev/sd",
		"mkfs.ext",
		"mkfs.xfs",
		"mkfs.btrfs",
		"wipefs",
	}

	commandLower := strings.ToLower(command)
	for _, dangerous := range dangerousCommands {
		if strings.Contains(commandLower, dangerous) {
			return "", fmt.Errorf("command blocked for security reasons")
		}
	}

	// Service-specific command handling
	switch service {
	case "scst":
		// Allow SCST admin commands
		if strings.HasPrefix(command, "scstadmin") {
			// SCST commands are safe
			break
		}
	case "backup":
		// Allow bconsole commands
		if strings.HasPrefix(command, "bconsole") {
			// Backup console commands are safe
			break
		}
	case "storage":
		// Allow ZFS and storage commands
		if strings.HasPrefix(command, "zfs") || strings.HasPrefix(command, "zpool") || strings.HasPrefix(command, "lsblk") {
			// Storage commands are safe
			break
		}
	case "tape":
		// Allow tape library commands
		if strings.HasPrefix(command, "mtx") || strings.HasPrefix(command, "lsscsi") || strings.HasPrefix(command, "sg_") {
			// Tape commands are safe
			break
		}
	}

	// Execute command with timeout (30 seconds)
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	// Check if command already has sudo (reuse commandLower from above)
	hasSudo := strings.HasPrefix(commandLower, "sudo ")
	
	// Determine if command needs sudo based on service and command type
	needsSudo := false
	
	if !hasSudo {
		// Commands that typically need sudo
		sudoCommands := []string{
			"scstadmin",
			"systemctl",
			"zfs",
			"zpool",
			"mount",
			"umount",
			"ip link",
			"ip addr",
			"iptables",
			"journalctl",
		}
		
		for _, sudoCmd := range sudoCommands {
			if strings.HasPrefix(commandLower, sudoCmd) {
				needsSudo = true
				break
			}
		}
		
		// Service-specific sudo requirements
		switch service {
		case "scst":
			// All SCST admin commands need sudo
			if strings.HasPrefix(commandLower, "scstadmin") {
				needsSudo = true
			}
		case "storage":
			// ZFS commands typically need sudo
			if strings.HasPrefix(commandLower, "zfs") || strings.HasPrefix(commandLower, "zpool") {
				needsSudo = true
			}
		case "system":
			// System commands like systemctl need sudo
			if strings.HasPrefix(commandLower, "systemctl") || strings.HasPrefix(commandLower, "journalctl") {
				needsSudo = true
			}
		}
	}

	// Build command with or without sudo
	var cmd *exec.Cmd
	if needsSudo && !hasSudo {
		// Use sudo for privileged commands (if not already present)
		cmd = exec.CommandContext(ctx, "sudo", "sh", "-c", command)
	} else {
		// Regular command (or already has sudo)
		cmd = exec.CommandContext(ctx, "sh", "-c", command)
	}
	
	cmd.Env = append(os.Environ(), "TERM=xterm-256color")
	
	cmd.Env = append(os.Environ(), "TERM=xterm-256color")

	output, err := cmd.CombinedOutput()
	
	if err != nil {
		// Return output even if there's an error (some commands return non-zero exit codes)
		outputStr := string(output)
		if len(outputStr) > 0 {
			return outputStr, nil
		}
		return "", fmt.Errorf("command execution failed: %w", err)
	}

	return string(output), nil
}
