package config

import (
	"errors"
	"fmt"
	"log/slog"
	"os"
	"strconv"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	customlogger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger"
	"github.com/joho/godotenv"
)

var (
	ErrInvalidDbUrl             = errors.New("db url is invalid")
	ErrInvalidPort              = errors.New("port number is invalid")
	ErrRefreshExpiry            = errors.New("refresh token expiry is invalid")
	ErrAccessExpiry             = errors.New("access token expiry is invalid")
	ErrInvalidJwtKey            = errors.New("jwt key is invalid")
	ErrLogLevel                 = errors.New("log level not set")
	ErrInvalidLevel             = errors.New("invalid log level")
	ErrInvalidEnv               = errors.New("env not set or invalid")
	ErrInvalidSMSAPIKey         = errors.New("SMS API key is invalid")
	ErrMissingBetToken          = errors.New("missing BET365_TOKEN in .env")
	ErrInvalidPopOKClientID     = errors.New("PopOK client ID is invalid")
	ErrInvalidPopOKSecretKey    = errors.New("PopOK secret key is invalid")
	ErrInvalidPopOKBaseURL      = errors.New("PopOK base URL is invalid")
	ErrInvalidPopOKCallbackURL  = errors.New("PopOK callback URL is invalid")
	ErrInvalidVeliAPIURL        = errors.New("Veli API URL is invalid")
	ErrInvalidVeliOperatorKey   = errors.New("Veli operator key is invalid")
	ErrInvalidVeliSecretKey     = errors.New("Veli secret key is invalid")
	ErrMissingResendApiKey      = errors.New("missing Resend Api key")
	ErrMissingResendSenderEmail = errors.New("missing Resend sender name")
)

type AleaPlayConfig struct {
	Enabled         bool   `mapstructure:"enabled"`
	BaseURL         string `mapstructure:"base_url"`         // "https://api.aleaplay.com"
	OperatorID      string `mapstructure:"operator_id"`      // Your operator ID with Alea
	SecretKey       string `mapstructure:"secret_key"`       // API secret for signatures
	GameListURL     string `mapstructure:"game_list_url"`    // Endpoint to fetch available games
	DefaultCurrency string `mapstructure:"default_currency"` // "USD", "EUR", etc.
	SessionTimeout  int    `mapstructure:"session_timeout"`  // In hours
}

type VeliGamesConfig struct {
	Enabled         bool   `mapstructure:"enabled"`
	APIURL          string `mapstructure:"api_url"`
	OperatorKey     string `mapstructure:"operator_key"`
	SecretKey       string `mapstructure:"secret_key"`
	DefaultCurrency string `mapstructure:"default_currency"`
	GameIDs         struct {
		Aviator string `mapstructure:"aviator"`
	} `mapstructure:"game_ids"`
}

type Config struct {
	FIXER_API_KEY                  string
	FIXER_BASE_URL                 string
	BASE_CURRENCY                  domain.IntCurrency
	Port                           int
	DbUrl                          string
	RefreshExpiry                  int
	AccessExpiry                   int
	JwtKey                         string
	LogLevel                       slog.Level
	Env                            string
	ReportExportPath               string `mapstructure:"REPORT_EXPORT_PATH"`
	AFRO_SMS_API_KEY               string
	AFRO_SMS_SENDER_NAME           string
	AFRO_SMS_RECEIVER_PHONE_NUMBER string
	ADRO_SMS_HOST_URL              string
	CHAPA_TRANSFER_TYPE            string
	CHAPA_PAYMENT_TYPE             string
	CHAPA_SECRET_KEY               string
	CHAPA_PUBLIC_KEY               string
	CHAPA_BASE_URL                 string
	CHAPA_ENCRYPTION_KEY           string
	CHAPA_CALLBACK_URL             string
	CHAPA_RETURN_URL               string
	Bet365Token                    string
	PopOK                          domain.PopOKConfig
	AleaPlay                       AleaPlayConfig  `mapstructure:"alea_play"`
	VeliGames                      VeliGamesConfig `mapstructure:"veli_games"`
	ResendApiKey                   string
	ResendSenderEmail              string
}

func NewConfig() (*Config, error) {
	config := &Config{}
	if err := config.loadEnv(); err != nil {
		return nil, err
	}
	return config, nil
}

func (c *Config) loadEnv() error {
	err := godotenv.Load()
	if err != nil && !os.IsNotExist(err) {
		return errors.New("failed to load env file: " + err.Error())
	}

	env := os.Getenv("ENV")
	if env == "" {
		return ErrInvalidEnv
	}
	c.Env = env

	c.ReportExportPath = os.Getenv("REPORT_EXPORT_PATH")

	c.CHAPA_TRANSFER_TYPE = os.Getenv("CHAPA_TRANSFER_TYPE")
	c.CHAPA_PAYMENT_TYPE = os.Getenv("CHAPA_PAYMENT_TYPE")

	c.FIXER_API_KEY = os.Getenv("FIXER_API_KEY")
	c.BASE_CURRENCY = domain.IntCurrency(os.Getenv("BASE_CURRENCY"))
	c.FIXER_BASE_URL = os.Getenv("FIXER_BASE_URL")

	portStr := os.Getenv("PORT")
	if portStr == "" {
		return ErrInvalidPort
	}
	port, err := strconv.Atoi(portStr)
	if err != nil || port < 1 || port > 65535 {
		return ErrInvalidPort
	}
	c.Port = port

	dbUrl := os.Getenv("DB_URL")
	if dbUrl == "" {
		return ErrInvalidDbUrl
	}
	c.DbUrl = dbUrl

	refreshExpiryStr := os.Getenv("REFRESH_EXPIRY")
	if refreshExpiryStr == "" {
		return ErrRefreshExpiry
	}
	refreshExpiry, err := strconv.Atoi(refreshExpiryStr)
	if err != nil || refreshExpiry <= 0 {
		return ErrRefreshExpiry
	}
	c.RefreshExpiry = refreshExpiry

	jwtKey := os.Getenv("JWT_KEY")
	if jwtKey == "" {
		return ErrInvalidJwtKey
	}
	c.JwtKey = jwtKey

	accessExpiryStr := os.Getenv("ACCESS_EXPIRY")
	if accessExpiryStr == "" {
		return ErrAccessExpiry
	}
	accessExpiry, err := strconv.Atoi(accessExpiryStr)
	if err != nil || accessExpiry <= 0 {
		return ErrAccessExpiry
	}
	c.AccessExpiry = accessExpiry

	logLevel := os.Getenv("LOG_LEVEL")
	if logLevel == "" {
		return ErrLogLevel
	}

	lvl, ok := customlogger.LogLevels[logLevel]
	if !ok {
		return ErrInvalidLevel
	}

	//Chapa
	c.CHAPA_SECRET_KEY = os.Getenv("CHAPA_SECRET_KEY")
	c.CHAPA_PUBLIC_KEY = os.Getenv("CHAPA_PUBLIC_KEY")
	c.CHAPA_ENCRYPTION_KEY = os.Getenv("CHAPA_ENCRYPTION_KEY")
	c.CHAPA_BASE_URL = os.Getenv("CHAPA_BASE_URL")
	if c.CHAPA_BASE_URL == "" {
		c.CHAPA_BASE_URL = "https://api.chapa.co/v1"
	}
	c.CHAPA_CALLBACK_URL = os.Getenv("CHAPA_CALLBACK_URL")
	c.CHAPA_RETURN_URL = os.Getenv("CHAPA_RETURN_URL")

	//Alea Play
	aleaEnabled := os.Getenv("ALEA_ENABLED")
	if aleaEnabled == "" {
		aleaEnabled = "false" // Default disabled
	}

	if enabled, err := strconv.ParseBool(aleaEnabled); err != nil {
		return fmt.Errorf("invalid ALEA_ENABLED value: %w", err)
	} else {
		c.AleaPlay.Enabled = enabled
	}

	c.AleaPlay.BaseURL = os.Getenv("ALEA_BASE_URL")
	if c.AleaPlay.BaseURL == "" && c.AleaPlay.Enabled {
		return errors.New("ALEA_BASE_URL is required when Alea is enabled")
	}

	c.AleaPlay.OperatorID = os.Getenv("ALEA_OPERATOR_ID")
	if c.AleaPlay.OperatorID == "" && c.AleaPlay.Enabled {
		return errors.New("ALEA_OPERATOR_ID is required when Alea is enabled")
	}

	c.AleaPlay.SecretKey = os.Getenv("ALEA_SECRET_KEY")
	if c.AleaPlay.SecretKey == "" && c.AleaPlay.Enabled {
		return errors.New("ALEA_SECRET_KEY is required when Alea is enabled")
	}

	c.AleaPlay.GameListURL = os.Getenv("ALEA_GAME_LIST_URL")
	c.AleaPlay.DefaultCurrency = os.Getenv("ALEA_DEFAULT_CURRENCY")
	if c.AleaPlay.DefaultCurrency == "" {
		c.AleaPlay.DefaultCurrency = "USD"
	}

	sessionTimeoutStr := os.Getenv("ALEA_SESSION_TIMEOUT")
	if sessionTimeoutStr != "" {
		timeout, err := strconv.Atoi(sessionTimeoutStr)
		if err == nil {
			c.AleaPlay.SessionTimeout = timeout
		}
	}

	//Veli Games
	veliEnabled := os.Getenv("VELI_ENABLED")
	if veliEnabled == "" {
		veliEnabled = "false" // Default to disabled if not specified
	}

	if enabled, err := strconv.ParseBool(veliEnabled); err != nil {
		return fmt.Errorf("invalid VELI_ENABLED value: %w", err)
	} else {
		c.VeliGames.Enabled = enabled
	}

	apiURL := os.Getenv("VELI_API_URL")
	if apiURL == "" {
		apiURL = "https://api.velitech.games" // Default production URL
	}
	c.VeliGames.APIURL = apiURL

	operatorKey := os.Getenv("VELI_OPERATOR_KEY")
	if operatorKey == "" && c.VeliGames.Enabled {
		return ErrInvalidVeliOperatorKey
	}
	c.VeliGames.OperatorKey = operatorKey

	secretKey := os.Getenv("VELI_SECRET_KEY")
	if secretKey == "" && c.VeliGames.Enabled {
		return ErrInvalidVeliSecretKey
	}
	c.VeliGames.SecretKey = secretKey
	c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR")

	defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY")
	if defaultCurrency == "" {
		defaultCurrency = "USD" // Default currency
	}
	c.VeliGames.DefaultCurrency = defaultCurrency

	c.LogLevel = lvl

	c.AFRO_SMS_API_KEY = os.Getenv("AFRO_SMS_API_KEY")
	if c.AFRO_SMS_API_KEY == "" {
		return ErrInvalidSMSAPIKey
	}

	c.AFRO_SMS_SENDER_NAME = os.Getenv("AFRO_SMS_SENDER_NAME")
	if c.AFRO_SMS_SENDER_NAME == "" {
		c.AFRO_SMS_SENDER_NAME = "FortuneBet"
	}

	c.AFRO_SMS_RECEIVER_PHONE_NUMBER = os.Getenv("AFRO_SMS_RECEIVER_PHONE_NUMBER")

	c.ADRO_SMS_HOST_URL = os.Getenv("ADRO_SMS_HOST_URL")
	if c.ADRO_SMS_HOST_URL == "" {
		c.ADRO_SMS_HOST_URL = "https://api.afrosms.com"
	}

	popOKClientID := os.Getenv("POPOK_CLIENT_ID")

	popOKPlatform := os.Getenv("POPOK_PLATFORM")

	if popOKClientID == "" {
		return ErrInvalidPopOKClientID
	}

	popOKSecretKey := os.Getenv("POPOK_SECRET_KEY")
	if popOKSecretKey == "" {
		return ErrInvalidPopOKSecretKey
	}

	popOKBaseURL := os.Getenv("POPOK_BASE_URL")
	if popOKBaseURL == "" {
		return ErrInvalidPopOKBaseURL
	}

	popOKCallbackURL := os.Getenv("POPOK_CALLBACK_URL")
	if popOKCallbackURL == "" {
		return ErrInvalidPopOKCallbackURL
	}

	c.PopOK = domain.PopOKConfig{
		ClientID:    popOKClientID,
		SecretKey:   popOKSecretKey,
		BaseURL:     popOKBaseURL,
		CallbackURL: popOKCallbackURL,
		Platform:    popOKPlatform,
	}
	betToken := os.Getenv("BET365_TOKEN")
	if betToken == "" {
		return ErrMissingBetToken
	}
	c.Bet365Token = betToken

	resendApiKey := os.Getenv("RESEND_API_KEY")
	if resendApiKey == "" {
		return ErrMissingResendApiKey
	}
	c.ResendApiKey = resendApiKey

	resendSenderEmail := os.Getenv("RESEND_SENDER_EMAIL")
	if resendSenderEmail == "" {
		return ErrMissingResendSenderEmail
	}
	c.ResendSenderEmail = resendSenderEmail

	return nil
}

type ChapaConfig struct {
	ChapaPaymentType  string `mapstructure:"chapa_payment_type"`
	ChapaTransferType string `mapstructure:"chapa_transfer_type"`
}
