package handlers

import (
	"context"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"yh_web/server/config"

	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

const chunkCleanupConfigID = "chunk_upload_cleanup"

type chunkCleanupConfigDoc struct {
	MaxAgeHours  float64 `bson:"max_age_hours" json:"max_age_hours"`
	SweepMinutes int     `bson:"sweep_minutes" json:"sweep_minutes"`
}

func maxAgeFromEnv() time.Duration {
	h := strings.TrimSpace(os.Getenv("YH_CHUNK_UPLOAD_MAX_AGE_HOURS"))
	if h == "" {
		return 72 * time.Hour
	}
	v, err := strconv.ParseFloat(h, 64)
	if err != nil || v < 6 {
		return 72 * time.Hour
	}
	return time.Duration(v * float64(time.Hour))
}

func sweepFromEnv() time.Duration {
	m := strings.TrimSpace(os.Getenv("YH_CHUNK_UPLOAD_SWEEP_MINUTES"))
	if m == "" {
		return time.Hour
	}
	v, err := strconv.Atoi(m)
	if err != nil || v < 5 {
		return time.Hour
	}
	return time.Duration(v) * time.Minute
}

func normalizeMaxAgeHours(h float64) time.Duration {
	if h < 6 {
		return 6 * time.Hour
	}
	if h > 336 {
		return 336 * time.Hour
	}
	return time.Duration(h * float64(time.Hour))
}

func normalizeSweepMinutes(m int) time.Duration {
	if m < 5 {
		return 5 * time.Minute
	}
	if m > 1440 {
		return 1440 * time.Minute
	}
	return time.Duration(m) * time.Minute
}

// loadChunkCleanupParameters 优先读 MongoDB system_config；无文档时用环境变量；用于定时清扫
func loadChunkCleanupParameters() (maxAge time.Duration, sweepEvery time.Duration) {
	db := config.GetDB(config.DBName)
	if db != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		var doc chunkCleanupConfigDoc
		err := db.Collection("system_config").FindOne(ctx, bson.M{"_id": chunkCleanupConfigID}).Decode(&doc)
		if err == nil && doc.MaxAgeHours >= 6 && doc.SweepMinutes >= 5 {
			return normalizeMaxAgeHours(doc.MaxAgeHours), normalizeSweepMinutes(doc.SweepMinutes)
		}
	}
	return maxAgeFromEnv(), sweepFromEnv()
}

// GetChunkUploadCleanupConfig 后台读取当前保存的配置（无文档时返回默认值）
func GetChunkUploadCleanupConfig(c *gin.Context) {
	db := config.GetDB(config.DBName)
	if db == nil {
		c.JSON(http.StatusOK, chunkCleanupConfigDoc{
			MaxAgeHours:  72,
			SweepMinutes: 60,
		})
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	var doc chunkCleanupConfigDoc
	err := db.Collection("system_config").FindOne(ctx, bson.M{"_id": chunkCleanupConfigID}).Decode(&doc)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			c.JSON(http.StatusOK, chunkCleanupConfigDoc{
				MaxAgeHours:  72,
				SweepMinutes: 60,
			})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if doc.MaxAgeHours < 6 {
		doc.MaxAgeHours = 72
	}
	if doc.SweepMinutes < 5 {
		doc.SweepMinutes = 60
	}
	c.JSON(http.StatusOK, doc)
}

// ChunkUploadCleanupUpdateInput 后台保存
type ChunkUploadCleanupUpdateInput struct {
	MaxAgeHours  float64 `json:"max_age_hours" binding:"required"`
	SweepMinutes int     `json:"sweep_minutes" binding:"required"`
}

// UpdateChunkUploadCleanupConfig 保存分片临时目录保留时长与扫描间隔
func UpdateChunkUploadCleanupConfig(c *gin.Context) {
	var input ChunkUploadCleanupUpdateInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if input.MaxAgeHours < 6 || input.MaxAgeHours > 336 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "保留时长须在 6～336 小时之间"})
		return
	}
	if input.SweepMinutes < 5 || input.SweepMinutes > 1440 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "扫描间隔须在 5～1440 分钟之间"})
		return
	}

	db := config.GetDB(config.DBName)
	if db == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "数据库未连接，无法保存"})
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
	defer cancel()
	coll := db.Collection("system_config")
	set := bson.M{
		"_id":             chunkCleanupConfigID,
		"max_age_hours":   input.MaxAgeHours,
		"sweep_minutes":   input.SweepMinutes,
		"updated_at":      time.Now().Format(time.RFC3339),
		"updated_by_hint": "admin",
	}
	opts := options.UpdateOne().SetUpsert(true)
	_, err := coll.UpdateOne(ctx, bson.M{"_id": chunkCleanupConfigID}, bson.M{"$set": set}, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "配置已保存"})
}
