package api

import (
	"encoding/json"
	"fmt"
	"log"
	"strconv"
	"time"

	"git.threefold.info/herocode/heroagent/pkg/processmanager"
	"git.threefold.info/herocode/heroagent/pkg/processmanager/interfaces"
	"git.threefold.info/herocode/heroagent/pkg/processmanager/interfaces/openrpc"
	"github.com/gofiber/fiber/v2"
)

// ProcessDisplayInfo represents information about a process for display purposes
type ProcessDisplayInfo struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Status    string `json:"status"`
	Uptime    string `json:"uptime"`
	StartTime string `json:"start_time"`
	CPU       string `json:"cpu"`
	Memory    string `json:"memory"`
}

// ConvertToDisplayInfo converts a ProcessInfo from the processmanager package to ProcessDisplayInfo
func ConvertToDisplayInfo(info *processmanager.ProcessInfo) ProcessDisplayInfo {
	// Calculate uptime from start time
	uptime := formatUptime(time.Since(info.StartTime))

	return ProcessDisplayInfo{
		ID:        fmt.Sprintf("%d", info.PID),
		Name:      info.Name,
		Status:    string(info.Status),
		Uptime:    uptime,
		StartTime: info.StartTime.Format("2006-01-02 15:04:05"),
		CPU:       fmt.Sprintf("%.2f%%", info.CPUPercent),
		Memory:    fmt.Sprintf("%.2f MB", info.MemoryMB),
	}
}

// ServiceHandler handles service-related API routes
type ServiceHandler struct {
	client *openrpc.Client
	logger *log.Logger
}

// default number of log lines to retrieve - use a high value to essentially show all logs
const DefaultLogLines = 10000

// NewServiceHandler creates a new service handler with the provided socket path and secret
func NewServiceHandler(socketPath, secret string, logger *log.Logger) *ServiceHandler {
	fmt.Printf("DEBUG: Creating new api.ServiceHandler with socket path: %s and secret: %s\n", socketPath, secret)
	return &ServiceHandler{
		client: openrpc.NewClient(socketPath, secret),
		logger: logger,
	}
}

// RegisterRoutes registers service API routes
func (h *ServiceHandler) RegisterRoutes(app *fiber.App) {
	// Register common routes to both API and admin groups
	serviceRoutes := func(group fiber.Router) {
		group.Get("/running", h.getRunningServices)
		group.Post("/start", h.startService)
		group.Post("/stop", h.stopService)
		group.Post("/restart", h.restartService)
		group.Post("/delete", h.deleteService)
		group.Post("/logs", h.getProcessLogs)
	}

	// Apply common routes to API group
	apiServices := app.Group("/api/services")
	serviceRoutes(apiServices)

	// Apply common routes to admin group and add admin-specific routes
	adminServices := app.Group("/admin/services")
	serviceRoutes(adminServices)

	// Admin-only routes
	adminServices.Get("/", h.getServicesPage)
	adminServices.Get("/data", h.getServicesData)
}

// getProcessList gets a list of processes from the process manager
// TODO: add swagger annotations
func (h *ServiceHandler) getProcessList() ([]ProcessDisplayInfo, error) {
	// Debug: Log the function entry
	h.logger.Printf("Entering getProcessList() function")
	fmt.Printf("DEBUG: API getProcessList called using client: %p\n", h.client)

	// Get the list of processes via the client
	result, err := h.client.ListProcesses("json")
	if err != nil {
		h.logger.Printf("Error listing processes: %v", err)
		return nil, err
	}

	// Convert the result to a slice of ProcessStatus
	processStatuses, ok := result.([]interfaces.ProcessStatus)
	if !ok {
		// Try to handle the result as a map or other structure
		h.logger.Printf("Warning: unexpected result type from ListProcesses, trying alternative parsing")

		// Try to convert the result to JSON and then parse it
		resultJSON, err := json.Marshal(result)
		if err != nil {
			h.logger.Printf("Error marshaling result to JSON: %v", err)
			return nil, fmt.Errorf("failed to marshal result: %w", err)
		}

		var processStatuses []interfaces.ProcessStatus
		if err := json.Unmarshal(resultJSON, &processStatuses); err != nil {
			h.logger.Printf("Error unmarshaling result to ProcessStatus: %v", err)
			return nil, fmt.Errorf("failed to unmarshal process list result: %w", err)
		}

		// Convert to display info format
		displayInfoList := make([]ProcessDisplayInfo, 0, len(processStatuses))
		for _, proc := range processStatuses {
			// Calculate uptime based on start time
			uptime := formatUptime(time.Since(proc.StartTime))

			displayInfo := ProcessDisplayInfo{
				ID:        fmt.Sprintf("%d", proc.PID),
				Name:      proc.Name,
				Status:    string(proc.Status),
				Uptime:    uptime,
				StartTime: proc.StartTime.Format("2006-01-02 15:04:05"),
				CPU:       fmt.Sprintf("%.2f%%", proc.CPUPercent),
				Memory:    fmt.Sprintf("%.2f MB", proc.MemoryMB),
			}
			displayInfoList = append(displayInfoList, displayInfo)
		}

		// Debug: Log the number of processes
		h.logger.Printf("Found %d processes", len(displayInfoList))
		return displayInfoList, nil
	}

	// Convert to display info format
	displayInfoList := make([]ProcessDisplayInfo, 0, len(processStatuses))
	for _, proc := range processStatuses {
		// Calculate uptime based on start time
		uptime := formatUptime(time.Since(proc.StartTime))

		displayInfo := ProcessDisplayInfo{
			ID:        fmt.Sprintf("%d", proc.PID),
			Name:      proc.Name,
			Status:    string(proc.Status),
			Uptime:    uptime,
			StartTime: proc.StartTime.Format("2006-01-02 15:04:05"),
			CPU:       fmt.Sprintf("%.2f%%", proc.CPUPercent),
			Memory:    fmt.Sprintf("%.2f MB", proc.MemoryMB),
		}
		displayInfoList = append(displayInfoList, displayInfo)
	}

	// Debug: Log the number of processes
	h.logger.Printf("Found %d processes", len(displayInfoList))

	return displayInfoList, nil
}

// formatUptime formats a duration as a human-readable uptime string
func formatUptime(duration time.Duration) string {
	totalSeconds := int(duration.Seconds())
	days := totalSeconds / (24 * 3600)
	hours := (totalSeconds % (24 * 3600)) / 3600
	minutes := (totalSeconds % 3600) / 60
	seconds := totalSeconds % 60

	if days > 0 {
		return fmt.Sprintf("%d days, %d hours", days, hours)
	} else if hours > 0 {
		return fmt.Sprintf("%d hours, %d minutes", hours, minutes)
	} else if minutes > 0 {
		return fmt.Sprintf("%d minutes, %d seconds", minutes, seconds)
	} else {
		return fmt.Sprintf("%d seconds", seconds)
	}
}

// @Summary Start a service
// @Description Start a new service with the given name and command
// @Tags services
// @Accept x-www-form-urlencoded
// @Produce json
// @Param name formData string true "Service name"
// @Param command formData string true "Command to run"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/services/start [post]
// @Router /admin/services/start [post]
func (h *ServiceHandler) startService(c *fiber.Ctx) error {
	// Get form values
	name := c.FormValue("name")
	command := c.FormValue("command")

	// Validate inputs
	if name == "" || command == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"error":   "Name and command are required",
		})
	}

	// Start the process with default values
	// logEnabled=true, deadline=0 (no deadline), no cron, no jobID
	fmt.Printf("DEBUG: API startService called for '%s' using client: %p\n", name, h.client)
	result, err := h.client.StartProcess(name, command, true, 0, "", "")
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to start service: %v", err),
		})
	}

	// Check if the result indicates success
	if !result.Success {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   result.Message,
		})
	}

	// Get the PID from the result
	pid := result.PID

	return c.JSON(fiber.Map{
		"success": true,
		"message": fmt.Sprintf("Service '%s' started with PID %d", name, pid),
		"pid":     pid,
	})
}

// @Summary Stop a service
// @Description Stop a running service by name
// @Tags services
// @Accept x-www-form-urlencoded
// @Produce json
// @Param name formData string true "Service name"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/services/stop [post]
// @Router /admin/services/stop [post]
// stopService stops a service
func (h *ServiceHandler) stopService(c *fiber.Ctx) error {
	// Get form values
	name := c.FormValue("name")

	// For backward compatibility, try ID field if name is empty
	if name == "" {
		name = c.FormValue("id")
		if name == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"error":   "Process name is required",
			})
		}
	}

	// Log the stop request
	h.logger.Printf("Stopping process with name: %s", name)

	// Stop the process
	fmt.Printf("DEBUG: API stopService called for '%s' using client: %p\n", name, h.client)
	result, err := h.client.StopProcess(name)
	if err != nil {
		h.logger.Printf("Error stopping process: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to stop service: %v", err),
		})
	}

	// Check if the result indicates success
	if !result.Success {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   result.Message,
		})
	}

	return c.JSON(fiber.Map{
		"success": true,
		"message": fmt.Sprintf("Service '%s' stopped successfully", name),
	})
}

// @Summary Restart a service
// @Description Restart a running service by name
// @Tags services
// @Accept x-www-form-urlencoded
// @Produce json
// @Param name formData string true "Service name"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/services/restart [post]
// @Router /admin/services/restart [post]
// restartService restarts a service
func (h *ServiceHandler) restartService(c *fiber.Ctx) error {
	// Get form values
	name := c.FormValue("name")

	// For backward compatibility, try ID field if name is empty
	if name == "" {
		name = c.FormValue("id")
		if name == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"error":   "Process name is required",
			})
		}
	}

	// Log the restart request
	h.logger.Printf("Restarting process with name: %s", name)

	// Restart the process
	fmt.Printf("DEBUG: API restartService called for '%s' using client: %p\n", name, h.client)
	result, err := h.client.RestartProcess(name)
	if err != nil {
		h.logger.Printf("Error restarting process: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to restart service: %v", err),
		})
	}

	// Check if the result indicates success
	if !result.Success {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   result.Message,
		})
	}

	return c.JSON(fiber.Map{
		"success": true,
		"message": fmt.Sprintf("Service '%s' restarted successfully", name),
	})
}

// @Summary Delete a service
// @Description Delete a service by name
// @Tags services
// @Accept x-www-form-urlencoded
// @Produce json
// @Param name formData string true "Service name"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/services/delete [post]
// @Router /admin/services/delete [post]
// deleteService deletes a service
func (h *ServiceHandler) deleteService(c *fiber.Ctx) error {
	// Get form values
	name := c.FormValue("name")

	// Validate inputs
	if name == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"error":   "Service name is required",
		})
	}

	// Debug: Log the delete request
	h.logger.Printf("Deleting process with name: %s", name)

	// Delete the process
	fmt.Printf("DEBUG: API deleteService called for '%s' using client: %p\n", name, h.client)
	result, err := h.client.DeleteProcess(name)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to delete service: %v", err),
		})
	}

	// Check if the result indicates success
	if !result.Success {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   result.Message,
		})
	}

	return c.JSON(fiber.Map{
		"success": true,
		"message": fmt.Sprintf("Service '%s' deleted successfully", name),
	})
}

// @Summary Get running services
// @Description Get a list of all currently running services
// @Tags services
// @Accept json
// @Produce json
// @Success 200 {object} map[string][]ProcessDisplayInfo
// @Failure 500 {object} map[string]string
// @Router /api/services/running [get]
// @Router /admin/services/running [get]
func (h *ServiceHandler) getRunningServices(c *fiber.Ctx) error {
	// Get the list of processes
	processes, err := h.getProcessList()
	if err != nil {
		h.logger.Printf("Error getting process list: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to get process list: %v", err),
		})
	}

	// Filter to only include running processes
	runningProcesses := make([]ProcessDisplayInfo, 0)
	for _, proc := range processes {
		if proc.Status == "running" {
			runningProcesses = append(runningProcesses, proc)
		}
	}

	// Return the processes as JSON
	return c.JSON(fiber.Map{
		"success":   true,
		"services":  runningProcesses,
		"processes": processes, // Keep for backward compatibility
	})
}

// @Summary Get process logs
// @Description Get logs for a specific process
// @Tags services
// @Accept x-www-form-urlencoded
// @Produce json
// @Param name formData string true "Service name"
// @Param lines formData integer false "Number of log lines to retrieve"
// @Success 200 {object} map[string]string
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/services/logs [post]
// @Router /admin/services/logs [post]
// getProcessLogs retrieves logs for a specific process
func (h *ServiceHandler) getProcessLogs(c *fiber.Ctx) error {
	// Get form values
	name := c.FormValue("name")

	// For backward compatibility, try ID field if name is empty
	if name == "" {
		name = c.FormValue("id")
		if name == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"error":   "Process name is required",
			})
		}
	}

	// Get the number of lines to retrieve
	linesStr := c.FormValue("lines")
	lines := DefaultLogLines
	if linesStr != "" {
		if parsedLines, err := strconv.Atoi(linesStr); err == nil && parsedLines > 0 {
			lines = parsedLines
		}
	}

	// Log the request
	h.logger.Printf("Getting logs for process: %s (lines: %d)", name, lines)

	// Get logs
	fmt.Printf("DEBUG: API getProcessLogs called for '%s' using client: %p\n", name, h.client)
	logs, err := h.client.GetProcessLogs(name, lines)
	if err != nil {
		h.logger.Printf("Error getting process logs: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success": false,
			"error":   fmt.Sprintf("Failed to get logs: %v", err),
		})
	}

	return c.JSON(fiber.Map{
		"success": true,
		"logs":    logs,
	})
}

// @Summary Get services page
// @Description Get the services management page
// @Tags admin
// @Produce html
// @Success 200 {string} string "HTML content"
// @Failure 500 {object} map[string]string
// @Router /admin/services/ [get]
// getServicesPage renders the services page
func (h *ServiceHandler) getServicesPage(c *fiber.Ctx) error {
	// Get processes to display on the initial page load
	processes, _ := h.getProcessList()

	// Check if client is properly initialized
	var warning string
	if h.client == nil {
		warning = "Process manager client is not properly initialized."
		h.logger.Printf("Warning: %s", warning)
	}

	return c.Render("admin/services", fiber.Map{
		"title":     "Services",
		"processes": processes,
		"warning":   warning,
	})
}

// @Summary Get services data
// @Description Get services data for AJAX updates
// @Tags admin
// @Produce html
// @Success 200 {string} string "HTML content"
// @Failure 500 {object} map[string]string
// @Router /admin/services/data [get]
// getServicesData returns only the services fragment for AJAX updates
func (h *ServiceHandler) getServicesData(c *fiber.Ctx) error {
	// Get processes
	processes, _ := h.getProcessList()

	// Check if client is properly initialized
	var warning string
	if h.client == nil {
		warning = "Process manager client is not properly initialized."
		h.logger.Printf("Warning: %s", warning)
	}

	// Return the fragment with process data and optional warning
	return c.Render("admin/services_fragment", fiber.Map{
		"processes": processes,
		"warning":   warning,
		"layout":    "",
	})
}
