package common

import (
	"context"
	"errors"
	"strings"
	"time"

	"gorm.io/gorm"
	"stream.api/internal/database/model"
)

type UserPayload struct {
	ID                string     `json:"id"`
	Email             string     `json:"email"`
	Username          *string    `json:"username,omitempty"`
	Avatar            *string    `json:"avatar,omitempty"`
	Role              *string    `json:"role,omitempty"`
	GoogleID          *string    `json:"google_id,omitempty"`
	StorageUsed       int64      `json:"storage_used"`
	PlanID            *string    `json:"plan_id,omitempty"`
	PlanStartedAt     *time.Time `json:"plan_started_at,omitempty"`
	PlanExpiresAt     *time.Time `json:"plan_expires_at,omitempty"`
	PlanTermMonths    *int32     `json:"plan_term_months,omitempty"`
	PlanPaymentMethod *string    `json:"plan_payment_method,omitempty"`
	PlanExpiringSoon  bool       `json:"plan_expiring_soon"`
	WalletBalance     float64    `json:"wallet_balance"`
	Language          string     `json:"language"`
	Locale            string     `json:"locale"`
	CreatedAt         *time.Time `json:"created_at,omitempty"`
	UpdatedAt         time.Time  `json:"updated_at"`
}

func BuildUserPayload(ctx context.Context, db *gorm.DB, user *model.User) (*UserPayload, error) {
	pref, err := model.FindOrCreateUserPreference(ctx, db, user.ID)
	if err != nil {
		return nil, err
	}

	walletBalance, err := model.GetWalletBalance(ctx, db, user.ID)
	if err != nil {
		return nil, err
	}

	language := strings.TrimSpace(model.StringValue(pref.Language))
	if language == "" {
		language = "en"
	}
	locale := strings.TrimSpace(model.StringValue(pref.Locale))
	if locale == "" {
		locale = language
	}

	effectivePlanID := user.PlanID
	var planStartedAt *time.Time
	var planExpiresAt *time.Time
	var planTermMonths *int32
	var planPaymentMethod *string
	planExpiringSoon := false
	now := time.Now().UTC()

	subscription, err := model.GetLatestPlanSubscription(ctx, db, user.ID)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	if err == nil {
		startedAt := subscription.StartedAt.UTC()
		expiresAt := subscription.ExpiresAt.UTC()
		termMonths := subscription.TermMonths
		paymentMethod := normalizePlanPaymentMethod(subscription.PaymentMethod)

		planStartedAt = &startedAt
		planExpiresAt = &expiresAt
		planTermMonths = &termMonths
		planPaymentMethod = &paymentMethod

		if expiresAt.After(now) {
			effectivePlanID = &subscription.PlanID
			planExpiringSoon = isPlanExpiringSoon(expiresAt, now)
		} else {
			effectivePlanID = nil
		}
	}

	return &UserPayload{
		ID:                user.ID,
		Email:             user.Email,
		Username:          user.Username,
		Avatar:            user.Avatar,
		Role:              user.Role,
		GoogleID:          user.GoogleID,
		StorageUsed:       user.StorageUsed,
		PlanID:            effectivePlanID,
		PlanStartedAt:     planStartedAt,
		PlanExpiresAt:     planExpiresAt,
		PlanTermMonths:    planTermMonths,
		PlanPaymentMethod: planPaymentMethod,
		PlanExpiringSoon:  planExpiringSoon,
		WalletBalance:     walletBalance,
		Language:          language,
		Locale:            locale,
		CreatedAt:         user.CreatedAt,
		UpdatedAt:         user.UpdatedAt,
	}, nil
}

func normalizePlanPaymentMethod(value string) string {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "topup":
		return "topup"
	default:
		return "wallet"
	}
}

func isPlanExpiringSoon(expiresAt time.Time, now time.Time) bool {
	hoursUntilExpiry := expiresAt.Sub(now).Hours()
	const thresholdHours = 7 * 24
	return hoursUntilExpiry > 0 && hoursUntilExpiry <= thresholdHours
}
