package domain

import (
	dbgen "Yimaru-Backend/gen/db"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap"
)

var (
	ErrSettingNotFound = errors.New("cannot find setting in list")
)

type SettingList struct {
	SMSProvider            SMSProvider `json:"sms_provider"`
	MaxNumberOfOutcomes    int64       `json:"max_number_of_outcomes"`
	MaxUnsettledBets       int64       `json:"max_unsettled_bets"`
	BetAmountLimit         Currency    `json:"bet_amount_limit"`
	DailyTicketPerIP       int64       `json:"daily_ticket_limit"`
	TotalWinningLimit      Currency    `json:"total_winning_limit"`
	TotalWinningNotify     Currency    `json:"total_winning_notify"`
	AmountForBetReferral   Currency    `json:"amount_for_bet_referral"`
	CashbackAmountCap      Currency    `json:"cashback_amount_cap"`
	DefaultWinningLimit    int64       `json:"default_winning_limit"`
	ReferralRewardAmount   Currency    `json:"referral_reward_amount"`
	CashbackPercentage     float32     `json:"cashback_percentage"`
	DefaultMaxReferrals    int64       `json:"default_max_referrals"`
	MinimumBetAmount       Currency    `json:"minimum_bet_amount"`
	BetDuplicateLimit      int64       `json:"bet_duplicate_limit"`
	SendEmailOnBetFinish   bool        `json:"send_email_on_bet_finish"`
	SendSMSOnBetFinish     bool        `json:"send_sms_on_bet_finish"`
	WelcomeBonusActive     bool        `json:"welcome_bonus_active"`
	WelcomeBonusMultiplier float32     `json:"welcome_bonus_multiplier"`
	WelcomeBonusCap        Currency    `json:"welcome_bonus_cap"`
	WelcomeBonusCount      int64       `json:"welcome_bonus_count"`
	WelcomeBonusExpire     int64       `json:"welcome_bonus_expiry"`
}

// Default Global Settings added to the database if the setting isn't found
// This is already configured in the seed_data.sql, but just added it here too
// in case of database to code drift
func NewDefaultSettingList() SettingList {
	return SettingList{
		SMSProvider:            AfroMessage, //Provider that will be used for sending sms
		MaxNumberOfOutcomes:    30,          //Maximum number of outcomes for a single bet
		MaxUnsettledBets:       100,         //Maximum number of unsettled bets before system disabled bet service
		BetAmountLimit:         10000000,    //Maximum amount that can be bet (100,000.00 Birr Limit)
		DailyTicketPerIP:       50,          //Daily Number of Tickets That can be cut
		TotalWinningLimit:      100000000,   //Winning Limit (1,000,000.00 Birr)
		TotalWinningNotify:     10000000,    //Notify if user wins (100,000.00+ Birr)
		AmountForBetReferral:   1000000,     //Reward for bet referral (only for betting) (10,000.00 Birr)
		CashbackAmountCap:      10.00,       //Cashback amount limit (10 Birr)
		DefaultWinningLimit:    5000000,     //Birr Limit on single event (50,000.00)
		ReferralRewardAmount:   10000,       //Reward for user referral (100.00 Birr)
		CashbackPercentage:     0.2,         //Cashback Percent (20%)
		DefaultMaxReferrals:    15,          //Max number of user referrals (15)
		MinimumBetAmount:       100,         //Minimum Bet Amount (1 Birr)
		BetDuplicateLimit:      5,           //Maximum number of duplicate bets (5)
		SendEmailOnBetFinish:   true,        //Whether to send email to user when he wins bet
		SendSMSOnBetFinish:     false,       //Whether to send sms to user when he wins bet
		WelcomeBonusActive:     false,       //Is Welcome Bonus in effect
		WelcomeBonusMultiplier: 1.5,         //Welcome Bonus Multiplier
		WelcomeBonusCap:        100000,      //Welcome Bonus Limit
		WelcomeBonusCount:      3,           //Maximum number of welcome bonuses given
		WelcomeBonusExpire:     10,          //Welcome Bonus Expiry
	}
}

func (s SettingList) ToSettingArray() []Setting {
	return []Setting{
		{"sms_provider", string(s.SMSProvider), time.Now()},
		{"max_number_of_outcomes", strconv.FormatInt(s.MaxNumberOfOutcomes, 10), time.Now()},
		{"max_unsettled_bets", strconv.FormatInt(s.MaxUnsettledBets, 10), time.Now()},
		{"bet_amount_limit", strconv.FormatInt(int64(s.BetAmountLimit), 10), time.Now()},
		{"daily_ticket_limit", strconv.FormatInt(s.DailyTicketPerIP, 10), time.Now()},
		{"total_winnings_limit", strconv.FormatInt(int64(s.TotalWinningLimit), 10), time.Now()},
		{"total_winnings_notify", strconv.FormatInt(int64(s.TotalWinningNotify), 10), time.Now()},
		{"amount_for_bet_referral", strconv.FormatInt(int64(s.AmountForBetReferral), 10), time.Now()},
		{"cashback_amount_cap", strconv.FormatInt(int64(s.CashbackAmountCap), 10), time.Now()},
		{"default_winning_limit", strconv.FormatInt(s.DefaultWinningLimit, 10), time.Now()},
		{"referral_reward_amount", strconv.FormatInt(int64(s.ReferralRewardAmount), 10), time.Now()},
		{"cashback_percentage", strconv.FormatFloat(float64(s.CashbackPercentage), 'f', -1, 32), time.Now()},
		{"default_max_referrals", strconv.FormatInt(s.DefaultMaxReferrals, 10), time.Now()},
		{"minimum_bet_amount", strconv.FormatInt(int64(s.MinimumBetAmount), 10), time.Now()},
		{"bet_duplicate_limit", strconv.FormatInt(s.BetDuplicateLimit, 10), time.Now()},
		{"send_email_on_bet_finish", strconv.FormatBool(s.SendEmailOnBetFinish), time.Now()},
		{"send_sms_on_bet_finish", strconv.FormatBool(s.SendSMSOnBetFinish), time.Now()},
		{"welcome_bonus_active", strconv.FormatBool(s.WelcomeBonusActive), time.Now()},
		{"welcome_bonus_multiplier", strconv.FormatFloat(float64(s.WelcomeBonusMultiplier), 'f', -1, 32), time.Now()},
		{"welcome_bonus_cap", strconv.FormatInt(int64(s.WelcomeBonusCap), 10), time.Now()},
		{"welcome_bonus_count", strconv.FormatInt(s.WelcomeBonusCount, 10), time.Now()},
		{"welcome_bonus_expiry", strconv.FormatInt(s.WelcomeBonusExpire, 10), time.Now()},
	}
}

type SettingListRes struct {
	SMSProvider            SMSProvider `json:"sms_provider"`
	MaxNumberOfOutcomes    int64       `json:"max_number_of_outcomes"`
	MaxUnsettledBets       int64       `json:"max_unsettled_bets"`
	BetAmountLimit         float32     `json:"bet_amount_limit"`
	DailyTicketPerIP       int64       `json:"daily_ticket_limit"`
	TotalWinningLimit      float32     `json:"total_winning_limit"`
	TotalWinningNotify     float32     `json:"total_winning_notify"`
	AmountForBetReferral   float32     `json:"amount_for_bet_referral"`
	CashbackAmountCap      float32     `json:"cashback_amount_cap"`
	DefaultWinningLimit    int64       `json:"default_winning_limit"`
	ReferralRewardAmount   float32     `json:"referral_reward_amount"`
	CashbackPercentage     float32     `json:"cashback_percentage"`
	DefaultMaxReferrals    int64       `json:"default_max_referrals"`
	MinimumBetAmount       float32     `json:"minimum_bet_amount"`
	BetDuplicateLimit      int64       `json:"bet_duplicate_limit"`
	SendEmailOnBetFinish   bool        `json:"send_email_on_bet_finish"`
	SendSMSOnBetFinish     bool        `json:"send_sms_on_bet_finish"`
	WelcomeBonusActive     bool        `json:"welcome_bonus_active"`
	WelcomeBonusMultiplier float32     `json:"welcome_bonus_multiplier"`
	WelcomeBonusCap        float32     `json:"welcome_bonus_cap"`
	WelcomeBonusCount      int64       `json:"welcome_bonus_count"`
	WelcomeBonusExpire     int64       `json:"welcome_bonus_expiry"`
}

func ConvertSettingListRes(settings SettingList) SettingListRes {
	return SettingListRes{
		SMSProvider:            settings.SMSProvider,
		MaxNumberOfOutcomes:    settings.MaxNumberOfOutcomes,
		MaxUnsettledBets:       settings.MaxUnsettledBets,
		BetAmountLimit:         settings.BetAmountLimit.Float32(),
		DailyTicketPerIP:       settings.DailyTicketPerIP,
		TotalWinningLimit:      settings.TotalWinningLimit.Float32(),
		TotalWinningNotify:     settings.TotalWinningNotify.Float32(),
		AmountForBetReferral:   settings.AmountForBetReferral.Float32(),
		CashbackAmountCap:      settings.CashbackAmountCap.Float32(),
		DefaultWinningLimit:    settings.DefaultWinningLimit,
		ReferralRewardAmount:   settings.ReferralRewardAmount.Float32(),
		CashbackPercentage:     settings.CashbackPercentage,
		DefaultMaxReferrals:    settings.DefaultMaxReferrals,
		MinimumBetAmount:       settings.MinimumBetAmount.Float32(),
		BetDuplicateLimit:      settings.BetDuplicateLimit,
		SendEmailOnBetFinish:   settings.SendEmailOnBetFinish,
		SendSMSOnBetFinish:     settings.SendSMSOnBetFinish,
		WelcomeBonusActive:     settings.WelcomeBonusActive,
		WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier,
		WelcomeBonusCap:        settings.WelcomeBonusCap.Float32(),
		WelcomeBonusCount:      settings.WelcomeBonusCount,
		WelcomeBonusExpire:     settings.WelcomeBonusExpire,
	}
}

type SaveSettingListReq struct {
	SMSProvider            *string  `json:"sms_provider,omitempty"`
	MaxNumberOfOutcomes    *int64   `json:"max_number_of_outcomes,omitempty"`
	MaxUnsettledBets       *int64   `json:"max_unsettled_bets,omitempty"`
	BetAmountLimit         *float32 `json:"bet_amount_limit,omitempty"`
	DailyTicketPerIP       *int64   `json:"daily_ticket_limit,omitempty"`
	TotalWinningLimit      *float32 `json:"total_winning_limit,omitempty"`
	TotalWinningNotify     *float32 `json:"total_winning_notify,omitempty"`
	AmountForBetReferral   *float32 `json:"amount_for_bet_referral,omitempty"`
	CashbackAmountCap      *float32 `json:"cashback_amount_cap,omitempty"`
	DefaultWinningLimit    *int64   `json:"default_winning_limit,omitempty"`
	ReferralRewardAmount   *float32 `json:"referral_reward_amount,omitempty"`
	CashbackPercentage     *float32 `json:"cashback_percentage,omitempty"`
	DefaultMaxReferrals    *int64   `json:"default_max_referrals,omitempty"`
	MinimumBetAmount       *float32 `json:"minimum_bet_amount,omitempty"`
	BetDuplicateLimit      *int64   `json:"bet_duplicate_limit,omitempty"`
	SendEmailOnBetFinish   *bool    `json:"send_email_on_bet_finish,omitempty"`
	SendSMSOnBetFinish     *bool    `json:"send_sms_on_bet_finish,omitempty"`
	WelcomeBonusActive     *bool    `json:"welcome_bonus_active,omitempty"`
	WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier,omitempty"`
	WelcomeBonusCap        *float32 `json:"welcome_bonus_cap,omitempty"`
	WelcomeBonusCount      *int64   `json:"welcome_bonus_count,omitempty"`
	WelcomeBonusExpire     *int64   `json:"welcome_bonus_expiry,omitempty"`
}

type ValidSettingList struct {
	SMSProvider            ValidString
	MaxNumberOfOutcomes    ValidInt64
	MaxUnsettledBets       ValidInt64
	BetAmountLimit         ValidCurrency
	DailyTicketPerIP       ValidInt64
	TotalWinningLimit      ValidCurrency
	TotalWinningNotify     ValidCurrency
	AmountForBetReferral   ValidCurrency
	CashbackAmountCap      ValidCurrency
	DefaultWinningLimit    ValidInt64
	ReferralRewardAmount   ValidCurrency
	CashbackPercentage     ValidFloat32
	DefaultMaxReferrals    ValidInt64
	MinimumBetAmount       ValidCurrency
	BetDuplicateLimit      ValidInt64
	SendEmailOnBetFinish   ValidBool
	SendSMSOnBetFinish     ValidBool
	WelcomeBonusActive     ValidBool
	WelcomeBonusMultiplier ValidFloat32
	WelcomeBonusCap        ValidCurrency
	WelcomeBonusCount      ValidInt64
	WelcomeBonusExpire     ValidInt64
}

func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
	return ValidSettingList{
		SMSProvider:            ConvertStringPtr(settings.SMSProvider),
		MaxNumberOfOutcomes:    ConvertInt64Ptr(settings.MaxNumberOfOutcomes),
		MaxUnsettledBets:       ConvertInt64Ptr(settings.MaxUnsettledBets),
		BetAmountLimit:         ConvertFloat32PtrToCurrency(settings.BetAmountLimit),
		DailyTicketPerIP:       ConvertInt64Ptr(settings.DailyTicketPerIP),
		TotalWinningLimit:      ConvertFloat32PtrToCurrency(settings.TotalWinningLimit),
		TotalWinningNotify:     ConvertFloat32PtrToCurrency(settings.TotalWinningNotify),
		AmountForBetReferral:   ConvertFloat32PtrToCurrency(settings.AmountForBetReferral),
		CashbackAmountCap:      ConvertFloat32PtrToCurrency(settings.CashbackAmountCap),
		DefaultWinningLimit:    ConvertInt64Ptr(settings.DefaultWinningLimit),
		ReferralRewardAmount:   ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount),
		CashbackPercentage:     ConvertFloat32Ptr(settings.CashbackPercentage),
		DefaultMaxReferrals:    ConvertInt64Ptr(settings.DefaultMaxReferrals),
		MinimumBetAmount:       ConvertFloat32PtrToCurrency(settings.MinimumBetAmount),
		BetDuplicateLimit:      ConvertInt64Ptr(settings.BetDuplicateLimit),
		SendEmailOnBetFinish:   ConvertBoolPtr(settings.SendEmailOnBetFinish),
		SendSMSOnBetFinish:     ConvertBoolPtr(settings.SendSMSOnBetFinish),
		WelcomeBonusActive:     ConvertBoolPtr(settings.WelcomeBonusActive),
		WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier),
		WelcomeBonusCap:        ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap),
		WelcomeBonusCount:      ConvertInt64Ptr(settings.WelcomeBonusCount),
		WelcomeBonusExpire:     ConvertInt64Ptr(settings.WelcomeBonusExpire),
	}
}

// Always make sure to run the validation before converting this
func (vsl *ValidSettingList) ToSettingList() SettingList {
	return SettingList{
		SMSProvider:            SMSProvider(vsl.SMSProvider.Value),
		MaxNumberOfOutcomes:    vsl.MaxNumberOfOutcomes.Value,
		MaxUnsettledBets:       vsl.MaxUnsettledBets.Value,
		BetAmountLimit:         vsl.BetAmountLimit.Value,
		DailyTicketPerIP:       vsl.DailyTicketPerIP.Value,
		TotalWinningLimit:      vsl.TotalWinningLimit.Value,
		TotalWinningNotify:     vsl.TotalWinningNotify.Value,
		AmountForBetReferral:   vsl.AmountForBetReferral.Value,
		CashbackAmountCap:      vsl.CashbackAmountCap.Value,
		DefaultWinningLimit:    vsl.DefaultWinningLimit.Value,
		ReferralRewardAmount:   vsl.ReferralRewardAmount.Value,
		CashbackPercentage:     vsl.CashbackPercentage.Value,
		DefaultMaxReferrals:    vsl.DefaultMaxReferrals.Value,
		MinimumBetAmount:       vsl.MinimumBetAmount.Value,
		BetDuplicateLimit:      vsl.BetDuplicateLimit.Value,
		SendEmailOnBetFinish:   vsl.SendEmailOnBetFinish.Value,
		SendSMSOnBetFinish:     vsl.SendSMSOnBetFinish.Value,
		WelcomeBonusActive:     vsl.WelcomeBonusActive.Value,
		WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value,
		WelcomeBonusCap:        vsl.WelcomeBonusCap.Value,
		WelcomeBonusCount:      vsl.WelcomeBonusCount.Value,
		WelcomeBonusExpire:     vsl.WelcomeBonusExpire.Value,
	}
}

// Custom Validations for non-generic types
func (vsl *ValidSettingList) CustomValidationSettings() error {
	if !SMSProvider(vsl.SMSProvider.Value).IsValid() {
		return fmt.Errorf("sms provider invalid")
	}
	return nil
}

func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 {
	return map[string]*ValidInt64{
		"max_number_of_outcomes": &vsl.MaxNumberOfOutcomes,
		"max_unsettled_bets":     &vsl.MaxUnsettledBets,
		"daily_ticket_limit":     &vsl.DailyTicketPerIP,
		"default_winning_limit":  &vsl.DefaultWinningLimit,
		"default_max_referrals":  &vsl.DefaultMaxReferrals,
		"bet_duplicate_limit":    &vsl.BetDuplicateLimit,
		"welcome_bonus_count":    &vsl.WelcomeBonusCount,
		"welcome_bonus_expiry":   &vsl.WelcomeBonusExpire,
	}
}

func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency {
	return map[string]*ValidCurrency{
		"bet_amount_limit":        &vsl.BetAmountLimit,
		"total_winnings_limit":    &vsl.TotalWinningLimit,
		"total_winnings_notify":   &vsl.TotalWinningNotify,
		"amount_for_bet_referral": &vsl.AmountForBetReferral,
		"cashback_amount_cap":     &vsl.CashbackAmountCap,
		"referral_reward_amount":  &vsl.ReferralRewardAmount,
		"minimum_bet_amount":      &vsl.MinimumBetAmount,
		"welcome_bonus_cap":       &vsl.WelcomeBonusCap,
	}
}

func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString {
	return map[string]*ValidString{
		"sms_provider": &vsl.SMSProvider,
	}
}

func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool {
	return map[string]*ValidBool{
		"send_email_on_bet_finish": &vsl.SendEmailOnBetFinish,
		"send_sms_on_bet_finish":   &vsl.SendSMSOnBetFinish,
		"welcome_bonus_active":     &vsl.WelcomeBonusActive,
	}
}

func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 {
	return map[string]*ValidFloat32{
		"cashback_percentage":      &vsl.CashbackPercentage,
		"welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier,
	}
}

func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime {
	return map[string]*ValidTime{}
}

// Setting Functions

func (vsl *ValidSettingList) GetTotalSettings() int {
	return len(vsl.GetInt64SettingsMap()) +
		len(vsl.GetCurrencySettingsMap()) +
		len(vsl.GetStringSettingsMap()) +
		len(vsl.GetBoolSettingsMap()) +
		len(vsl.GetFloat32SettingsMap()) +
		len(vsl.GetTimeSettingsMap())
}

func (vsl *ValidSettingList) GetAllValid() map[string]*bool {

	settingValid := make(map[string]*bool)

	for key, setting := range vsl.GetInt64SettingsMap() {
		settingValid[key] = &(*setting).Valid
	}
	for key, setting := range vsl.GetCurrencySettingsMap() {
		settingValid[key] = &(*setting).Valid
	}
	for key, setting := range vsl.GetStringSettingsMap() {
		settingValid[key] = &(*setting).Valid
	}
	for key, setting := range vsl.GetBoolSettingsMap() {
		settingValid[key] = &(*setting).Valid
	}
	for key, setting := range vsl.GetFloat32SettingsMap() {
		settingValid[key] = &(*setting).Valid
	}
	for key, setting := range vsl.GetTimeSettingsMap() {
		settingValid[key] = &(*setting).Valid
	}

	return settingValid
}

// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error {
// 	for key, setting := range settings {

//			if key == searchKey {
//				s, err := setVal(searchVal)
//				if err != nil {
//					return err
//				}
//				*setting = s
//			}
//			return nil
//		}
//		return ErrSettingNotFound
//	}
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetInt64SettingsMap() {
		if key == searchKey {
			value, err := strconv.ParseInt(searchVal, 10, 64)
			if err != nil {
				return err
			}
			*setting = ValidInt64{Value: value, Valid: true}
			return nil
		}
	}
	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetCurrencySettingsMap() {
		if key == searchKey {
			value, err := strconv.ParseInt(searchVal, 10, 64)
			if err != nil {
				return err
			}
			*setting = ValidCurrency{Value: Currency(value), Valid: true}
			return nil
		}
	}

	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetStringSettingsMap() {
		if key == searchKey {
			*setting = ValidString{Value: searchVal, Valid: true}
			return nil
		}
	}

	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetBoolSettingsMap() {

		if key == searchKey {
			value, err := strconv.ParseBool(searchVal)
			if err != nil {
				return err
			}

			*setting = ValidBool{Value: value, Valid: true}
			return nil
		}

	}

	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetFloat32SettingsMap() {
		if key == searchKey {
			value, err := strconv.ParseFloat(searchVal, 32)
			if err != nil {
				return err
			}
			*setting = ValidFloat32{Value: float32(value), Valid: true}
			return nil
		}
	}

	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error {
	for key, setting := range vsl.GetTimeSettingsMap() {
		if key == searchKey {
			value, err := time.Parse(time.RFC3339, searchVal)
			if err != nil {
				return err
			}
			*setting = ValidTime{Value: value, Valid: true}
			return nil
		}
	}
	return ErrSettingNotFound
}

func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error {
	setters := []func(string, string) error{
		vsl.SetInt64Setting,
		vsl.SetCurrencySetting,
		vsl.SetStringSetting,
		vsl.SetBoolSetting,
		vsl.SetFloat32Setting,
		vsl.SetTimeSetting,
	}

	for _, setter := range setters {
		if err := setter(searchKey, searchVal); err != nil {
			if err == ErrSettingNotFound {
				// fmt.Printf("setting is not found %v \n", searchKey)
				continue // not this setter, try the next
			}
			return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err)
		}
		return nil // successfully set
	}

	// If we get here, none of the setters matched
	return ErrSettingNotFound
}

func convertValidSettings[T any](
	settings map[string]*T,
	isValid func(*T) bool,
	toString func(*T) string,
) []Setting {
	result := make([]Setting, 0, len(settings))
	for key, s := range settings {
		if isValid(s) {
			result = append(result, Setting{
				Key:   key,
				Value: toString(s),
			})
		}
	}
	return result
}

func (vsl *ValidSettingList) ConvertInt64Settings() []Setting {
	return convertValidSettings(
		vsl.GetInt64SettingsMap(),
		func(s *ValidInt64) bool { return s.Valid },
		func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) },
	)
}

func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting {
	return convertValidSettings(
		vsl.GetCurrencySettingsMap(),
		func(s *ValidCurrency) bool { return s.Valid },
		func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) },
	)
}

func (vsl *ValidSettingList) ConvertStringSettings() []Setting {
	return convertValidSettings(
		vsl.GetStringSettingsMap(),
		func(s *ValidString) bool { return s.Valid },
		func(s *ValidString) string { return s.Value },
	)
}

func (vsl *ValidSettingList) ConvertBoolSettings() []Setting {
	return convertValidSettings(
		vsl.GetBoolSettingsMap(),
		func(s *ValidBool) bool { return s.Valid },
		func(s *ValidBool) string { return strconv.FormatBool(s.Value) },
	)
}

func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting {
	return convertValidSettings(
		vsl.GetFloat32SettingsMap(),
		func(s *ValidFloat32) bool { return s.Valid },
		func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) },
	)
}

func (vsl *ValidSettingList) ConvertTimeSettings() []Setting {
	return convertValidSettings(
		vsl.GetTimeSettingsMap(),
		func(s *ValidTime) bool { return s.Valid },
		func(s *ValidTime) string { return s.Value.Format(time.RFC3339) },
	)
}

func validateSettings[T any](
	settings map[string]*T,
	customValidator func(*T) bool,
) error {
	var errs []string
	for key, s := range settings {
		if !customValidator(s) {

			errs = append(errs, fmt.Sprintf("%v is invalid", key))
		}
	}
	if len(errs) > 0 {
		return errors.New(strings.Join(errs, "; "))
	}

	return nil
}

func (vsl *ValidSettingList) ValidateInt64Settings() error {
	return validateSettings(vsl.GetInt64SettingsMap(),
		func(s *ValidInt64) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateCurrencySettings() error {
	return validateSettings(vsl.GetCurrencySettingsMap(),
		func(s *ValidCurrency) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateStringSettings() error {
	return validateSettings(vsl.GetStringSettingsMap(),
		func(s *ValidString) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateBoolSettings() error {
	return validateSettings(vsl.GetBoolSettingsMap(),
		func(s *ValidBool) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateFloat32Settings() error {
	return validateSettings(vsl.GetFloat32SettingsMap(),
		func(s *ValidFloat32) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateTimeSettings() error {
	return validateSettings(vsl.GetTimeSettingsMap(),
		func(s *ValidTime) bool {
			return s.Valid
		},
	)
}

func (vsl *ValidSettingList) ValidateAllSettings() error {
	var errs []string
	validators := []func() error{
		vsl.ValidateInt64Settings,
		vsl.ValidateCurrencySettings,
		vsl.ValidateStringSettings,
		vsl.ValidateBoolSettings,
		vsl.ValidateFloat32Settings,
		vsl.ValidateTimeSettings,
		vsl.CustomValidationSettings,
	}

	for _, validator := range validators {
		if err := validator(); err != nil {

			errs = append(errs, err.Error())
		}
	}
	if len(errs) > 0 {
		return errors.New(strings.Join(errs, "; "))
	}

	return nil
}

func (vsl *ValidSettingList) ConvertAllSettings() []Setting {
	totalCap := vsl.GetTotalSettings()
	all := make([]Setting, 0, totalCap)

	all = append(all, vsl.ConvertInt64Settings()...)
	all = append(all, vsl.ConvertCurrencySettings()...)
	all = append(all, vsl.ConvertStringSettings()...)
	all = append(all, vsl.ConvertBoolSettings()...)
	all = append(all, vsl.ConvertFloat32Settings()...)
	all = append(all, vsl.ConvertTimeSettings()...)

	return all
}

func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) {
	var dbSettingList ValidSettingList

	for _, setting := range settings {
		if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
			if err == ErrSettingNotFound {
				MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
				continue
			}
			MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err))
		}
	}

	if err := dbSettingList.ValidateAllSettings(); err != nil {
		MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList))
		return SettingList{}, err
	}

	settingList := dbSettingList.ToSettingList()

	return settingList, nil
}

// func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) {
// 	var dbSettingList ValidSettingList

// 	for _, setting := range settings {
// 		if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
// 			if err == ErrSettingNotFound {
// 				MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
// 			}
// 		}
// 	}

// 	if err := dbSettingList.ValidateAllSettings(); err != nil {
// 		MongoDBLogger.Warn("setting validation error", zap.Error(err))
// 		return SettingList{}, err
// 	}

// 	settingList := dbSettingList.ToSettingList()

// 	return settingList, nil
// }
