package config

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"bookra/apps/backend/internal/shared"
)

type Config struct {
	Environment          string
	Port                 string
	APIURL               string
	FrontendURL          string
	DatabaseURL          string
	DatabaseDirectURL    string
	NeonAuthURL          string
	AuthJWTSecret        string
	JobRunnerKey         string
	EmailFrom            string
	SMTPHost             string
	SMTPPort             string
	SMTPUsername         string
	SMTPPassword         string
	PaddleEnvironment    string
	PaddleAPIKey         string
	PaddleWebhookKey     string
	PaddlePriceMatrix    map[string]map[string]string
	StripeAPIKey         string
	StripeWebhookKey     string
	StripePriceMatrix    map[string]map[string]string
	AdminEmail           string
	AdminKey             string
	UmamiAPIURL          string
	UmamiAPIKey          string
	SentryDSN            string
	DemoMode             bool
	SMSManagerAPIKey     string
	SMSManagerBaseURL    string
	StripeSMSPriceMatrix map[string]string // currency -> price ID (czk, usd, eur, gbp, ...)
}

func Load() (Config, error) {
	cfg := Config{
		Environment:          valueOrDefault("BOOKRA_APP_ENV", "development"),
		Port:                 valueOrDefault("BOOKRA_API_PORT", "8080"),
		APIURL:               valueOrDefault("BOOKRA_API_URL", "http://localhost:8080"),
		FrontendURL:          valueOrDefault("BOOKRA_FRONTEND_URL", "http://localhost:3000"),
		DatabaseURL:          strings.TrimSpace(os.Getenv("BOOKRA_DATABASE_URL")),
		DatabaseDirectURL:    strings.TrimSpace(os.Getenv("BOOKRA_DATABASE_DIRECT_URL")),
		NeonAuthURL:          strings.TrimSpace(os.Getenv("BOOKRA_NEON_AUTH_URL")),
		AuthJWTSecret:        strings.TrimSpace(os.Getenv("BOOKRA_AUTH_JWT_SECRET")),
		JobRunnerKey:         strings.TrimSpace(os.Getenv("BOOKRA_JOB_RUNNER_KEY")),
		EmailFrom:            valueOrDefault("BOOKRA_EMAIL_FROM", "noreply@bookra.dev"),
		SMTPHost:             strings.TrimSpace(os.Getenv("BOOKRA_SMTP_HOST")),
		SMTPPort:             valueOrDefault("BOOKRA_SMTP_PORT", "587"),
		SMTPUsername:         strings.TrimSpace(os.Getenv("BOOKRA_SMTP_USERNAME")),
		SMTPPassword:         strings.TrimSpace(os.Getenv("BOOKRA_SMTP_PASSWORD")),
		PaddleEnvironment:    normalizePaddleEnvironment(os.Getenv("BOOKRA_PADDLE_ENV")),
		PaddleAPIKey:         strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_API_KEY")),
		PaddleWebhookKey:     strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_WEBHOOK_SECRET")),
		PaddlePriceMatrix:    paddlePriceMatrixFromEnv(),
		StripeAPIKey:         strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_API_KEY")),
		StripeWebhookKey:     strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_WEBHOOK_SECRET")),
		StripePriceMatrix:    stripePriceMatrixFromEnv(),
		AdminEmail:           strings.TrimSpace(os.Getenv("BOOKRA_ADMIN_EMAIL")),
		AdminKey:             strings.TrimSpace(os.Getenv("BOOKRA_ADMIN_KEY")),
		UmamiAPIURL:          strings.TrimSpace(os.Getenv("BOOKRA_UMAMI_API_URL")),
		UmamiAPIKey:          strings.TrimSpace(os.Getenv("BOOKRA_UMAMI_API_KEY")),
		SentryDSN:            strings.TrimSpace(os.Getenv("BOOKRA_SENTRY_DSN")),
		DemoMode:             boolFromEnv("BOOKRA_DEMO_MODE", false),
		SMSManagerAPIKey:     strings.TrimSpace(os.Getenv("BOOKRA_SMSMANAGER_API_KEY")),
		SMSManagerBaseURL:    valueOrDefault("BOOKRA_SMSMANAGER_BASE_URL", "https://api.smsmngr.com/v2"),
		StripeSMSPriceMatrix: smsPriceMatrixFromEnv(),
	}

	if cfg.FrontendURL == "" {
		return Config{}, errors.New("BOOKRA_FRONTEND_URL is required")
	}
	if err := cfg.validateRuntimeRequirements(); err != nil {
		return Config{}, err
	}

	return cfg, nil
}

func (cfg Config) validateRuntimeRequirements() error {
	if cfg.Environment == "development" || cfg.Environment == "test" {
		return nil
	}

	missing := make([]string, 0, 3)
	if cfg.DatabaseURL == "" {
		missing = append(missing, "BOOKRA_DATABASE_URL")
	}
	if cfg.NeonAuthURL == "" {
		missing = append(missing, "BOOKRA_NEON_AUTH_URL")
	}
	if cfg.JobRunnerKey == "" {
		missing = append(missing, "BOOKRA_JOB_RUNNER_KEY")
	}
	if cfg.SMTPHost == "" {
		missing = append(missing, "BOOKRA_SMTP_HOST")
	}
	if len(missing) > 0 {
		return fmt.Errorf("%s required when BOOKRA_APP_ENV=%s", strings.Join(uniqueStrings(missing), ", "), cfg.Environment)
	}
	return nil
}

func (cfg Config) PaddleConfigured() bool {
	return strings.TrimSpace(cfg.PaddleAPIKey) != ""
}

func (cfg Config) PaddleWebhookConfigured() bool {
	return strings.TrimSpace(cfg.PaddleWebhookKey) != ""
}

func (cfg Config) PaddleCheckoutConfigured(planCode string) bool {
	planCode = shared.NormalizePlanCode(planCode)
	return cfg.PaddleConfigured() && cfg.PaddleWebhookConfigured() && cfg.PaddlePriceMatrix[planCode]["czk"] != "" && cfg.PaddlePriceMatrix[planCode]["usd"] != ""
}

func (cfg Config) StripeConfigured() bool {
	return strings.TrimSpace(cfg.StripeAPIKey) != ""
}

func (cfg Config) StripeWebhookConfigured() bool {
	return strings.TrimSpace(cfg.StripeWebhookKey) != ""
}

func (cfg Config) StripeCheckoutConfigured(planCode string) bool {
	planCode = shared.NormalizePlanCode(planCode)
	return cfg.StripeConfigured() && cfg.StripeWebhookConfigured() && cfg.StripePriceMatrix[planCode]["czk"] != "" && cfg.StripePriceMatrix[planCode]["usd"] != ""
}

func (cfg Config) BillingProvider() string {
	if cfg.StripeConfigured() {
		return "stripe"
	}
	return "paddle"
}

func (cfg Config) BillingConfigured() bool {
	return cfg.StripeConfigured() || cfg.PaddleConfigured()
}

func (cfg Config) BillingWebhookConfigured() bool {
	return cfg.StripeWebhookConfigured() || cfg.PaddleWebhookConfigured()
}

func (cfg Config) SMSConfigured() bool {
	return strings.TrimSpace(cfg.SMSManagerAPIKey) != ""
}

func (cfg Config) StripeSMSConfigured() bool {
	return cfg.StripeConfigured() && cfg.StripeSMSPriceMatrix["czk"] != ""
}

func (cfg Config) StripeSMSPriceID(currency string) string {
	c := strings.ToLower(strings.TrimSpace(currency))
	if c == "" {
		c = "czk"
	}
	if id := cfg.StripeSMSPriceMatrix[c]; id != "" {
		return id
	}
	return cfg.StripeSMSPriceMatrix["czk"]
}

func paddlePriceMatrixFromEnv() map[string]map[string]string {
	matrix := map[string]map[string]string{
		"starter":  {},
		"pro":      {},
		"business": {},
	}
	for _, planCode := range []string{"starter", "pro", "business"} {
		envPlan := strings.ToUpper(strings.ReplaceAll(planCode, "-", "_"))
		matrix[planCode]["czk"] = strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_" + envPlan + "_CZK_PRICE_ID"))
		matrix[planCode]["usd"] = strings.TrimSpace(os.Getenv("BOOKRA_PADDLE_" + envPlan + "_USD_PRICE_ID"))
	}
	return matrix
}

func stripePriceMatrixFromEnv() map[string]map[string]string {
	matrix := map[string]map[string]string{
		"starter":  {},
		"pro":      {},
		"business": {},
	}
	for _, planCode := range []string{"starter", "pro", "business"} {
		envPlan := strings.ToUpper(strings.ReplaceAll(planCode, "-", "_"))

		// Monthly prices
		matrix[planCode][planCode+":czk:monthly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_MONTHLY_PRICE_ID"))
		matrix[planCode][planCode+":usd:monthly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_MONTHLY_PRICE_ID"))
		matrix[planCode][planCode+":czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_PRICE_ID"))
		matrix[planCode][planCode+":usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_PRICE_ID"))
		matrix[planCode]["czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_PRICE_ID"))
		matrix[planCode]["usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_PRICE_ID"))

		// Yearly prices
		matrix[planCode][planCode+":czk:yearly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_YEARLY_PRICE_ID"))
		matrix[planCode][planCode+":usd:yearly"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_YEARLY_PRICE_ID"))
		matrix[planCode]["yearly:czk"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_CZK_YEARLY_PRICE_ID"))
		matrix[planCode]["yearly:usd"] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_" + envPlan + "_USD_YEARLY_PRICE_ID"))
	}
	return matrix
}

func normalizePaddleEnvironment(value string) string {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "live", "production":
		return "live"
	default:
		return "sandbox"
	}
}

func valueOrDefault(key string, fallback string) string {
	if value := strings.TrimSpace(os.Getenv(key)); value != "" {
		return value
	}
	return fallback
}

func boolFromEnv(key string, fallback bool) bool {
	value := strings.TrimSpace(os.Getenv(key))
	if value == "" {
		return fallback
	}
	value = strings.ToLower(value)
	return value == "true" || value == "1" || value == "yes" || value == "on"
}

func smsPriceMatrixFromEnv() map[string]string {
	matrix := map[string]string{}
	for _, currency := range []string{"czk", "usd", "eur", "gbp", "pln"} {
		upper := strings.ToUpper(currency)
		matrix[currency] = strings.TrimSpace(os.Getenv("BOOKRA_STRIPE_SMS_" + upper + "_PRICE_ID"))
	}
	return matrix
}

func uniqueStrings(values []string) []string {
	seen := map[string]struct{}{}
	out := make([]string, 0, len(values))
	for _, value := range values {
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		out = append(out, value)
	}
	return out
}
