package invite

import (
	"context"

	modellog "github.com/perfect-panel/server/internal/model/log"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
	"gorm.io/gorm"
)

type Benefits struct {
	OrderCount        int64
	HasPurchased      bool
	InviterCommission int64
	InviterGiftDays   int64
	InviteeGiftDays   int64
}

type InviteRelation struct {
	InviteeId int64
	InviterId int64
}

type paidOrderRow struct {
	UserId             int64  `gorm:"column:user_id"`
	SubscriptionUserId int64  `gorm:"column:subscription_user_id"`
	OrderNo            string `gorm:"column:order_no"`
}

type systemLogRow struct {
	ObjectID int64  `gorm:"column:object_id"`
	Content  string `gorm:"column:content"`
}

func NormalizePage(page, size int) (int, int) {
	if page < 1 {
		page = 1
	}
	if size < 1 {
		size = 10
	}
	if size > 100 {
		size = 100
	}
	return page, size
}

func QueryBenefits(ctx context.Context, db *gorm.DB, relations []InviteRelation) (map[int64]Benefits, error) {
	result := make(map[int64]Benefits, len(relations))
	if len(relations) == 0 {
		return result, nil
	}

	inviteeIds := make([]int64, 0, len(relations))
	inviteeToInviter := make(map[int64]int64, len(relations))
	for _, relation := range relations {
		inviteeIds = append(inviteeIds, relation.InviteeId)
		inviteeToInviter[relation.InviteeId] = relation.InviterId
		result[relation.InviteeId] = Benefits{}
	}

	var orderCounts []struct {
		UserId int64 `gorm:"column:user_id"`
		Cnt    int64 `gorm:"column:cnt"`
	}
	if err := db.WithContext(ctx).
		Table("`order`").
		Select("user_id, COUNT(*) as cnt").
		Where("user_id IN ? AND status IN ?", inviteeIds, []int{2, 5}).
		Group("user_id").
		Scan(&orderCounts).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count invitee paid orders failed: %v", err)
	}
	for _, row := range orderCounts {
		benefit := result[row.UserId]
		benefit.OrderCount = row.Cnt
		benefit.HasPurchased = row.Cnt > 0
		result[row.UserId] = benefit
	}

	var paidOrders []paidOrderRow
	if err := db.WithContext(ctx).
		Table("`order`").
		Select("user_id, subscription_user_id, order_no").
		Where("user_id IN ? AND status IN ?", inviteeIds, []int{2, 5}).
		Scan(&paidOrders).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invitee paid orders failed: %v", err)
	}
	if len(paidOrders) == 0 {
		return result, nil
	}

	orderNos := make([]string, 0, len(paidOrders))
	orderToInvitee := make(map[string]int64, len(paidOrders))
	orderToSubscriptionUser := make(map[string]int64, len(paidOrders))
	inviterIds := make([]int64, 0, len(relations))
	inviteeAndInviterSet := make(map[int64]struct{}, len(relations)*2)
	for _, order := range paidOrders {
		orderNos = append(orderNos, order.OrderNo)
		orderToInvitee[order.OrderNo] = order.UserId
		if order.SubscriptionUserId > 0 && order.SubscriptionUserId != order.UserId {
			orderToSubscriptionUser[order.OrderNo] = order.SubscriptionUserId
			inviteeAndInviterSet[order.SubscriptionUserId] = struct{}{}
		}
	}
	for _, relation := range relations {
		inviterIds = append(inviterIds, relation.InviterId)
		inviteeAndInviterSet[relation.InviteeId] = struct{}{}
		inviteeAndInviterSet[relation.InviterId] = struct{}{}
	}
	inviteeAndInviterIds := make([]int64, 0, len(inviteeAndInviterSet))
	for userId := range inviteeAndInviterSet {
		inviteeAndInviterIds = append(inviteeAndInviterIds, userId)
	}

	if err := fillCommissionBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderNos, inviterIds); err != nil {
		return nil, err
	}
	if err := fillGiftBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderToSubscriptionUser, orderNos, inviteeAndInviterIds); err != nil {
		return nil, err
	}

	return result, nil
}

func fillCommissionBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderNos []string, inviterIds []int64) error {
	var rows []systemLogRow
	if err := db.WithContext(ctx).
		Table("system_logs").
		Select("object_id, content").
		Where("type = ? AND object_id IN ? AND JSON_UNQUOTE(JSON_EXTRACT(content, '$.order_no')) IN ? AND JSON_EXTRACT(content, '$.type') IN ?", modellog.TypeCommission.Uint8(), inviterIds, orderNos, []int{int(modellog.CommissionTypePurchase), int(modellog.CommissionTypeRenewal)}).
		Scan(&rows).Error; err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invite commission logs failed: %v", err)
	}
	for _, row := range rows {
		content := modellog.Commission{}
		if err := content.Unmarshal([]byte(row.Content)); err != nil {
			continue
		}
		inviteeId, ok := orderToInvitee[content.OrderNo]
		if !ok || inviteeToInviter[inviteeId] != row.ObjectID {
			continue
		}
		benefit := benefits[inviteeId]
		benefit.InviterCommission += content.Amount
		benefits[inviteeId] = benefit
	}
	return nil
}

func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderToSubscriptionUser map[string]int64, orderNos []string, userIds []int64) error {
	var rows []systemLogRow
	if err := db.WithContext(ctx).
		Table("system_logs").
		Select("object_id, content").
		Where("type = ? AND object_id IN ? AND JSON_UNQUOTE(JSON_EXTRACT(content, '$.order_no')) IN ?", modellog.TypeGift.Uint8(), userIds, orderNos).
		Scan(&rows).Error; err != nil {
		return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invite gift logs failed: %v", err)
	}
	for _, row := range rows {
		content := modellog.Gift{}
		if err := content.Unmarshal([]byte(row.Content)); err != nil {
			continue
		}
		if content.Type != modellog.GiftTypeIncrease {
			continue
		}
		inviteeId, ok := orderToInvitee[content.OrderNo]
		if !ok {
			continue
		}
		benefit := benefits[inviteeId]
		switch row.ObjectID {
		case inviteeToInviter[inviteeId]:
			benefit.InviterGiftDays += content.Amount
		case inviteeId:
			benefit.InviteeGiftDays += content.Amount
		case orderToSubscriptionUser[content.OrderNo]:
			benefit.InviteeGiftDays += content.Amount
		default:
			continue
		}
		benefits[inviteeId] = benefit
	}
	return nil
}

func QueryIdentifiers(ctx context.Context, db *gorm.DB, userIds []int64) (map[int64]string, error) {
	identifiers := make(map[int64]string, len(userIds))
	if len(userIds) == 0 {
		return identifiers, nil
	}

	type identifierRow struct {
		UserId     int64  `gorm:"column:user_id"`
		Identifier string `gorm:"column:identifier"`
	}
	var rows []identifierRow
	if err := db.WithContext(ctx).
		Table("user_auth_methods uam").
		Select("uam.user_id, uam.auth_identifier as identifier").
		Joins("JOIN (SELECT user_id, MIN(id) AS id FROM user_auth_methods WHERE user_id IN ? GROUP BY user_id) first_uam ON first_uam.id = uam.id", userIds).
		Scan(&rows).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user identifiers failed: %v", err)
	}
	for _, row := range rows {
		identifiers[row.UserId] = row.Identifier
	}
	return identifiers, nil
}
