// Filename: internal/domain/proxy/handler.go
package proxy

import (
	"context"
	"encoding/json"
	"gemini-balancer/internal/errors"
	"gemini-balancer/internal/models"
	"gemini-balancer/internal/response"
	"gemini-balancer/internal/settings"
	"gemini-balancer/internal/store"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type handler struct {
	db       *gorm.DB
	manager  *manager
	store    store.Store
	settings *settings.SettingsManager
}

func newHandler(db *gorm.DB, m *manager, s store.Store, sp *settings.SettingsManager) *handler {
	return &handler{
		db:       db,
		manager:  m,
		store:    s,
		settings: sp,
	}
}

// === 领域暴露的公共API ===

func (h *handler) registerRoutes(rg *gin.RouterGroup) {
	proxyRoutes := rg.Group("/proxies")
	{
		proxyRoutes.PUT("/sync", h.SyncProxies)
		proxyRoutes.POST("/check", h.CheckSingleProxy)
		proxyRoutes.POST("/check-all", h.CheckAllProxies)

		proxyRoutes.POST("/", h.CreateProxyConfig)
		proxyRoutes.GET("/", h.ListProxyConfigs)
		proxyRoutes.GET("/:id", h.GetProxyConfig)
		proxyRoutes.PUT("/:id", h.UpdateProxyConfig)
		proxyRoutes.DELETE("/:id", h.DeleteProxyConfig)

	}
}

type CreateProxyConfigRequest struct {
	Address     string `json:"address" binding:"required"`
	Protocol    string `json:"protocol" binding:"required,oneof=http https socks5"`
	Status      string `json:"status" binding:"omitempty,oneof=active inactive"`
	Description string `json:"description"`
}

type UpdateProxyConfigRequest struct {
	Address     *string `json:"address"`
	Protocol    *string `json:"protocol" binding:"omitempty,oneof=http https socks5"`
	Status      *string `json:"status" binding:"omitempty,oneof=active inactive"`
	Description *string `json:"description"`
}

type CheckSingleProxyRequest struct {
	Proxy string `json:"proxy" binding:"required"`
}

type CheckAllProxiesRequest struct {
	Proxies []string `json:"proxies" binding:"required"`
}

// --- Handler 方法 ---

func (h *handler) CreateProxyConfig(c *gin.Context) {
	var req CreateProxyConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
		return
	}

	if req.Status == "" {
		req.Status = "active"
	}

	proxyConfig := models.ProxyConfig{
		Address:     req.Address,
		Protocol:    req.Protocol,
		Status:      req.Status,
		Description: req.Description,
	}

	if err := h.db.Create(&proxyConfig).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}
	h.publishAndInvalidate(proxyConfig.ID, "created")
	response.Created(c, proxyConfig)
}

func (h *handler) ListProxyConfigs(c *gin.Context) {
	var proxyConfigs []models.ProxyConfig
	if err := h.db.Find(&proxyConfigs).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}
	response.Success(c, proxyConfigs)
}

func (h *handler) GetProxyConfig(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
		return
	}

	var proxyConfig models.ProxyConfig
	if err := h.db.First(&proxyConfig, id).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}
	response.Success(c, proxyConfig)
}

func (h *handler) UpdateProxyConfig(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
		return
	}

	var req UpdateProxyConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
		return
	}

	var proxyConfig models.ProxyConfig
	if err := h.db.First(&proxyConfig, id).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}

	if req.Address != nil {
		proxyConfig.Address = *req.Address
	}
	if req.Protocol != nil {
		proxyConfig.Protocol = *req.Protocol
	}
	if req.Status != nil {
		proxyConfig.Status = *req.Status
	}
	if req.Description != nil {
		proxyConfig.Description = *req.Description
	}

	if err := h.db.Save(&proxyConfig).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}

	h.publishAndInvalidate(uint(id), "updated")
	response.Success(c, proxyConfig)
}

func (h *handler) DeleteProxyConfig(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrBadRequest, "Invalid ID format"))
		return
	}

	var count int64
	if err := h.db.Model(&models.APIKey{}).Where("proxy_id = ?", id).Count(&count).Error; err != nil {
		response.Error(c, errors.ParseDBError(err))
		return
	}
	if count > 0 {
		response.Error(c, errors.NewAPIError(errors.ErrDuplicateResource, "Cannot delete proxy config that is still in use by API keys"))
		return
	}

	result := h.db.Delete(&models.ProxyConfig{}, id)
	if result.Error != nil {
		response.Error(c, errors.ParseDBError(result.Error))
		return
	}
	if result.RowsAffected == 0 {
		response.Error(c, errors.ErrResourceNotFound)
		return
	}

	h.publishAndInvalidate(uint(id), "deleted")
	response.NoContent(c)
}

func (h *handler) publishAndInvalidate(proxyID uint, action string) {
	go h.manager.invalidate()
	go func() {
		ctx := context.Background()
		event := models.ProxyStatusChangedEvent{ProxyID: proxyID, Action: action}
		eventData, _ := json.Marshal(event)
		_ = h.store.Publish(ctx, models.TopicProxyStatusChanged, eventData)
	}()
}

type SyncProxiesRequest struct {
	Proxies []string `json:"proxies"`
}

func (h *handler) SyncProxies(c *gin.Context) {
	var req SyncProxiesRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
		return
	}

	taskStatus, err := h.manager.SyncProxiesInBackground(c.Request.Context(), req.Proxies)
	if err != nil {
		if errors.Is(err, ErrTaskConflict) {
			response.Error(c, errors.NewAPIError(errors.ErrTaskInProgress, err.Error()))
		} else {
			response.Error(c, errors.NewAPIError(errors.ErrInternalServer, err.Error()))
		}
		return
	}
	response.Success(c, gin.H{
		"message": "Proxy synchronization task started.",
		"task":    taskStatus,
	})
}

func (h *handler) CheckSingleProxy(c *gin.Context) {
	var req CheckSingleProxyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
		return
	}

	cfg := h.settings.GetSettings()
	timeout := time.Duration(cfg.ProxyCheckTimeoutSeconds) * time.Second
	result := h.manager.CheckSingleProxy(req.Proxy, timeout)
	response.Success(c, result)
}

func (h *handler) CheckAllProxies(c *gin.Context) {
	var req CheckAllProxiesRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Error(c, errors.NewAPIError(errors.ErrInvalidJSON, err.Error()))
		return
	}
	cfg := h.settings.GetSettings()
	timeout := time.Duration(cfg.ProxyCheckTimeoutSeconds) * time.Second

	concurrency := cfg.ProxyCheckConcurrency
	if concurrency <= 0 {
		concurrency = 5
	}
	results := h.manager.CheckMultipleProxies(req.Proxies, timeout, concurrency)
	response.Success(c, results)
}
