package handlers

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

	"github.com/gin-gonic/gin"

	"github.com/Wikid82/charon/backend/internal/logger"
	"github.com/Wikid82/charon/backend/internal/services"
	"github.com/Wikid82/charon/backend/internal/util"
)

// BackupServiceInterface defines the contract for backup service operations
type BackupServiceInterface interface {
	CreateBackup() (string, error)
	ListBackups() ([]services.BackupFile, error)
	DeleteBackup(filename string) error
	GetBackupPath(filename string) (string, error)
	RestoreBackup(filename string) error
	GetAvailableSpace() (int64, error)
}

type CertificateHandler struct {
	service             *services.CertificateService
	backupService       BackupServiceInterface
	notificationService *services.NotificationService
	// Rate limiting for notifications
	notificationMu       sync.Mutex
	lastNotificationTime map[uint]time.Time
}

func NewCertificateHandler(service *services.CertificateService, backupService BackupServiceInterface, ns *services.NotificationService) *CertificateHandler {
	return &CertificateHandler{
		service:              service,
		backupService:        backupService,
		notificationService:  ns,
		lastNotificationTime: make(map[uint]time.Time),
	}
}

func (h *CertificateHandler) List(c *gin.Context) {
	certs, err := h.service.ListCertificates()
	if err != nil {
		logger.Log().WithError(err).Error("failed to list certificates")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list certificates"})
		return
	}

	c.JSON(http.StatusOK, certs)
}

type UploadCertificateRequest struct {
	Name        string `form:"name" binding:"required"`
	Certificate string `form:"certificate"` // PEM content
	PrivateKey  string `form:"private_key"` // PEM content
}

func (h *CertificateHandler) Upload(c *gin.Context) {
	// Handle multipart form
	name := c.PostForm("name")
	if name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
		return
	}

	// Read files
	certFile, err := c.FormFile("certificate_file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "certificate_file is required"})
		return
	}

	keyFile, err := c.FormFile("key_file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "key_file is required"})
		return
	}

	// Open and read content
	certSrc, err := certFile.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open cert file"})
		return
	}
	defer func() {
		if err := certSrc.Close(); err != nil {
			logger.Log().WithError(err).Warn("failed to close certificate file")
		}
	}()

	keySrc, err := keyFile.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open key file"})
		return
	}
	defer func() {
		if err := keySrc.Close(); err != nil {
			logger.Log().WithError(err).Warn("failed to close key file")
		}
	}()

	// Read to string
	// Limit size to avoid DoS (e.g. 1MB)
	certBytes := make([]byte, 1024*1024)
	n, _ := certSrc.Read(certBytes)
	certPEM := string(certBytes[:n])

	keyBytes := make([]byte, 1024*1024)
	n, _ = keySrc.Read(keyBytes)
	keyPEM := string(keyBytes[:n])

	cert, err := h.service.UploadCertificate(name, certPEM, keyPEM)
	if err != nil {
		logger.Log().WithError(err).Error("failed to upload certificate")
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to upload certificate"})
		return
	}

	// Send Notification
	if h.notificationService != nil {
		h.notificationService.SendExternal(c.Request.Context(),
			"cert",
			"Certificate Uploaded",
			fmt.Sprintf("Certificate %s uploaded", util.SanitizeForLog(cert.Name)),
			map[string]interface{}{
				"Name":    util.SanitizeForLog(cert.Name),
				"Domains": util.SanitizeForLog(cert.Domains),
				"Action":  "uploaded",
			},
		)
	}

	c.JSON(http.StatusCreated, cert)
}

func (h *CertificateHandler) Delete(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
		return
	}

	// Validate ID range
	if id == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
		return
	}

	// Check if certificate is in use before proceeding
	inUse, err := h.service.IsCertificateInUse(uint(id))
	if err != nil {
		logger.Log().WithError(err).WithField("certificate_id", id).Error("failed to check certificate usage")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to check certificate usage"})
		return
	}
	if inUse {
		c.JSON(http.StatusConflict, gin.H{"error": "certificate is in use by one or more proxy hosts"})
		return
	}

	// Create backup before deletion
	if h.backupService != nil {
		// Check disk space before backup (require at least 100MB free)
		if availableSpace, err := h.backupService.GetAvailableSpace(); err != nil {
			logger.Log().WithError(err).Warn("unable to check disk space, proceeding with backup")
		} else if availableSpace < 100*1024*1024 {
			logger.Log().WithField("available_bytes", availableSpace).Warn("low disk space, skipping backup")
			c.JSON(http.StatusInsufficientStorage, gin.H{"error": "insufficient disk space for backup"})
			return
		}

		if _, err := h.backupService.CreateBackup(); err != nil {
			logger.Log().WithError(err).Error("failed to create backup before deletion")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create backup before deletion"})
			return
		}
	}

	// Proceed with deletion
	if err := h.service.DeleteCertificate(uint(id)); err != nil {
		if err == services.ErrCertInUse {
			c.JSON(http.StatusConflict, gin.H{"error": "certificate is in use by one or more proxy hosts"})
			return
		}
		logger.Log().WithError(err).WithField("certificate_id", id).Error("failed to delete certificate")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete certificate"})
		return
	}

	// Send Notification with rate limiting (1 per cert per 10 seconds)
	if h.notificationService != nil {
		h.notificationMu.Lock()
		lastTime, exists := h.lastNotificationTime[uint(id)]
		if !exists || time.Since(lastTime) > 10*time.Second {
			h.lastNotificationTime[uint(id)] = time.Now()
			h.notificationMu.Unlock()
			h.notificationService.SendExternal(c.Request.Context(),
				"cert",
				"Certificate Deleted",
				fmt.Sprintf("Certificate ID %d deleted", id),
				map[string]interface{}{
					"ID":     id,
					"Action": "deleted",
				},
			)
		} else {
			h.notificationMu.Unlock()
			logger.Log().WithField("certificate_id", id).Debug("notification rate limited")
		}
	}

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