package common

import (
	"context"
	"sort"
	"time"

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

const (
	NewUserEligibilitySourceDeviceCreatedAt       = "device_created_at"
	NewUserEligibilitySourceUserCreatedAtFallback = "user_created_at_fallback"
	NewUserEligibilityJoinSourceBindEmail         = "bind_email_with_verification"
	NewUserEligibilityWindow                      = 24 * time.Hour
)

type NewUserEligibilityContext struct {
	ScopeUserIDs       []int64
	EligibilityStartAt time.Time
	Source             string
}

func (c *NewUserEligibilityContext) IsNewUserAt(now time.Time) bool {
	if c == nil || c.EligibilityStartAt.IsZero() {
		return false
	}
	return now.Sub(c.EligibilityStartAt) <= NewUserEligibilityWindow
}

type newUserEligibilityRelation struct {
	FamilyID    int64  `gorm:"column:family_id"`
	Role        uint8  `gorm:"column:role"`
	JoinSource  string `gorm:"column:join_source"`
	OwnerUserID int64  `gorm:"column:owner_user_id"`
}

func ResolveNewUserEligibility(ctx context.Context, db *gorm.DB, currentUserID int64) (*NewUserEligibilityContext, error) {
	if currentUserID <= 0 {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "current user id is empty")
	}

	scopeUserIDs, err := resolveNewUserEligibilityScope(ctx, db, currentUserID)
	if err != nil {
		return nil, err
	}

	startAt, source, err := resolveNewUserEligibilityStartAt(ctx, db, scopeUserIDs)
	if err != nil {
		return nil, err
	}

	return &NewUserEligibilityContext{
		ScopeUserIDs:       scopeUserIDs,
		EligibilityStartAt: startAt,
		Source:             source,
	}, nil
}

func CountScopedSubscribePurchaseOrders(
	ctx context.Context,
	db *gorm.DB,
	scopeUserIDs []int64,
	subscribeID int64,
	statuses []int64,
	excludeOrderNo string,
) (int64, error) {
	if len(scopeUserIDs) == 0 {
		return 0, nil
	}

	var count int64
	query := db.WithContext(ctx).
		Model(&modelOrder.Order{}).
		Where("user_id IN ? AND type IN ?", scopeUserIDs, []int64{1, 2})
	if subscribeID > 0 {
		query = query.Where("subscribe_id = ?", subscribeID)
	}
	if len(statuses) > 0 {
		query = query.Where("status IN ?", statuses)
	}
	if excludeOrderNo != "" {
		query = query.Where("order_no != ?", excludeOrderNo)
	}
	if err := query.Count(&count).Error; err != nil {
		return 0, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count scoped subscribe purchase orders failed")
	}

	return count, nil
}

func resolveNewUserEligibilityScope(ctx context.Context, db *gorm.DB, currentUserID int64) ([]int64, error) {
	defaultScope := []int64{currentUserID}

	var relation newUserEligibilityRelation
	err := db.WithContext(ctx).
		Table("user_family_member AS ufm").
		Select("ufm.family_id, ufm.role, ufm.join_source, uf.owner_user_id").
		Joins("JOIN user_family AS uf ON uf.id = ufm.family_id AND uf.deleted_at IS NULL AND uf.status = ?", modelUser.FamilyStatusActive).
		Where("ufm.user_id = ? AND ufm.deleted_at IS NULL AND ufm.status = ?", currentUserID, modelUser.FamilyMemberActive).
		Limit(1).
		Take(&relation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return defaultScope, nil
		}
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query new user eligibility family relation failed")
	}

	if relation.Role != modelUser.FamilyRoleOwner && relation.JoinSource != NewUserEligibilityJoinSourceBindEmail {
		return defaultScope, nil
	}

	var scopedUserIDs []int64
	if err = db.WithContext(ctx).
		Table("user_family_member AS ufm").
		Select("ufm.user_id").
		Joins("JOIN user_family AS uf ON uf.id = ufm.family_id AND uf.deleted_at IS NULL AND uf.status = ?", modelUser.FamilyStatusActive).
		Where(
			"ufm.family_id = ? AND ufm.deleted_at IS NULL AND ufm.status = ? AND (ufm.role = ? OR ufm.join_source = ?)",
			relation.FamilyID,
			modelUser.FamilyMemberActive,
			modelUser.FamilyRoleOwner,
			NewUserEligibilityJoinSourceBindEmail,
		).
		Pluck("ufm.user_id", &scopedUserIDs).Error; err != nil {
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query new user eligibility scope failed")
	}

	scopedUserIDs = append(scopedUserIDs, currentUserID)
	return uniqueSortedInt64(scopedUserIDs), nil
}

func resolveNewUserEligibilityStartAt(ctx context.Context, db *gorm.DB, scopeUserIDs []int64) (time.Time, string, error) {
	var earliestDevice modelUser.Device
	err := db.WithContext(ctx).
		Model(&modelUser.Device{}).
		Where("user_id IN ?", scopeUserIDs).
		Order("created_at ASC").
		Limit(1).
		Take(&earliestDevice).Error
	switch {
	case err == nil && !earliestDevice.CreatedAt.IsZero():
		return earliestDevice.CreatedAt, NewUserEligibilitySourceDeviceCreatedAt, nil
	case err != nil && !errors.Is(err, gorm.ErrRecordNotFound):
		return time.Time{}, "", errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query new user eligibility device start failed")
	}

	var earliestUser modelUser.User
	err = db.WithContext(ctx).
		Model(&modelUser.User{}).
		Where("id IN ?", scopeUserIDs).
		Order("created_at ASC").
		Limit(1).
		Take(&earliestUser).Error
	if err != nil {
		return time.Time{}, "", errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query new user eligibility fallback start failed")
	}
	if earliestUser.CreatedAt.IsZero() {
		return time.Time{}, "", errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "new user eligibility start time not found")
	}

	return earliestUser.CreatedAt, NewUserEligibilitySourceUserCreatedAtFallback, nil
}

func uniqueSortedInt64(values []int64) []int64 {
	if len(values) == 0 {
		return nil
	}

	seen := make(map[int64]struct{}, len(values))
	result := make([]int64, 0, len(values))
	for _, value := range values {
		if value == 0 {
			continue
		}
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		result = append(result, value)
	}
	sort.Slice(result, func(i, j int) bool {
		return result[i] < result[j]
	})
	return result
}
