package backend

import (
	"context"
	"errors"
	"fmt"
	"kpt-pasture/model"
	"net/http"
	"strings"
	"time"

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

	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
	"gitee.com/xuyiping_admin/pkg/xerr"
	"gorm.io/gorm"
)

func (s *StoreEntry) CalvingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchLavingEventResponse, error) {
	lavingList := make([]*model.EventCalvingList, 0)
	var count int64 = 0
	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCalving).TableName())).
		Select(`a.*,b.name as pen_name,c.name as staff_member_name`).
		Joins(fmt.Sprintf("JOIN %s AS b on a.pen_id = b.id", new(model.Pen).TableName())).
		Joins(fmt.Sprintf("JOIN %s AS c on a.staff_member_id = c.id", new(model.SystemUser).TableName()))
	if len(req.CowId) > 0 {
		cowIds := strings.Split(req.CowId, ",")
		pref.Where("a.cow_id IN ?", cowIds)
	}

	if err := pref.Order("a.id desc").
		Count(&count).Limit(int(pagination.PageSize)).
		Offset(int(pagination.PageOffset)).
		Find(&lavingList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	calvingIds := make([]int64, 0)
	for _, v := range lavingList {
		calvingIds = append(calvingIds, v.Id)
	}
	calvingCalfList := make([]*model.CalvingCalf, 0)
	if err := s.DB.Model(new(model.CalvingCalf)).Where("calving_id IN ?", calvingIds).Find(&calvingCalfList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	return &pasturePb.SearchLavingEventResponse{
		Code:    http.StatusOK,
		Message: "ok",
		Data: &pasturePb.SearchLavingData{
			List:     model.EventCalvingListSlice(lavingList).ToPB(calvingCalfList),
			Total:    int32(count),
			PageSize: pagination.PageSize,
			Page:     pagination.Page,
		},
	}, nil
}

func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalving) error {
	if len(req.CowId) <= 0 {
		return xerr.Custom("请选择相关牛只")
	}

	cowList, err := s.ParseCowIds(ctx, req.CowId)
	if err != nil {
		return xerr.WithStack(err)
	}

	if len(cowList) <= 0 {
		return xerr.Custom("请选择相关牛只")
	}

	cow := cowList[0]

	if err = s.DB.Transaction(func(tx *gorm.DB) error {
		// 母牛信息
		newCalving := model.NewEventCalving(cow, req)
		if err = tx.Create(newCalving).Error; err != nil {
			return xerr.WithStack(err)
		}
		// 犊牛信息
		newCalvingCalfList := model.NewEventCalvingCalf(cow.Id, newCalving.Id, req)
		for _, v := range newCalvingCalfList {
			if v.IsLive == pasturePb.IsShow_No || v.IsAdoption == pasturePb.IsShow_No {
				continue
			}
			// 留养犊牛
			newCow := model.NewCalfCow(cow.Id, cow.LastBullId, v)
			if err = tx.Create(newCow).Error; err != nil {
				return xerr.WithStack(err)
			}
			v.CowId = newCow.Id
		}
		if err = tx.Create(newCalvingCalfList).Error; err != nil {
			return xerr.WithStack(err)
		}

		if err = tx.Model(new(model.Cow)).Where("id = ?", cow).Updates(map[string]interface{}{
			"calving_age":     0,
			"lact":            cow.Lact + 1,
			"breed_status":    pasturePb.BreedStatus_Calving,
			"is_pregnant":     pasturePb.IsShow_No,
			"calving_at":      time.Now().Unix(),
			"last_calving_at": time.Now().Unix(),
		}).Error; err != nil {
			return xerr.WithStack(err)
		}

		return nil
	}); err != nil {
		return xerr.WithStack(err)
	}
	return nil
}

func (s *StoreEntry) PregnantCheckList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.PregnantCheckEventResponse, error) {
	pregnantCheckList := make([]*model.EventPregnantCheck, 0)
	var count int64 = 0
	pref := s.DB.Table(new(model.EventPregnantCheck).TableName())
	if len(req.CowId) > 0 {
		cowIds := strings.Split(req.CowId, ",")
		pref.Where("cow_id IN ?", cowIds)
	}

	if err := pref.Order("id desc").
		Count(&count).Limit(int(pagination.PageSize)).
		Offset(int(pagination.PageOffset)).
		Find(&pregnantCheckList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	systemUserList, _ := s.SystemUserList(ctx)
	pregnantCheckResult := s.PregnantCheckResultEnumList()
	pregnantCheckMethod := s.PregnantCheckMethodEnumList()

	return &pasturePb.PregnantCheckEventResponse{
		Code:    http.StatusOK,
		Message: "ok",
		Data: &pasturePb.SearchPregnantCheckData{
			List:     model.EventPregnantCheckSlice(pregnantCheckList).ToPB(systemUserList, pregnantCheckResult, pregnantCheckMethod),
			Total:    int32(count),
			PageSize: pagination.PageSize,
			Page:     pagination.Page,
		},
	}, nil
}

func (s *StoreEntry) PregnantCheckCreate(ctx context.Context, req *pasturePb.EventPregnantCheck) error {
	if len(req.CowId) <= 0 {
		return xerr.Custom("请选择相关牛只")
	}
	cowList, err := s.ParseCowIds(ctx, req.CowId)
	if err != nil {
		return xerr.WithStack(err)
	}
	eventPregnantCheckList := make([]*model.EventPregnantCheck, 0)
	currentUser, _ := s.GetCurrentSystemUser(ctx)

	for _, cow := range cowList {
		eventPregnantCheckList = append(eventPregnantCheckList, model.NewEventPregnantCheck(cow, currentUser, req))
	}

	if err := s.DB.Create(eventPregnantCheckList).Error; err != nil {
		return xerr.WithStack(err)
	}
	return nil
}

func (s *StoreEntry) MatingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.MatingEventResponse, error) {
	matingList := make([]*model.EventMating, 0)
	var count int64 = 0
	pref := s.DB.Table(new(model.EventMating).TableName())
	if len(req.CowId) > 0 {
		cowIds := strings.Split(req.CowId, ",")
		pref.Where("cow_id IN ?", cowIds)
	}

	if err := pref.Order("id desc").
		Count(&count).Limit(int(pagination.PageSize)).
		Offset(int(pagination.PageOffset)).
		Find(&matingList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	systemUserList, _ := s.SystemUserList(ctx)
	exposeEstrusTypeMap := s.ExposeEstrusTypeMap()
	return &pasturePb.MatingEventResponse{
		Code:    http.StatusOK,
		Message: "ok",
		Data: &pasturePb.SearchMatingData{
			List:     model.EventMatingSlice(matingList).ToPB(systemUserList, exposeEstrusTypeMap),
			Total:    int32(count),
			PageSize: pagination.PageSize,
			Page:     pagination.Page,
		},
	}, nil
}

func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMating) error {
	if len(req.CowId) <= 0 {
		return xerr.Custom("请选择相关牛只")
	}
	cowList, err := s.ParseCowIds(ctx, req.CowId)
	if err != nil {
		return xerr.WithStack(err)
	}
	matingList := make([]*model.EventMating, 0)
	eventFrozenSemenLogList := make([]*model.EventFrozenSemenLog, 0)
	currentUser, _ := s.GetCurrentSystemUser(ctx)

	frozenSemen := &model.EventFrozenSemen{}
	if err = s.DB.Where("bull_id = ?", req.BullId).First(frozenSemen).Error; err != nil {
		return xerr.WithStack(err)
	}

	if err = s.DB.Transaction(func(tx *gorm.DB) error {
		if err = tx.Create(matingList).Error; err != nil {
			return xerr.WithStack(err)
		}

		for _, cow := range cowList {
			matingList = append(matingList, model.NewEventMating(cow, currentUser, req))
			eventFrozenSemenLogList = append(eventFrozenSemenLogList, model.NewEventFrozenSemenLog(req.BullId, cow, int64(req.StaffMemberId)))
			if err = tx.Model(&model.SameTimeCow{}).Where("lact = ?", cow.Lact).Where("cow_id = ?", cow.Id).Updates(map[string]interface{}{
				"status": pasturePb.IsShow_No,
				"end_at": time.Now().Unix(),
			}).Error; err != nil {
				return xerr.WithStack(err)
			}
		}

		// 创建冻精使用记录日志
		if err = tx.Create(eventFrozenSemenLogList).Error; err != nil {
			return xerr.WithStack(err)
		}

		if err = tx.Table(new(model.EventFrozenSemen).TableName()).
			Where("bull_id = ?", req.BullId).
			Where("quantity > 0").UpdateColumn("quantity", gorm.Expr("quantity - ?", len(matingList))).Error; err != nil {
			return xerr.WithStack(err)
		}

		return nil
	}); err != nil {
		return xerr.WithStack(err)
	}

	return nil
}

func (s *StoreEntry) EstrusList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EstrusEventResponse, error) {
	estrusList := make([]*model.EventEstrus, 0)
	var count int64 = 0
	pref := s.DB.Table(new(model.EventEstrus).TableName())
	if len(req.CowId) > 0 {
		cowIds := strings.Split(req.CowId, ",")
		pref.Where("cow_id IN ?", cowIds)
	}

	if err := pref.Order("id desc").
		Count(&count).Limit(int(pagination.PageSize)).
		Offset(int(pagination.PageOffset)).
		Find(&estrusList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	systemUserList, _ := s.SystemUserList(ctx)
	return &pasturePb.EstrusEventResponse{
		Code:    http.StatusOK,
		Message: "ok",
		Data: &pasturePb.SearchEstrusData{
			List:     model.EstrusSlice(estrusList).ToPB(systemUserList),
			Total:    int32(count),
			PageSize: pagination.PageSize,
			Page:     pagination.Page,
		},
	}, nil
}

func (s *StoreEntry) EstrusCreate(ctx context.Context, req *pasturePb.EventEstrus) error {
	if len(req.CowId) <= 0 {
		return xerr.Custom("请选择相关牛只")
	}
	cowList, err := s.ParseCowIds(ctx, req.CowId)
	if err != nil {
		return xerr.WithStack(err)
	}
	estrusList := make([]*model.EventEstrus, 0)
	currentUser, _ := s.GetCurrentSystemUser(ctx)

	for _, cow := range cowList {
		estrusList = append(estrusList, model.NewEventEstrus(cow, currentUser, req))
	}

	if err := s.DB.Create(estrusList).Error; err != nil {
		return xerr.WithStack(err)
	}
	return nil
}

func (s *StoreEntry) FrozenSemenList(ctx context.Context, req *pasturePb.FrozenSemenRequest, pagination *pasturePb.PaginationModel) (*pasturePb.FrozenSemenResponse, error) {
	frozenSemenList := make([]*model.EventFrozenSemen, 0)
	var count int64 = 0
	pref := s.DB.Table(new(model.EventFrozenSemen).TableName())
	if req.BullId != "" {
		pref.Where("bull_id = ?", req.BullId)
	}

	if req.Producer != "" {
		pref.Where("producer = ?", req.Producer)
	}

	if err := pref.Order("id desc").
		Count(&count).Limit(int(pagination.PageSize)).
		Offset(int(pagination.PageOffset)).
		Find(&frozenSemenList).Error; err != nil {
		return nil, xerr.WithStack(err)
	}

	frozenSemenTypeMap := s.FrozenSemenTypeMap()
	unitMap := s.UnitMap()

	return &pasturePb.FrozenSemenResponse{
		Code:    http.StatusOK,
		Message: "ok",
		Data: &pasturePb.SearchFrozenSemenData{
			List:     model.EventFrozenSemenSlice(frozenSemenList).ToPB(frozenSemenTypeMap, unitMap),
			Total:    int32(count),
			PageSize: pagination.PageSize,
			Page:     pagination.Page,
		},
	}, nil
}

func (s *StoreEntry) FrozenSemenCreate(ctx context.Context, req *pasturePb.SearchFrozenSemenList) error {
	currentUser, _ := s.GetCurrentSystemUser(ctx)
	req.CowKindName = s.CowKindMap()[req.CowKind]
	newFrozenSemen := model.NewEventFrozenSemen(req, currentUser)
	if err := s.DB.Create(newFrozenSemen).Error; err != nil {
		return xerr.WithStack(err)
	}
	return nil
}

func (s *StoreEntry) SameTimeCreate(ctx context.Context, req *pasturePb.EventSameTime) error {
	sameTime, err := s.GetSameTimeById(ctx, int64(req.SameTimeId))
	if err != nil {
		return xerr.WithStack(err)
	}

	zaplog.Info("SameTimeCreate", zap.Any("sameTime", sameTime), zap.Any("req", req))
	cowList, err := s.ParseCowIds(ctx, req.CowId)
	if err != nil {
		return xerr.WithStack(err)
	}

	drugs := &model.Drugs{}
	if req.DrugsId > 0 {
		drugs, err = s.GetDrugsById(ctx, int64(req.DrugsId))
		if err != nil {
			zaplog.Error("SameTimeCreate", zap.Any("GetDrugsById", err), zap.Any("req", req))
		}
	}

	if err = s.DB.Transaction(func(tx *gorm.DB) error {
		eventSameTimeList := make([]*model.EventSameTime, 0)
		for _, cow := range cowList {
			eventSameTimeList = append(eventSameTimeList, model.NewEventSameTime(cow, req, drugs))
			// sameTimeCow 不存在时创建
			if err = tx.Where("same_time_id = ? ", sameTime.Id).
				Where("cow_id = ?", cow.Id).
				Where("lact = ?", cow.Lact).
				First(new(model.SameTimeCow)).Error; err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					if err = tx.Create(model.NewSameTimeCow(cow.Id, sameTime.Id)).Error; err != nil {
						return xerr.WithStack(err)
					}
				}
			}
		}
		if err = tx.Create(eventSameTimeList).Error; err != nil {
			return xerr.WithStack(err)
		}

		return nil
	}); err != nil {
		return xerr.WithStack(err)
	}

	return nil
}