package user

import (
	"context"
	"fmt"
	"time"

	modelOrder "github.com/perfect-panel/server/internal/model/order"
	"github.com/perfect-panel/server/internal/model/user"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/pkg/uuidx"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type familyJoinResult struct {
	FamilyId    int64
	OwnerUserId int64
}

type familyBindingHelper struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

type familySubscribeTransferMode uint8

const (
	familySubscribeDiscard familySubscribeTransferMode = iota
	familySubscribeMoveToOwner
)

func newFamilyBindingHelper(ctx context.Context, svcCtx *svc.ServiceContext) *familyBindingHelper {
	return &familyBindingHelper{
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (h *familyBindingHelper) getUserEmailMethod(userId int64) (*user.AuthMethods, error) {
	method, err := h.svcCtx.UserModel.FindUserAuthMethodByUserId(h.ctx, "email", userId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user email auth method failed")
	}
	return method, nil
}

func validateMemberJoinConflict(ownerFamilyId int64, memberRecord *user.UserFamilyMember) error {
	if memberRecord == nil {
		return nil
	}

	if ownerFamilyId != 0 && memberRecord.FamilyId == ownerFamilyId {
		if memberRecord.Status == user.FamilyMemberActive {
			return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already in this family")
		}
		return nil
	}

	if memberRecord.Status == user.FamilyMemberActive {
		return errors.Wrapf(xerr.NewErrCode(xerr.FamilyCrossBindForbidden), "user already belongs to another family")
	}

	return nil
}

func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64) error {
	if ownerUserId == memberUserId {
		return errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already bound to this family")
	}

	var ownerFamily user.UserFamily
	err := h.svcCtx.DB.WithContext(h.ctx).
		Unscoped().
		Model(&user.UserFamily{}).
		Where("owner_user_id = ?", ownerUserId).
		First(&ownerFamily).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner family failed")
	}

	var memberRecord user.UserFamilyMember
	memberExists := false
	err = h.svcCtx.DB.WithContext(h.ctx).
		Unscoped().
		Model(&user.UserFamilyMember{}).
		Where("user_id = ?", memberUserId).
		First(&memberRecord).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member family relation failed")
	}
	if err == nil {
		memberExists = true
	}
	if memberExists {
		if err = validateMemberJoinConflict(ownerFamily.Id, &memberRecord); err != nil {
			return err
		}
	}

	if ownerFamily.Id == 0 {
		return nil
	}

	var activeCount int64
	if err = h.svcCtx.DB.WithContext(h.ctx).
		Model(&user.UserFamilyMember{}).
		Where("family_id = ? AND status = ?", ownerFamily.Id, user.FamilyMemberActive).
		Count(&activeCount).Error; err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count family members failed")
	}
	if activeCount >= ownerFamily.MaxMembers {
		return errors.Wrapf(xerr.NewErrCode(xerr.FamilyMemberLimitExceeded), "family member limit exceeded")
	}
	return nil
}

func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source string) (*familyJoinResult, error) {
	return h.joinFamilyWithSubscribeMode(ownerUserId, memberUserId, source, familySubscribeDiscard)
}

func (h *familyBindingHelper) joinFamilyAndMoveSubscribesToOwner(ownerUserId, memberUserId int64, source string) (*familyJoinResult, error) {
	return h.joinFamilyWithSubscribeMode(ownerUserId, memberUserId, source, familySubscribeMoveToOwner)
}

func (h *familyBindingHelper) joinFamilyWithSubscribeMode(ownerUserId, memberUserId int64, source string, subscribeMode familySubscribeTransferMode) (*familyJoinResult, error) {
	if ownerUserId == memberUserId {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already bound to this family")
	}

	result := &familyJoinResult{
		OwnerUserId: ownerUserId,
	}
	affectedSubscribes := make([]user.Subscribe, 0)

	err := h.svcCtx.DB.WithContext(h.ctx).Transaction(func(tx *gorm.DB) error {
		ownerFamily, err := h.getOrCreateOwnerFamily(tx, ownerUserId)
		if err != nil {
			return err
		}
		result.FamilyId = ownerFamily.Id

		if err = h.ensureOwnerMember(tx, ownerFamily.Id, ownerUserId); err != nil {
			return err
		}

		var memberRecord user.UserFamilyMember
		err = tx.Unscoped().Model(&user.UserFamilyMember{}).Where("user_id = ?", memberUserId).First(&memberRecord).Error
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member family relation failed")
		}
		memberExists := err == nil

		if memberExists {
			if err = validateMemberJoinConflict(ownerFamily.Id, &memberRecord); err != nil {
				return err
			}
		}

		var activeCount int64
		if err = tx.Model(&user.UserFamilyMember{}).
			Where("family_id = ? AND status = ?", ownerFamily.Id, user.FamilyMemberActive).
			Count(&activeCount).Error; err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count family members failed")
		}
		if activeCount >= ownerFamily.MaxMembers {
			return errors.Wrapf(xerr.NewErrCode(xerr.FamilyMemberLimitExceeded), "family member limit exceeded")
		}

		now := time.Now()
		if !memberExists {
			memberRecord = user.UserFamilyMember{
				FamilyId:   ownerFamily.Id,
				UserId:     memberUserId,
				Role:       user.FamilyRoleMember,
				Status:     user.FamilyMemberActive,
				JoinSource: source,
				JoinedAt:   now,
			}
			if err = tx.Create(&memberRecord).Error; err != nil {
				return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create family member failed")
			}
		} else {
			if memberRecord.FamilyId != ownerFamily.Id {
				memberRecord.FamilyId = ownerFamily.Id
			}
			memberRecord.Status = user.FamilyMemberActive
			memberRecord.Role = user.FamilyRoleMember
			memberRecord.JoinSource = source
			memberRecord.JoinedAt = now
			memberRecord.LeftAt = nil
			memberRecord.DeletedAt = gorm.DeletedAt{}
			if err = tx.Unscoped().Save(&memberRecord).Error; err != nil {
				return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update family member failed")
			}
		}

		switch subscribeMode {
		case familySubscribeMoveToOwner:
			affectedSubscribes, err = moveMemberSubscribesToOwner(tx, memberUserId, ownerUserId)
			if err != nil {
				return err
			}
		default:
			affectedSubscribes, err = transferMemberSubscribesToOwner(tx, memberUserId, ownerUserId)
			if err != nil {
				return err
			}
		}
		return nil
	})

	if err != nil {
		return nil, err
	}

	if subscribeMode == familySubscribeMoveToOwner {
		if err = h.clearMovedMemberSubscribeCache(affectedSubscribes, ownerUserId); err != nil {
			return nil, err
		}
	} else {
		if err = h.clearRemovedMemberSubscribeCache(affectedSubscribes); err != nil {
			return nil, err
		}
	}
	return result, nil
}

func moveMemberSubscribesToOwner(tx *gorm.DB, memberUserId, ownerUserId int64) ([]user.Subscribe, error) {
	var subscribes []user.Subscribe
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Model(&user.Subscribe{}).
		Where("user_id = ?", memberUserId).
		Find(&subscribes).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member subscribe list failed")
	}
	if len(subscribes) == 0 {
		return nil, nil
	}

	for _, sub := range subscribes {
		newToken := uuidx.SubscribeToken(fmt.Sprintf("familyMove:%d:%d:%s", ownerUserId, sub.Id, uuidx.NewUUID().String()))
		if err := tx.Model(&user.Subscribe{}).
			Where("id = ? AND user_id = ?", sub.Id, memberUserId).
			Updates(map[string]interface{}{
				"user_id":    ownerUserId,
				"token":      newToken,
				"uuid":       uuidx.NewUUID().String(),
				"updated_at": time.Now(),
			}).Error; err != nil {
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "move member subscribe to owner failed")
		}
		if sub.OrderId > 0 {
			if err := tx.Model(&modelOrder.Order{}).
				Where("id = ? AND subscription_user_id = ?", sub.OrderId, memberUserId).
				Updates(map[string]interface{}{
					"subscription_user_id": ownerUserId,
					"subscribe_token":      newToken,
				}).Error; err != nil {
				return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "move member subscribe order to owner failed")
			}
		}
	}
	return subscribes, nil
}

func transferMemberSubscribesToOwner(tx *gorm.DB, memberUserId, ownerUserId int64) ([]user.Subscribe, error) {
	var subscribes []user.Subscribe
	if err := tx.Model(&user.Subscribe{}).
		Where("user_id = ?", memberUserId).
		Find(&subscribes).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member subscribe list failed")
	}
	if len(subscribes) == 0 {
		return nil, nil
	}
	// 加入家庭组时，无条件丢弃成员的所有订阅（软删除）
	if err := tx.Where("user_id = ?", memberUserId).Delete(&user.Subscribe{}).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "discard member subscribes failed")
	}
	return subscribes, nil
}

func (h *familyBindingHelper) clearRemovedMemberSubscribeCache(removedSubscribes []user.Subscribe) error {
	if len(removedSubscribes) == 0 {
		return nil
	}

	subscribeModels, subscribeIDSet := buildRemovedSubscribeCacheMeta(removedSubscribes)

	if err := h.svcCtx.UserModel.ClearSubscribeCache(h.ctx, subscribeModels...); err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear member subscribe cache failed")
	}
	for subscribeID := range subscribeIDSet {
		if err := h.svcCtx.SubscribeModel.ClearCache(h.ctx, subscribeID); err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear subscribe cache failed")
		}
	}
	if err := h.svcCtx.NodeModel.ClearServerAllCache(h.ctx); err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear node cache failed")
	}

	return nil
}

func (h *familyBindingHelper) clearMovedMemberSubscribeCache(movedSubscribes []user.Subscribe, ownerUserId int64) error {
	if len(movedSubscribes) == 0 {
		return nil
	}

	cacheModels := make([]*user.Subscribe, 0, len(movedSubscribes)*2)
	ownerCopies := make([]user.Subscribe, len(movedSubscribes))
	for i := range movedSubscribes {
		cacheModels = append(cacheModels, &movedSubscribes[i])
		ownerCopies[i] = movedSubscribes[i]
		ownerCopies[i].UserId = ownerUserId
		cacheModels = append(cacheModels, &ownerCopies[i])
	}

	if err := h.svcCtx.UserModel.ClearSubscribeCache(h.ctx, cacheModels...); err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear moved subscribe cache failed")
	}

	_, subscribeIDSet := buildRemovedSubscribeCacheMeta(movedSubscribes)
	for subscribeID := range subscribeIDSet {
		if err := h.svcCtx.SubscribeModel.ClearCache(h.ctx, subscribeID); err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear subscribe cache failed")
		}
	}
	if err := h.svcCtx.NodeModel.ClearServerAllCache(h.ctx); err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear node cache failed")
	}

	return nil
}

func buildRemovedSubscribeCacheMeta(removedSubscribes []user.Subscribe) ([]*user.Subscribe, map[int64]struct{}) {
	subscribeModels := make([]*user.Subscribe, 0, len(removedSubscribes))
	subscribeIDSet := make(map[int64]struct{}, len(removedSubscribes))
	for i := range removedSubscribes {
		subscribeModels = append(subscribeModels, &removedSubscribes[i])
		if removedSubscribes[i].SubscribeId > 0 {
			subscribeIDSet[removedSubscribes[i].SubscribeId] = struct{}{}
		}
	}
	return subscribeModels, subscribeIDSet
}

func (h *familyBindingHelper) getOrCreateOwnerFamily(tx *gorm.DB, ownerUserId int64) (*user.UserFamily, error) {
	var ownerFamily user.UserFamily
	err := tx.Unscoped().Clauses(clause.Locking{Strength: "UPDATE"}).
		Model(&user.UserFamily{}).
		Where("owner_user_id = ?", ownerUserId).
		First(&ownerFamily).Error
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner family failed")
		}
		ownerFamily = user.UserFamily{
			OwnerUserId: ownerUserId,
			MaxMembers:  user.DefaultFamilyMaxSize,
			Status:      user.FamilyStatusActive,
		}
		if err = tx.Create(&ownerFamily).Error; err != nil {
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create owner family failed")
		}
	} else if ownerFamily.Status != user.FamilyStatusActive || ownerFamily.DeletedAt.Valid {
		ownerFamily.Status = user.FamilyStatusActive
		ownerFamily.DeletedAt = gorm.DeletedAt{}
		if err = tx.Unscoped().Save(&ownerFamily).Error; err != nil {
			return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "activate owner family failed")
		}
	}
	return &ownerFamily, nil
}

func (h *familyBindingHelper) ensureOwnerMember(tx *gorm.DB, familyId, ownerUserId int64) error {
	var ownerMember user.UserFamilyMember
	err := tx.Unscoped().Model(&user.UserFamilyMember{}).Where("user_id = ?", ownerUserId).First(&ownerMember).Error
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner family member failed")
		}
		ownerMember = user.UserFamilyMember{
			FamilyId:   familyId,
			UserId:     ownerUserId,
			Role:       user.FamilyRoleOwner,
			Status:     user.FamilyMemberActive,
			JoinSource: "owner_init",
			JoinedAt:   time.Now(),
		}
		if err = tx.Create(&ownerMember).Error; err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create owner family member failed")
		}
		return nil
	}

	if ownerMember.FamilyId != familyId {
		return errors.Wrapf(xerr.NewErrCode(xerr.FamilyCrossBindForbidden), "owner already belongs to another family")
	}
	if ownerMember.Status != user.FamilyMemberActive || ownerMember.Role != user.FamilyRoleOwner || ownerMember.DeletedAt.Valid {
		ownerMember.Status = user.FamilyMemberActive
		ownerMember.Role = user.FamilyRoleOwner
		ownerMember.LeftAt = nil
		ownerMember.DeletedAt = gorm.DeletedAt{}
		if err = tx.Unscoped().Save(&ownerMember).Error; err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update owner family member failed")
		}
	}
	return nil
}
