package backend

import (
	"context"
	"kpt-pasture/model"
	"kpt-pasture/util"
	"net/http"
	"strconv"
	"strings"
	"time"

	"gorm.io/gorm"

	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
	"gitee.com/xuyiping_admin/pkg/xerr"
	"go.uber.org/zap"

	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
)

func (s *StoreEntry) Bar(ctx context.Context) (*pasturePb.BarCowStructResponse, error) {
	barCowStructList := make([]*model.BarCowStruct, 0)
	var count int32 = 0
	if err := s.DB.Model(new(model.Cow)).
		Select("COUNT(*) AS number ,cow_type").
		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
		Group("cow_type").
		Find(&barCowStructList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}
	cowTypeMap := s.CowTypeMap()

	for _, v := range barCowStructList {
		count += v.Number
	}
	return &pasturePb.BarCowStructResponse{
		Code: http.StatusOK,
		Msg:  "ok",
		Data: &pasturePb.BarCowStructData{
			List:  model.BarCowStructSlice(barCowStructList).ToPB(cowTypeMap, count),
			Total: 38563,
		},
	}, nil
}

func (s *StoreEntry) NeckRingWarning(ctx context.Context) (*pasturePb.IndexNeckRingResponse, error) {
	userModel, err := s.GetUserModel(ctx)
	if err != nil {
		return nil, xerr.WithStack(err)
	}

	estrusWarningCowList := make([]*model.NeckRingEstrus, 0)
	estrusWarningLevelItems := map[int32]int32{
		int32(pasturePb.EstrusLevel_Low):    0,
		int32(pasturePb.EstrusLevel_Middle): 0,
		int32(pasturePb.EstrusLevel_High):   0,
	}
	if err = s.DB.Model(new(model.EventEstrus)).
		Where("pasture_id = ?", userModel.AppPasture.Id).
		Where("is_show = ?", pasturePb.IsShow_Ok).
		Group("cow_id").Find(&estrusWarningCowList).Error; err != nil {
		zaplog.Error("NeckRingWarning", zap.Any("estrusWarningNumber", err))
	}

	for _, v := range estrusWarningCowList {
		estrusWarningLevelItems[int32(v.Level)] += estrusWarningLevelItems[int32(v.Level)]
	}

	healthWarningNumber := int64(0)
	if err = s.DB.Model(new(model.NeckRingHealthWarning)).
		Where("pasture_id = ?", userModel.AppPasture.Id).
		Where("is_show = ?", pasturePb.IsShow_Ok).
		Group("cow_id").
		Count(&healthWarningNumber).Error; err != nil {
		zaplog.Error("NeckRingWarning", zap.Any("estrusWarningNumber", err))
	}

	return &pasturePb.IndexNeckRingResponse{
		Code: http.StatusOK,
		Msg:  "ok",
		Data: &pasturePb.NeckRingData{
			EstrusWarningNumber:     int32(len(estrusWarningCowList)),
			HealthWarningNumber:     int32(healthWarningNumber),
			AbortionWarningNumber:   0,
			StressWarningNumber:     0,
			EstrusWarningLevelItems: estrusWarningLevelItems,
		},
	}, nil

}

func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string) (*pasturePb.IndexFocusIndicatorsResponse, error) {
	userModel, err := s.GetUserModel(ctx)
	if err != nil {
		return nil, xerr.WithStack(err)
	}

	userFocusIndicators := userModel.SystemUser.IndicatorsKinds
	if len(userFocusIndicators) <= 0 {
		userFocusIndicators = model.DefaultFocusIndicators
	}
	userFocusIndicatorsList := strings.Split(userFocusIndicators, ",")
	indicatorsDataList := make([]*model.IndicatorsData, 0)
	pref := s.DB.Model(new(model.IndicatorsData)).
		Where("pasture_id = ?", userModel.AppPasture.Id).
		Where("kind in (?)", userFocusIndicatorsList)

	/*if dimension == "Year" {
		pref.Where("date = ?", time.Now().Format(model.LayoutMonth))
	}*/

	nowTime := time.Now()
	if dimension == "Month" {
		pref.Where("date = ?", nowTime.Format(model.LayoutMonth))
	}

	if err = pref.Find(&indicatorsDataList).Error; err != nil {
		zaplog.Error("FocusIndicators", zap.Any("err", err))
	}

	indicatorsDetailsMap, err := s.GetIndicatorsDetailsMap(ctx)
	if err != nil {
		return nil, xerr.WithStack(err)
	}
	focusIndicatorsList := make([]*pasturePb.FocusIndicators, 0)
	for _, v := range indicatorsDataList {
		indicatorsDetails, ok := indicatorsDetailsMap[v.Kind]
		if !ok {
			continue
		}
		onYear, onMonth := "", ""
		if dimension == "Year" {
			return nil, xerr.Custom("暂不支持该维度")
		}

		if dimension == "Month" {
			lastMonth := nowTime.AddDate(0, -1, 0).Format(model.LayoutMonth)
			oldIndicators, _ := s.GetIndicatorsDataByDate(userModel.AppPasture.Id, v.Kind, lastMonth)
			if oldIndicators != nil {
				if oldIndicators.Value != "" && oldIndicators.Value != "0" {
					oldValue, _ := strconv.ParseFloat(oldIndicators.Value, 64)
					currValue, _ := strconv.ParseFloat(v.Value, 64)
					onMonthValue := (oldValue - currValue) / oldValue * 100
					omv := util.RoundToTwoDecimals(onMonthValue)
					onMonth = strconv.FormatFloat(omv, 'f', 2, 64) + "%"
				}
			}
		}
		focusIndicatorsList = append(focusIndicatorsList, &pasturePb.FocusIndicators{
			Kind:     indicatorsDetails.Kind,
			Name:     indicatorsDetails.Name,
			Value:    v.Value,
			Describe: indicatorsDetails.Zh,
			UnitName: indicatorsDetails.Unit,
			OnMonth:  onMonth,
			OnYear:   onYear,
		})
	}

	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList(ctx)

	return &pasturePb.IndexFocusIndicatorsResponse{
		Code: http.StatusOK,
		Msg:  "ok",
		Data: &pasturePb.FocusData{
			FocusIndicators: focusIndicatorsList,
			IndicatorsSet:   model.IndicatorsDetailsSlice(indicatorsDetailsList).ToPB(userFocusIndicatorsList),
		},
	}, err
}

func (s *StoreEntry) FocusIndicatorsSet(ctx context.Context, req *pasturePb.IndexFocusIndicatorsSetRequest) error {
	userModel, err := s.GetUserModel(ctx)
	if err != nil {
		return xerr.WithStack(err)
	}

	if len(req.IndicatorsKind) <= 0 {
		return nil
	}

	userFocusIndicators := strings.Join(req.IndicatorsKind, ",")
	if err = s.DB.Model(new(model.SystemUser)).
		Where("id = ?", userModel.SystemUser.Id).
		Update("indicators_kinds", userFocusIndicators).Error; err != nil {
		return xerr.WithStack(err)
	}
	return nil
}

func (s *StoreEntry) DataWarningSet(ctx context.Context, req *pasturePb.IndexDataWarningSetRequest) error {
	userModel, err := s.GetUserModel(ctx)
	if err != nil {
		return xerr.WithStack(err)
	}

	if len(req.WarningDataSet) <= 0 {
		return nil
	}

	defaultDataWarning, _ := s.FindDataWarning(ctx, model.DefaultUserId)
	if len(defaultDataWarning) <= 0 {
		return xerr.Custom("默认预警数据不存在,请联系管理员!")
	}

	userDataWarningList, err := s.FindDataWarning(ctx, userModel.SystemUser.Id)
	if err != nil {
		return xerr.WithStack(err)
	}

	if len(userDataWarningList) <= 0 { // 新增
		return s.addUserDataWarning(ctx, userModel.SystemUser.Id, defaultDataWarning, req.WarningDataSet)
	}
	return s.updateUserDataWarning(ctx, userModel.SystemUser.Id, userDataWarningList, req.WarningDataSet)
}

func (s *StoreEntry) DataWarningList(ctx context.Context) (*pasturePb.IndexDataWarningResponse, error) {
	userModel, err := s.GetUserModel(ctx)
	if err != nil {
		return nil, xerr.WithStack(err)
	}

	defaultDataWarning, _ := s.FindDataWarning(ctx, model.DefaultUserId)
	if len(defaultDataWarning) <= 0 {
		return nil, xerr.Custom("默认预警数据有误,请联系管理员!")
	}

	var isExist bool // 判断是否存在自己的设置的数据
	userDataWarning, _ := s.FindDataWarning(ctx, userModel.SystemUser.Id)
	if len(userDataWarning) == 0 {
		// 如果用户没有配置自己的预警数据,则使用默认数据
		isExist = true
		userDataWarning = defaultDataWarning
	}

	newTime := time.Now().Unix()
	needUpdateWarningIds := make([]int64, 0)
	for _, warningData := range userDataWarning {
		// 如果预警数据更新时间大于预警条件更新时间,并且更新时间距离当前时间小于2小时,则跳过
		if warningData.DataUpdateAt > warningData.ConditionUpdateAt && newTime-warningData.DataUpdateAt < int64(2*time.Hour) {
			continue
		}
		needUpdateWarningIds = append(needUpdateWarningIds, warningData.Id)
	}

	// 需要重新计算更新的warningId
	if len(needUpdateWarningIds) > 0 {
		s.UpdateWarningData(ctx, needUpdateWarningIds)
	}

	userDataWarningItems := make([]*model.DataWarningItems, 0)
	// 计算过后重新获取数据
	if isExist {
		userDataWarning, _ = s.FindDataWarning(ctx, model.DefaultUserId)
		userDataWarningItems, _ = s.FindDataWarningItems(ctx, model.DefaultUserId)
	} else {
		userDataWarning, _ = s.FindDataWarning(ctx, userModel.SystemUser.Id)
		userDataWarningItems, _ = s.FindDataWarningItems(ctx, userModel.SystemUser.Id)
	}

	return &pasturePb.IndexDataWarningResponse{
		Code: http.StatusOK,
		Msg:  "ok",
		Data: &pasturePb.DataWarning{
			DataSet:  model.DataWarningItemsSlice(userDataWarningItems).ToPB(userDataWarning),
			DataShow: model.DataWarningSlice(userDataWarning).ToPB(),
		},
	}, nil
}

// 新增用户预警数据
func (s *StoreEntry) addUserDataWarning(ctx context.Context, userId int64, defaultDataWarning []*model.DataWarning, warningDataSet []*pasturePb.WarningDataSet) error {
	// 将默认预警数据按 Kind 映射
	defaultDataWarningMap := make(map[string]*model.DataWarning)
	for _, v := range defaultDataWarning {
		defaultDataWarningMap[v.Kind] = v
	}

	// 在事务中执行新增操作
	return s.DB.Transaction(func(tx *gorm.DB) error {
		addedKinds := make(map[string]bool) // 记录已添加的 Kind
		for _, set := range warningDataSet {
			dataWarning := model.NewDataWarning(userId, set.Kind, pasturePb.IsShow_Ok, defaultDataWarningMap[set.Kind])
			// 如果该 Kind 已添加,跳过
			if !addedKinds[set.Kind] {
				// 创建新的预警数据
				if err := tx.Create(dataWarning).Error; err != nil {
					return xerr.WithStack(err)
				}
			} else {
				oldDataWarning := &model.DataWarning{}
				if err := tx.Model(new(model.DataWarning)).
					Where("user_id = ?", userId).
					Where("kind = ?", set.Kind).
					First(oldDataWarning).Error; err != nil {
					return xerr.WithStack(err)
				}
				dataWarning.Id = oldDataWarning.Id
			}

			// 创建预警项数据
			if err := tx.Create(model.NewDataWarningItems(userId, dataWarning, set)).Error; err != nil {
				return xerr.WithStack(err)
			}
			addedKinds[set.Kind] = true
		}

		return nil
	})
}

// 更新用户预警数据
func (s *StoreEntry) updateUserDataWarning(ctx context.Context, userId int64, userDataWarningList []*model.DataWarning, warningDataSet []*pasturePb.WarningDataSet) error {
	// 将请求数据按 WarningId 和 Id 映射
	warningIsShowMap := make(map[int32]pasturePb.IsShow_Kind)
	warningItemDataMap := make(map[int32]*pasturePb.WarningDataSet)
	for _, set := range warningDataSet {
		warningIsShowMap[set.WarningId] = set.IsShow
		warningItemDataMap[set.Id] = set
	}

	// 获取用户预警项数据
	userDataWarningItems, err := s.FindDataWarningItems(ctx, userId)
	if err != nil {
		return xerr.WithStack(err)
	}
	if len(userDataWarningItems) == 0 {
		return xerr.Custom("预警数据有误,请联系管理员!")
	}

	// 在事务中执行更新操作
	return s.DB.Transaction(func(tx *gorm.DB) error {
		// 更新预警数据的 IsShow 字段
		for _, warning := range userDataWarningList {
			if isShow, ok := warningIsShowMap[int32(warning.Id)]; ok {
				if err = tx.Model(&model.DataWarning{}).
					Where("id = ?", warning.Id).
					Update("is_show", isShow).Error; err != nil {
					return xerr.WithStack(err)
				}
			}
		}

		// 更新预警项数据的 IsShow 和 Value 字段
		for _, item := range userDataWarningItems {
			if set, ok := warningItemDataMap[int32(item.Id)]; ok {
				if err = tx.Model(&model.DataWarningItems{}).
					Where("id = ?", item.Id).
					Updates(map[string]interface{}{
						"is_show": set.IsShow,
						"value":   set.Value,
					}).Error; err != nil {
					return xerr.WithStack(err)
				}
			}
		}

		return nil
	})
}

func (s *StoreEntry) FindDataWarning(ctx context.Context, userId int64) ([]*model.DataWarning, error) {
	dataWarningList := make([]*model.DataWarning, 0)
	if err := s.DB.Model(new(model.DataWarning)).
		Where("user_id = ?", userId).
		Find(&dataWarningList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}
	return dataWarningList, nil
}

func (s *StoreEntry) FindDataWarningItems(ctx context.Context, userId int64) ([]*model.DataWarningItems, error) {
	dataWarningItemsList := make([]*model.DataWarningItems, 0)
	if err := s.DB.Model(new(model.DataWarningItems)).
		Where("user_id = ?", userId).
		Find(&dataWarningItemsList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}
	return dataWarningItemsList, nil
}

func (s *StoreEntry) FindDataWarningMap(ctx context.Context, userId int64) (map[int64]*model.DataWarning, error) {
	dataWarning, err := s.FindDataWarning(ctx, userId)
	if err != nil {
		return nil, xerr.Custom("默认预警数据有误,请联系管理员!")
	}
	dataWarningMap := make(map[int64]*model.DataWarning)
	for _, v := range dataWarning {
		dataWarningMap[v.Id] = v
	}
	return dataWarningMap, nil
}

func (s *StoreEntry) FindDataWarningItemsMap(ctx context.Context, userId int64) (map[int64]*model.DataWarningItems, error) {
	dataWarningItemsList := make([]*model.DataWarningItems, 0)
	if err := s.DB.Model(new(model.DataWarningItems)).
		Where("user_id = ?", userId).
		Find(&dataWarningItemsList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}
	dataWarningItemsMap := make(map[int64]*model.DataWarningItems)
	for _, v := range dataWarningItemsList {
		dataWarningItemsMap[v.Id] = v
	}
	return dataWarningItemsMap, nil
}

// UpdateWarningData 更新计算数据
func (s *StoreEntry) UpdateWarningData(ctx context.Context, needUpdateWarningIds []int64) {
	if len(needUpdateWarningIds) <= 0 {
		return
	}
	for _, warningId := range needUpdateWarningIds {
		query, params, err := s.BuildQuery(warningId)
		if err != nil {
			zaplog.Error("UpdateWarningData", zap.Any("BuildQuery", err), zap.Any("warningId", warningId))
		}
		if len(query) == 0 || len(params) == 0 {
			continue
		}
		var count int64
		if err = s.DB.Model(new(model.Cow)).Where(query, params...).Count(&count).Error; err != nil {
			zaplog.Error("UpdateWarningData", zap.Any("err", err), zap.Any("query", query), zap.Any("params", params))
		}

		if err = s.DB.Model(new(model.DataWarning)).
			Where("id = ?", warningId).
			Updates(map[string]interface{}{
				"data_value":     count,
				"data_update_at": time.Now().Unix(),
			}).Error; err != nil {
			zaplog.Error("UpdateWarningData", zap.Any("update", err))
		}
	}
}