package system

import (
	"net/http"
	"strconv"
	"time"

	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/atlasos/calypso/internal/tasks"
	"github.com/gin-gonic/gin"
)

// Handler handles system management API requests
type Handler struct {
	service    *Service
	taskEngine *tasks.Engine
	logger     *logger.Logger
}

// NewHandler creates a new system handler
func NewHandler(log *logger.Logger, taskEngine *tasks.Engine) *Handler {
	return &Handler{
		service:    NewService(log),
		taskEngine: taskEngine,
		logger:     log,
	}
}

// ListServices lists all system services
func (h *Handler) ListServices(c *gin.Context) {
	services, err := h.service.ListServices(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to list services", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list services"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"services": services})
}

// GetServiceStatus retrieves status of a specific service
func (h *Handler) GetServiceStatus(c *gin.Context) {
	serviceName := c.Param("name")

	status, err := h.service.GetServiceStatus(c.Request.Context(), serviceName)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "service not found"})
		return
	}

	c.JSON(http.StatusOK, status)
}

// RestartService restarts a system service
func (h *Handler) RestartService(c *gin.Context) {
	serviceName := c.Param("name")

	if err := h.service.RestartService(c.Request.Context(), serviceName); err != nil {
		h.logger.Error("Failed to restart service", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "service restarted successfully"})
}

// GetServiceLogs retrieves journald logs for a service
func (h *Handler) GetServiceLogs(c *gin.Context) {
	serviceName := c.Param("name")
	linesStr := c.DefaultQuery("lines", "100")
	lines, err := strconv.Atoi(linesStr)
	if err != nil {
		lines = 100
	}

	logs, err := h.service.GetJournalLogs(c.Request.Context(), serviceName, lines)
	if err != nil {
		h.logger.Error("Failed to get logs", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get logs"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"logs": logs})
}

// GenerateSupportBundle generates a diagnostic support bundle
func (h *Handler) GenerateSupportBundle(c *gin.Context) {
	userID, _ := c.Get("user_id")

	// Create async task
	taskID, err := h.taskEngine.CreateTask(c.Request.Context(),
		tasks.TaskTypeSupportBundle, userID.(string), map[string]interface{}{
			"operation": "generate_support_bundle",
		})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create task"})
		return
	}

	// Run bundle generation in background
	go func() {
		ctx := c.Request.Context()
		h.taskEngine.StartTask(ctx, taskID)
		h.taskEngine.UpdateProgress(ctx, taskID, 50, "Collecting system information...")

		outputPath := "/tmp/calypso-support-bundle-" + taskID
		if err := h.service.GenerateSupportBundle(ctx, outputPath); err != nil {
			h.taskEngine.FailTask(ctx, taskID, err.Error())
			return
		}

		h.taskEngine.UpdateProgress(ctx, taskID, 100, "Support bundle generated")
		h.taskEngine.CompleteTask(ctx, taskID, "Support bundle generated successfully")
	}()

	c.JSON(http.StatusAccepted, gin.H{"task_id": taskID})
}

// ListNetworkInterfaces lists all network interfaces
func (h *Handler) ListNetworkInterfaces(c *gin.Context) {
	interfaces, err := h.service.ListNetworkInterfaces(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to list network interfaces", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list network interfaces"})
		return
	}

	// Ensure we return an empty array instead of null
	if interfaces == nil {
		interfaces = []NetworkInterface{}
	}

	c.JSON(http.StatusOK, gin.H{"interfaces": interfaces})
}

// GetManagementIPAddress returns the management IP address
func (h *Handler) GetManagementIPAddress(c *gin.Context) {
	ip, err := h.service.GetManagementIPAddress(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get management IP address", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get management IP address"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"ip_address": ip})
}

// SaveNTPSettings saves NTP configuration to the OS
func (h *Handler) SaveNTPSettings(c *gin.Context) {
	var settings NTPSettings
	if err := c.ShouldBindJSON(&settings); err != nil {
		h.logger.Error("Invalid request body", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}

	// Validate timezone
	if settings.Timezone == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "timezone is required"})
		return
	}

	// Validate NTP servers
	if len(settings.NTPServers) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "at least one NTP server is required"})
		return
	}

	if err := h.service.SaveNTPSettings(c.Request.Context(), settings); err != nil {
		h.logger.Error("Failed to save NTP settings", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "NTP settings saved successfully"})
}

// GetNTPSettings retrieves current NTP configuration
func (h *Handler) GetNTPSettings(c *gin.Context) {
	settings, err := h.service.GetNTPSettings(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get NTP settings", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get NTP settings"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"settings": settings})
}

// UpdateNetworkInterface updates a network interface configuration
func (h *Handler) UpdateNetworkInterface(c *gin.Context) {
	ifaceName := c.Param("name")
	if ifaceName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "interface name is required"})
		return
	}

	var req struct {
		IPAddress string `json:"ip_address" binding:"required"`
		Subnet    string `json:"subnet" binding:"required"`
		Gateway   string `json:"gateway,omitempty"`
		DNS1      string `json:"dns1,omitempty"`
		DNS2      string `json:"dns2,omitempty"`
		Role      string `json:"role,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid request body", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}

	// Convert to service request
	serviceReq := UpdateNetworkInterfaceRequest{
		IPAddress: req.IPAddress,
		Subnet:    req.Subnet,
		Gateway:   req.Gateway,
		DNS1:      req.DNS1,
		DNS2:      req.DNS2,
		Role:      req.Role,
	}

	updatedIface, err := h.service.UpdateNetworkInterface(c.Request.Context(), ifaceName, serviceReq)
	if err != nil {
		h.logger.Error("Failed to update network interface", "interface", ifaceName, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"interface": updatedIface})
}

// GetSystemLogs retrieves recent system logs
func (h *Handler) GetSystemLogs(c *gin.Context) {
	limitStr := c.DefaultQuery("limit", "30")
	limit, err := strconv.Atoi(limitStr)
	if err != nil || limit <= 0 || limit > 100 {
		limit = 30
	}

	logs, err := h.service.GetSystemLogs(c.Request.Context(), limit)
	if err != nil {
		h.logger.Error("Failed to get system logs", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get system logs"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"logs": logs})
}

// GetNetworkThroughput retrieves network throughput data from RRD
func (h *Handler) GetNetworkThroughput(c *gin.Context) {
	// Default to last 5 minutes
	durationStr := c.DefaultQuery("duration", "5m")
	duration, err := time.ParseDuration(durationStr)
	if err != nil {
		duration = 5 * time.Minute
	}

	data, err := h.service.GetNetworkThroughput(c.Request.Context(), duration)
	if err != nil {
		h.logger.Error("Failed to get network throughput", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get network throughput"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"data": data})
}

// ExecuteCommand executes a shell command
func (h *Handler) ExecuteCommand(c *gin.Context) {
	var req struct {
		Command string `json:"command" binding:"required"`
		Service string `json:"service,omitempty"` // Optional: system, scst, storage, backup, tape
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid request body", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "command is required"})
		return
	}

	// Execute command based on service context
	output, err := h.service.ExecuteCommand(c.Request.Context(), req.Command, req.Service)
	if err != nil {
		h.logger.Error("Failed to execute command", "error", err, "command", req.Command, "service", req.Service)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":  err.Error(),
			"output": output, // Include output even on error
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{"output": output})
}
