package repository

import (
	"context"
	"database/sql"
	"errors"

	dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	"github.com/jackc/pgx/v5/pgtype"
)

type ReferralRepository interface {
	CreateReferral(ctx context.Context, referral *domain.Referral) error
	GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error)
	UpdateReferral(ctx context.Context, referral *domain.Referral) error
	GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error)
	GetSettings(ctx context.Context) (*domain.ReferralSettings, error)
	UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error
	CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error
	GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) // New method
	UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error
}

type ReferralRepo struct {
	store *Store
}

func NewReferralRepository(store *Store) ReferralRepository {
	return &ReferralRepo{store: store}
}

func (r *ReferralRepo) UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error {
	params := dbgen.UpdateReferralCodeParams{
		ID: codedata.UserID,
		ReferralCode: pgtype.Text{
			String: codedata.Code,
			Valid:  true,
		},
	}

	return r.store.queries.UpdateReferralCode(ctx, params)
}

func (r *ReferralRepo) CreateReferral(ctx context.Context, referral *domain.Referral) error {
	rewardAmount := pgtype.Numeric{}
	if err := rewardAmount.Scan(referral.RewardAmount); err != nil {
		return err
	}

	params := dbgen.CreateReferralParams{
		ReferralCode: referral.ReferralCode,
		ReferrerID:   referral.ReferrerID,
		Status:       dbgen.Referralstatus(referral.Status),
		RewardAmount: rewardAmount,
		ExpiresAt:    pgtype.Timestamptz{Time: referral.ExpiresAt, Valid: true},
	}

	_, err := r.store.queries.CreateReferral(ctx, params)
	return err
}

func (r *ReferralRepo) GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error) {
	dbReferral, err := r.store.queries.GetReferralByCode(ctx, code)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return r.mapToDomainReferral(&dbReferral), nil
}

func (r *ReferralRepo) UpdateReferral(ctx context.Context, referral *domain.Referral) error {
	var referredID pgtype.Text
	if referral.ReferredID != nil {
		referredID = pgtype.Text{String: *referral.ReferredID, Valid: true}
	}

	params := dbgen.UpdateReferralParams{
		ID:         referral.ID,
		ReferredID: referredID,
		Status:     dbgen.Referralstatus(referral.Status),
	}

	_, err := r.store.queries.UpdateReferral(ctx, params)
	return err
}

func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error) {
	stats, err := r.store.queries.GetReferralStats(ctx, userID)
	if err != nil {
		return nil, err
	}

	return &domain.ReferralStats{
		TotalReferrals:     int(stats.TotalReferrals),
		CompletedReferrals: int(stats.CompletedReferrals),
		TotalRewardEarned:  stats.TotalRewardEarned.(float64),
		PendingRewards:     stats.PendingRewards.(float64),
	}, nil
}

func (r *ReferralRepo) GetSettings(ctx context.Context) (*domain.ReferralSettings, error) {
	settings, err := r.store.queries.GetReferralSettings(ctx)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return r.mapToDomainSettings(&settings), nil
}

func (r *ReferralRepo) UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
	rewardAmount := pgtype.Numeric{}
	if err := rewardAmount.Scan(settings.ReferralRewardAmount); err != nil {
		return err
	}

	cashbackPercentage := pgtype.Numeric{}
	if err := cashbackPercentage.Scan(settings.CashbackPercentage); err != nil {
		return err
	}

	betReferralBonusPercentage := pgtype.Numeric{}
	if err := betReferralBonusPercentage.Scan(settings.BetReferralBonusPercentage); err != nil {
		return err
	}

	params := dbgen.UpdateReferralSettingsParams{
		ID:                         settings.ID,
		ReferralRewardAmount:       rewardAmount,
		CashbackPercentage:         cashbackPercentage,
		BetReferralBonusPercentage: betReferralBonusPercentage, // New field
		MaxReferrals:               settings.MaxReferrals,
		ExpiresAfterDays:           settings.ExpiresAfterDays,
		UpdatedBy:                  settings.UpdatedBy,
	}

	_, err := r.store.queries.UpdateReferralSettings(ctx, params)
	return err
}

func (r *ReferralRepo) CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
	rewardAmount := pgtype.Numeric{}
	if err := rewardAmount.Scan(settings.ReferralRewardAmount); err != nil {
		return err
	}

	cashbackPercentage := pgtype.Numeric{}
	if err := cashbackPercentage.Scan(settings.CashbackPercentage); err != nil {
		return err
	}

	betReferralBonusPercentage := pgtype.Numeric{}
	if err := betReferralBonusPercentage.Scan(settings.BetReferralBonusPercentage); err != nil {
		return err
	}

	params := dbgen.CreateReferralSettingsParams{
		ReferralRewardAmount:       rewardAmount,
		CashbackPercentage:         cashbackPercentage,
		BetReferralBonusPercentage: betReferralBonusPercentage, // New field
		MaxReferrals:               settings.MaxReferrals,
		ExpiresAfterDays:           settings.ExpiresAfterDays,
		UpdatedBy:                  settings.UpdatedBy,
	}

	_, err := r.store.queries.CreateReferralSettings(ctx, params)
	return err
}

func (r *ReferralRepo) GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) {
	dbReferral, err := r.store.queries.GetReferralByReferredID(ctx, pgtype.Text{String: referredID, Valid: true})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return r.mapToDomainReferral(&dbReferral), nil
}

func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral {
	var referredID *string
	if dbRef.ReferredID.Valid {
		referredID = &dbRef.ReferredID.String
	}

	rewardAmount := 0.0
	if dbRef.RewardAmount.Valid {
		if f8, err := dbRef.RewardAmount.Float64Value(); err == nil {
			rewardAmount = f8.Float64
		}
	}

	cashbackAmount := 0.0
	if dbRef.CashbackAmount.Valid {
		if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil {
			cashbackAmount = f8.Float64
		}
	}

	return &domain.Referral{
		ID:             dbRef.ID,
		ReferralCode:   dbRef.ReferralCode,
		ReferrerID:     dbRef.ReferrerID,
		ReferredID:     referredID,
		Status:         domain.ReferralStatus(dbRef.Status),
		RewardAmount:   rewardAmount,
		CashbackAmount: cashbackAmount,
		CreatedAt:      dbRef.CreatedAt.Time,
		UpdatedAt:      dbRef.UpdatedAt.Time,
		ExpiresAt:      dbRef.ExpiresAt.Time,
	}
}

func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings {
	rewardAmount := 0.0
	if dbSettings.ReferralRewardAmount.Valid {
		if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil {
			rewardAmount = f8.Float64
		}
	}

	cashbackPercentage := 0.0
	if dbSettings.CashbackPercentage.Valid {
		if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil {
			cashbackPercentage = f8.Float64
		}
	}

	betReferralBonusPercentage := 0.0
	if dbSettings.BetReferralBonusPercentage.Valid {
		if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil {
			betReferralBonusPercentage = f8.Float64
		}
	}

	return &domain.ReferralSettings{
		ID:                         dbSettings.ID,
		ReferralRewardAmount:       rewardAmount,
		CashbackPercentage:         cashbackPercentage,
		BetReferralBonusPercentage: betReferralBonusPercentage, // New field
		MaxReferrals:               dbSettings.MaxReferrals,
		ExpiresAfterDays:           dbSettings.ExpiresAfterDays,
		UpdatedBy:                  dbSettings.UpdatedBy,
		CreatedAt:                  dbSettings.CreatedAt.Time,
		UpdatedAt:                  dbSettings.UpdatedAt.Time,
		Version:                    dbSettings.Version,
	}
}
