package admin

import (
	"encoding/gob"
	"time"

	model "github.com/cloudreve/Cloudreve/v3/models"
	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
	"github.com/cloudreve/Cloudreve/v3/pkg/conf"
	"github.com/cloudreve/Cloudreve/v3/pkg/email"
	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
)

func init() {
	gob.Register(map[string]interface{}{})
	gob.Register(map[string]string{})
}

// NoParamService 无需参数的服务
type NoParamService struct {
}

// BatchSettingChangeService 设定批量更改服务
type BatchSettingChangeService struct {
	Options []SettingChangeService `json:"options"`
}

// SettingChangeService  设定更改服务
type SettingChangeService struct {
	Key   string `json:"key" binding:"required"`
	Value string `json:"value"`
}

// BatchSettingGet 设定批量获取服务
type BatchSettingGet struct {
	Keys []string `json:"keys"`
}

// MailTestService 邮件测试服务
type MailTestService struct {
	Email string `json:"to" binding:"email"`
}

// Send 发送测试邮件
func (service *MailTestService) Send() serializer.Response {
	if err := email.Send(service.Email, "Cloudreve Email delivery test", "This is a test Email, to test Cloudreve Email delivery settings"); err != nil {
		return serializer.Err(serializer.CodeFailedSendEmail, err.Error(), nil)
	}
	return serializer.Response{}
}

// Get 获取设定值
func (service *BatchSettingGet) Get() serializer.Response {
	options := model.GetSettingByNames(service.Keys...)
	return serializer.Response{Data: options}
}

// Change 批量更改站点设定
func (service *BatchSettingChangeService) Change() serializer.Response {
	cacheClean := make([]string, 0, len(service.Options))
	tx := model.DB.Begin()

	for _, setting := range service.Options {

		if err := tx.Model(&model.Setting{}).Where("name = ?", setting.Key).Update("value", setting.Value).Error; err != nil {
			cache.Deletes(cacheClean, "setting_")
			tx.Rollback()
			return serializer.Err(serializer.CodeUpdateSetting, "Setting "+setting.Key+" failed to update", err)
		}

		cacheClean = append(cacheClean, setting.Key)
	}

	if err := tx.Commit().Error; err != nil {
		return serializer.DBErr("Failed to update setting", err)
	}

	cache.Deletes(cacheClean, "setting_")

	return serializer.Response{}
}

// Summary 获取站点统计概况
func (service *NoParamService) Summary() serializer.Response {
	// 获取版本信息
	versions := map[string]string{
		"backend": conf.BackendVersion,
		"db":      conf.RequiredDBVersion,
		"commit":  conf.LastCommit,
		"is_pro":  conf.IsPro,
	}

	if res, ok := cache.Get("admin_summary"); ok {
		resMap := res.(map[string]interface{})
		resMap["version"] = versions
		resMap["siteURL"] = model.GetSettingByName("siteURL")
		return serializer.Response{Data: resMap}
	}

	// 统计每日概况
	total := 12
	files := make([]int, total)
	users := make([]int, total)
	shares := make([]int, total)
	date := make([]string, total)

	toRound := time.Now()
	timeBase := time.Date(toRound.Year(), toRound.Month(), toRound.Day()+1, 0, 0, 0, 0, toRound.Location())
	for day := range files {
		start := timeBase.Add(-time.Duration(total-day) * time.Hour * 24)
		end := timeBase.Add(-time.Duration(total-day-1) * time.Hour * 24)
		date[day] = start.Format("1月2日")
		model.DB.Model(&model.User{}).Where("created_at BETWEEN ? AND ?", start, end).Count(&users[day])
		model.DB.Model(&model.File{}).Where("created_at BETWEEN ? AND ?", start, end).Count(&files[day])
		model.DB.Model(&model.Share{}).Where("created_at BETWEEN ? AND ?", start, end).Count(&shares[day])
	}

	// 统计总数
	fileTotal := 0
	userTotal := 0
	publicShareTotal := 0
	secretShareTotal := 0
	model.DB.Model(&model.User{}).Count(&userTotal)
	model.DB.Model(&model.File{}).Count(&fileTotal)
	model.DB.Model(&model.Share{}).Where("password = ?", "").Count(&publicShareTotal)
	model.DB.Model(&model.Share{}).Where("password <> ?", "").Count(&secretShareTotal)

	resp := map[string]interface{}{
		"date":             date,
		"files":            files,
		"users":            users,
		"shares":           shares,
		"version":          versions,
		"siteURL":          model.GetSettingByName("siteURL"),
		"fileTotal":        fileTotal,
		"userTotal":        userTotal,
		"publicShareTotal": publicShareTotal,
		"secretShareTotal": secretShareTotal,
	}

	cache.Set("admin_summary", resp, 86400)
	return serializer.Response{
		Data: resp,
	}
}
