package backup

import (
	"fmt"
	"net/http"

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

// Handler handles backup-related API requests
type Handler struct {
	service *Service
	logger  *logger.Logger
}

// NewHandler creates a new backup handler
func NewHandler(service *Service, log *logger.Logger) *Handler {
	return &Handler{
		service: service,
		logger:  log,
	}
}

// ListJobs lists backup jobs with optional filters
func (h *Handler) ListJobs(c *gin.Context) {
	opts := ListJobsOptions{
		Status:     c.Query("status"),
		JobType:    c.Query("job_type"),
		ClientName: c.Query("client_name"),
		JobName:    c.Query("job_name"),
	}

	// Parse pagination
	var limit, offset int
	if limitStr := c.Query("limit"); limitStr != "" {
		if _, err := fmt.Sscanf(limitStr, "%d", &limit); err == nil {
			opts.Limit = limit
		}
	}
	if offsetStr := c.Query("offset"); offsetStr != "" {
		if _, err := fmt.Sscanf(offsetStr, "%d", &offset); err == nil {
			opts.Offset = offset
		}
	}

	jobs, totalCount, err := h.service.ListJobs(c.Request.Context(), opts)
	if err != nil {
		h.logger.Error("Failed to list jobs", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list jobs"})
		return
	}

	if jobs == nil {
		jobs = []Job{}
	}

	c.JSON(http.StatusOK, gin.H{
		"jobs":   jobs,
		"total":  totalCount,
		"limit":  opts.Limit,
		"offset": opts.Offset,
	})
}

// GetJob retrieves a job by ID
func (h *Handler) GetJob(c *gin.Context) {
	id := c.Param("id")

	job, err := h.service.GetJob(c.Request.Context(), id)
	if err != nil {
		if err.Error() == "job not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "job not found"})
			return
		}
		h.logger.Error("Failed to get job", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get job"})
		return
	}

	c.JSON(http.StatusOK, job)
}

// CreateJob creates a new backup job
func (h *Handler) CreateJob(c *gin.Context) {
	var req CreateJobRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Validate job type
	validJobTypes := map[string]bool{
		"Backup": true, "Restore": true, "Verify": true, "Copy": true, "Migrate": true,
	}
	if !validJobTypes[req.JobType] {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid job_type"})
		return
	}

	// Validate job level
	validJobLevels := map[string]bool{
		"Full": true, "Incremental": true, "Differential": true, "Since": true,
	}
	if !validJobLevels[req.JobLevel] {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid job_level"})
		return
	}

	job, err := h.service.CreateJob(c.Request.Context(), req)
	if err != nil {
		h.logger.Error("Failed to create job", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create job"})
		return
	}

	c.JSON(http.StatusCreated, job)
}

// ExecuteBconsoleCommand executes a bconsole command
func (h *Handler) ExecuteBconsoleCommand(c *gin.Context) {
	var req struct {
		Command string `json:"command" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "command is required"})
		return
	}

	output, err := h.service.ExecuteBconsoleCommand(c.Request.Context(), req.Command)
	if err != nil {
		h.logger.Error("Failed to execute bconsole command", "error", err, "command", req.Command)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "failed to execute command",
			"output":  output,
			"details": err.Error(),
		})
		return
	}

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

// ListClients lists all backup clients with optional filters
func (h *Handler) ListClients(c *gin.Context) {
	opts := ListClientsOptions{}

	// Parse enabled filter
	if enabledStr := c.Query("enabled"); enabledStr != "" {
		enabled := enabledStr == "true"
		opts.Enabled = &enabled
	}

	// Parse search query
	opts.Search = c.Query("search")

	// Parse category filter
	if category := c.Query("category"); category != "" {
		// Validate category
		validCategories := map[string]bool{
			"File":     true,
			"Database": true,
			"Virtual":  true,
		}
		if validCategories[category] {
			opts.Category = category
		}
	}

	clients, err := h.service.ListClients(c.Request.Context(), opts)
	if err != nil {
		h.logger.Error("Failed to list clients", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "failed to list clients",
			"details": err.Error(),
		})
		return
	}

	if clients == nil {
		clients = []Client{}
	}

	c.JSON(http.StatusOK, gin.H{
		"clients": clients,
		"total":   len(clients),
	})
}

// GetDashboardStats returns dashboard statistics
func (h *Handler) GetDashboardStats(c *gin.Context) {
	stats, err := h.service.GetDashboardStats(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get dashboard stats", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get dashboard stats"})
		return
	}

	c.JSON(http.StatusOK, stats)
}

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

	if pools == nil {
		pools = []StoragePool{}
	}

	h.logger.Info("Listed storage pools", "count", len(pools))
	c.JSON(http.StatusOK, gin.H{
		"pools": pools,
		"total": len(pools),
	})
}

// ListStorageVolumes lists all storage volumes
func (h *Handler) ListStorageVolumes(c *gin.Context) {
	poolName := c.Query("pool_name")

	volumes, err := h.service.ListStorageVolumes(c.Request.Context(), poolName)
	if err != nil {
		h.logger.Error("Failed to list storage volumes", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list storage volumes"})
		return
	}

	if volumes == nil {
		volumes = []StorageVolume{}
	}

	c.JSON(http.StatusOK, gin.H{
		"volumes": volumes,
		"total":   len(volumes),
	})
}

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

	if daemons == nil {
		daemons = []StorageDaemon{}
	}

	c.JSON(http.StatusOK, gin.H{
		"daemons": daemons,
		"total":   len(daemons),
	})
}

// CreateStoragePool creates a new storage pool
func (h *Handler) CreateStoragePool(c *gin.Context) {
	var req CreatePoolRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	pool, err := h.service.CreateStoragePool(c.Request.Context(), req)
	if err != nil {
		h.logger.Error("Failed to create storage pool", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, pool)
}

// DeleteStoragePool deletes a storage pool
func (h *Handler) DeleteStoragePool(c *gin.Context) {
	idStr := c.Param("id")
	if idStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "pool ID is required"})
		return
	}

	var poolID int
	if _, err := fmt.Sscanf(idStr, "%d", &poolID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid pool ID"})
		return
	}

	err := h.service.DeleteStoragePool(c.Request.Context(), poolID)
	if err != nil {
		h.logger.Error("Failed to delete storage pool", "error", err, "pool_id", poolID)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

// CreateStorageVolume creates a new storage volume
func (h *Handler) CreateStorageVolume(c *gin.Context) {
	var req CreateVolumeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	volume, err := h.service.CreateStorageVolume(c.Request.Context(), req)
	if err != nil {
		h.logger.Error("Failed to create storage volume", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, volume)
}

// UpdateStorageVolume updates a storage volume
func (h *Handler) UpdateStorageVolume(c *gin.Context) {
	idStr := c.Param("id")
	if idStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "volume ID is required"})
		return
	}

	var volumeID int
	if _, err := fmt.Sscanf(idStr, "%d", &volumeID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid volume ID"})
		return
	}

	var req UpdateVolumeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	volume, err := h.service.UpdateStorageVolume(c.Request.Context(), volumeID, req)
	if err != nil {
		h.logger.Error("Failed to update storage volume", "error", err, "volume_id", volumeID)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, volume)
}

// DeleteStorageVolume deletes a storage volume
func (h *Handler) DeleteStorageVolume(c *gin.Context) {
	idStr := c.Param("id")
	if idStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "volume ID is required"})
		return
	}

	var volumeID int
	if _, err := fmt.Sscanf(idStr, "%d", &volumeID); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid volume ID"})
		return
	}

	err := h.service.DeleteStorageVolume(c.Request.Context(), volumeID)
	if err != nil {
		h.logger.Error("Failed to delete storage volume", "error", err, "volume_id", volumeID)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

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

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

	if media == nil {
		media = []Media{}
	}

	h.logger.Info("Listed media", "count", len(media))
	c.JSON(http.StatusOK, gin.H{
		"media": media,
		"total": len(media),
	})
}
