package app

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	defaultAppMode         = "development"
	defaultAPIPort         = "8080"
	defaultShutdownTimeout = 10 * time.Second
)

var (
	defaultLocalCORSOrigins = []string{
		"http://localhost:3000",
		"http://127.0.0.1:3000",
		"http://localhost:3001",
		"http://127.0.0.1:3001",
	}
	insecureSecretPlaceholders = map[string]struct{}{
		"":                                     {},
		"replace-me-with-a-long-random-secret": {},
		"replace-me-with-a-dedicated-mail-secret": {},
		"productier-local-mail-key":               {},
		"changeme":                                {},
		"change-me":                               {},
		"replace-me":                              {},
	}
)

type runtimeConfig struct {
	mode             string
	apiPort          string
	authServiceURL   string
	shutdownTimeout  time.Duration
	corsAllowOrigins []string
	mailSecret       string
	metricsAuthToken string
}

func loadRuntimeConfig() (runtimeConfig, error) {
	mode, err := parseAppMode(os.Getenv("APP_ENV"))
	if err != nil {
		return runtimeConfig{}, err
	}

	apiPort, err := parsePort(os.Getenv("API_PORT"), defaultAPIPort, "API_PORT")
	if err != nil {
		return runtimeConfig{}, err
	}

	authServiceURL, err := parseAbsoluteHTTPURL(
		valueOrDefault(strings.TrimSpace(os.Getenv("AUTH_SERVICE_URL")), "http://localhost:3001"),
		"AUTH_SERVICE_URL",
	)
	if err != nil {
		return runtimeConfig{}, err
	}

	shutdownTimeout, err := parseDuration(
		os.Getenv("API_SHUTDOWN_TIMEOUT"),
		defaultShutdownTimeout,
		"API_SHUTDOWN_TIMEOUT",
	)
	if err != nil {
		return runtimeConfig{}, err
	}

	corsAllowOrigins, err := parseCORSAllowOrigins(mode, os.Getenv("CORS_ALLOW_ORIGINS"))
	if err != nil {
		return runtimeConfig{}, err
	}

	mailSecret, err := resolveMailSecret(mode)
	if err != nil {
		return runtimeConfig{}, err
	}

	metricsAuthToken, err := resolveMetricsAuthToken(mode)
	if err != nil {
		return runtimeConfig{}, err
	}

	return runtimeConfig{
		mode:             mode,
		apiPort:          apiPort,
		authServiceURL:   authServiceURL,
		shutdownTimeout:  shutdownTimeout,
		corsAllowOrigins: corsAllowOrigins,
		mailSecret:       mailSecret,
		metricsAuthToken: metricsAuthToken,
	}, nil
}

func parseAppMode(raw string) (string, error) {
	mode := strings.TrimSpace(strings.ToLower(raw))
	if mode == "" {
		return defaultAppMode, nil
	}
	switch mode {
	case "development", "test", "staging", "production":
		return mode, nil
	default:
		return "", fmt.Errorf("unsupported APP_ENV %q (allowed: development, test, staging, production)", mode)
	}
}

func parsePort(raw string, fallback string, envName string) (string, error) {
	port := strings.TrimSpace(raw)
	if port == "" {
		port = fallback
	}
	numeric, err := strconv.Atoi(port)
	if err != nil || numeric < 1 || numeric > 65535 {
		return "", fmt.Errorf("%s must be a valid TCP port (1-65535)", envName)
	}
	return strconv.Itoa(numeric), nil
}

func parseDuration(raw string, fallback time.Duration, envName string) (time.Duration, error) {
	value := strings.TrimSpace(raw)
	if value == "" {
		return fallback, nil
	}
	duration, err := time.ParseDuration(value)
	if err != nil {
		return 0, fmt.Errorf("%s must be a valid duration (example: 10s): %w", envName, err)
	}
	if duration <= 0 {
		return 0, fmt.Errorf("%s must be greater than zero", envName)
	}
	return duration, nil
}

func parseAbsoluteHTTPURL(raw string, envName string) (string, error) {
	value := strings.TrimSpace(raw)
	if value == "" {
		return "", fmt.Errorf("%s is required", envName)
	}
	parsed, err := url.Parse(value)
	if err != nil {
		return "", fmt.Errorf("%s must be a valid URL: %w", envName, err)
	}
	if parsed.Scheme != "http" && parsed.Scheme != "https" {
		return "", fmt.Errorf("%s must use http or https", envName)
	}
	if parsed.Host == "" {
		return "", fmt.Errorf("%s must include a host", envName)
	}
	return strings.TrimRight(parsed.String(), "/"), nil
}

func parseCORSAllowOrigins(mode string, raw string) ([]string, error) {
	value := strings.TrimSpace(raw)
	if value == "" {
		if mode == "staging" || mode == "production" {
			return nil, errors.New("CORS_ALLOW_ORIGINS is required in staging/production (comma-separated origins)")
		}
		return append([]string(nil), defaultLocalCORSOrigins...), nil
	}

	parts := strings.Split(value, ",")
	origins := make([]string, 0, len(parts))
	seen := make(map[string]struct{}, len(parts))
	for _, part := range parts {
		origin := strings.TrimSpace(part)
		if origin == "" {
			continue
		}
		if origin == "*" {
			return nil, errors.New("CORS_ALLOW_ORIGINS cannot include '*' when credentials are enabled")
		}
		validated, err := parseOrigin(origin, "CORS_ALLOW_ORIGINS")
		if err != nil {
			return nil, err
		}
		if _, exists := seen[validated]; exists {
			continue
		}
		seen[validated] = struct{}{}
		origins = append(origins, validated)
	}
	if len(origins) == 0 {
		return nil, errors.New("CORS_ALLOW_ORIGINS must include at least one valid origin")
	}
	return origins, nil
}

func parseOrigin(raw string, envName string) (string, error) {
	parsed, err := url.Parse(strings.TrimSpace(raw))
	if err != nil {
		return "", fmt.Errorf("%s must contain valid origins: %w", envName, err)
	}
	if parsed.Scheme != "http" && parsed.Scheme != "https" {
		return "", fmt.Errorf("%s origins must use http or https", envName)
	}
	if parsed.Host == "" {
		return "", fmt.Errorf("%s origins must include a host", envName)
	}
	if parsed.Path != "" && parsed.Path != "/" {
		return "", fmt.Errorf("%s origins cannot include URL paths", envName)
	}
	if parsed.RawQuery != "" || parsed.Fragment != "" {
		return "", fmt.Errorf("%s origins cannot include query or fragment components", envName)
	}
	return parsed.Scheme + "://" + parsed.Host, nil
}

func resolveMailSecret(mode string) (string, error) {
	mailSecret := strings.TrimSpace(os.Getenv("MAIL_ENCRYPTION_KEY"))
	if mailSecret == "" {
		mailSecret = strings.TrimSpace(os.Getenv("BETTER_AUTH_SECRET"))
	}

	if mode != "staging" && mode != "production" {
		return mailSecret, nil
	}
	if isInsecureSecret(mailSecret) {
		return "", errors.New("set a strong MAIL_ENCRYPTION_KEY (or BETTER_AUTH_SECRET fallback) for staging/production")
	}
	return mailSecret, nil
}

func resolveMetricsAuthToken(mode string) (string, error) {
	token := strings.TrimSpace(os.Getenv("METRICS_AUTH_TOKEN"))
	if token == "" {
		return "", nil
	}

	if mode == "production" && isInsecureSecret(token) {
		return "", errors.New("METRICS_AUTH_TOKEN must be a strong non-placeholder secret when set in production")
	}

	return token, nil
}

func isInsecureSecret(secret string) bool {
	normalized := strings.TrimSpace(strings.ToLower(secret))
	if _, exists := insecureSecretPlaceholders[normalized]; exists {
		return true
	}
	return len(strings.TrimSpace(secret)) < 16
}

func valueOrDefault(value string, fallback string) string {
	if strings.TrimSpace(value) == "" {
		return fallback
	}
	return value
}
