package invite

import (
	"context"

	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
	"gorm.io/gorm"
)

type GetInviteManageListLogic struct {
	logger.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetInviteManageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInviteManageListLogic {
	return &GetInviteManageListLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetInviteManageListLogic) GetInviteManageList(req *types.GetInviteManageListRequest) (resp *types.GetInviteManageListResponse, err error) {
	req.Page, req.Size = NormalizePage(req.Page, req.Size)

	type inviteRow struct {
		InviteeId     int64  `gorm:"column:invitee_id"`
		InviteeAvatar string `gorm:"column:invitee_avatar"`
		InviteeEnable bool   `gorm:"column:invitee_enable"`
		InvitedAt     int64  `gorm:"column:invited_at"`
		InviterId     int64  `gorm:"column:inviter_id"`
	}

	baseQuery := applyInviteManageFilters(l.svcCtx.DB.WithContext(l.ctx).
		Table("user invitee").
		Where("invitee.referer_id > 0 AND invitee.deleted_at IS NULL"), req)

	var total int64
	if err = baseQuery.Count(&total).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count invite manage records failed: %v", err)
	}

	var rows []inviteRow
	if err = applyInviteManageFilters(l.svcCtx.DB.WithContext(l.ctx).
		Table("user invitee").
		Select("invitee.id as invitee_id, invitee.avatar as invitee_avatar, invitee.enable as invitee_enable, CAST(UNIX_TIMESTAMP(invitee.created_at) AS SIGNED) as invited_at, invitee.referer_id as inviter_id").
		Where("invitee.referer_id > 0 AND invitee.deleted_at IS NULL"), req).
		Order("invitee.created_at DESC").
		Limit(req.Size).
		Offset((req.Page - 1) * req.Size).
		Scan(&rows).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invite manage records failed: %v", err)
	}

	relations := make([]InviteRelation, 0, len(rows))
	userIds := make([]int64, 0, len(rows)*2)
	for _, row := range rows {
		relations = append(relations, InviteRelation{InviteeId: row.InviteeId, InviterId: row.InviterId})
		userIds = append(userIds, row.InviteeId, row.InviterId)
	}

	benefits, err := QueryBenefits(l.ctx, l.svcCtx.DB, relations)
	if err != nil {
		return nil, err
	}
	devices, err := QueryDeviceIdentifiers(l.ctx, l.svcCtx.DB, userIds)
	if err != nil {
		return nil, err
	}

	list := make([]types.InviteManageRecord, 0, len(rows))
	for _, row := range rows {
		benefit := benefits[row.InviteeId]
		list = append(list, types.InviteManageRecord{
			InviterId:         row.InviterId,
			InviterIdentifier: devices[row.InviterId].Identifier,
			InviterDeviceNo:   devices[row.InviterId].DeviceNo,
			InviteeId:         row.InviteeId,
			InviteeIdentifier: devices[row.InviteeId].Identifier,
			InviteeDeviceNo:   devices[row.InviteeId].DeviceNo,
			InviteeAvatar:     row.InviteeAvatar,
			InviteeEnable:     row.InviteeEnable,
			InvitedAt:         row.InvitedAt,
			OrderCount:        benefit.OrderCount,
			HasPurchased:      benefit.HasPurchased,
			InviterCommission: benefit.InviterCommission,
			InviterGiftDays:   benefit.InviterGiftDays,
			InviteeGiftDays:   benefit.InviteeGiftDays,
		})
	}

	return &types.GetInviteManageListResponse{
		Total: total,
		List:  list,
	}, nil
}

func applyInviteManageFilters(db *gorm.DB, req *types.GetInviteManageListRequest) *gorm.DB {
	if req.InviterId > 0 {
		db = db.Where("invitee.referer_id = ?", req.InviterId)
	}
	if req.InviteeId > 0 {
		db = db.Where("invitee.id = ?", req.InviteeId)
	}
	if req.Search != "" {
		search := "%" + req.Search + "%"
		db = db.Where(
			"(EXISTS (SELECT 1 FROM user_auth_methods inviter_auth WHERE inviter_auth.user_id = invitee.referer_id AND inviter_auth.auth_identifier LIKE ?) OR EXISTS (SELECT 1 FROM user_auth_methods invitee_auth WHERE invitee_auth.user_id = invitee.id AND invitee_auth.auth_identifier LIKE ?))",
			search,
			search,
		)
	}
	return db
}
