// Package config provides application configuration loading and access.
package config

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

type EpisodeAvailabilityMode string

const (
	EpisodeAvailabilityModeAuto   EpisodeAvailabilityMode = "auto"
	EpisodeAvailabilityModeLegacy EpisodeAvailabilityMode = "legacy"
	EpisodeAvailabilityModeJikan  EpisodeAvailabilityMode = "jikan"
)

type Config struct {
	Port string

	// GinMode maps to gin.SetMode. When empty, the server uses release mode by default.
	GinMode string

	DatabaseFile string

	// Allow any Origin for CORS. Intended for local dev / reverse proxy setups only.
	CORSAllowAll bool

	EpisodeAvailabilityMode EpisodeAvailabilityMode

	// Optional. When empty, proxy token signing is disabled.
	PlaybackProxySecret string

	// Optional debug toggle for Jikan client tracing.
	JikanTrace bool
}

func Load() (Config, error) {
	cfg := Config{
		Port:                    firstNonEmpty(strings.TrimSpace(os.Getenv("PORT")), "3000"),
		GinMode:                 strings.TrimSpace(os.Getenv("GIN_MODE")),
		DatabaseFile:            firstNonEmpty(strings.TrimSpace(os.Getenv("DATABASE_FILE")), "mal.db"),
		CORSAllowAll:            strings.TrimSpace(os.Getenv("MAL_CORS_ALLOW_ALL")) == "1",
		PlaybackProxySecret:     strings.TrimSpace(os.Getenv("PLAYBACK_PROXY_SECRET")),
		JikanTrace:              truthy(strings.TrimSpace(os.Getenv("MAL_JIKAN_TRACE"))),
		EpisodeAvailabilityMode: EpisodeAvailabilityModeAuto,
	}

	if raw := strings.ToLower(strings.TrimSpace(os.Getenv("EPISODE_AVAILABILITY_MODE"))); raw != "" {
		switch EpisodeAvailabilityMode(raw) {
		case EpisodeAvailabilityModeAuto, EpisodeAvailabilityModeLegacy, EpisodeAvailabilityModeJikan:
			cfg.EpisodeAvailabilityMode = EpisodeAvailabilityMode(raw)
		default:
			return Config{}, fmt.Errorf("invalid EPISODE_AVAILABILITY_MODE: %q (expected auto|legacy|jikan)", raw)
		}
	}

	if strings.TrimSpace(cfg.Port) == "" {
		return Config{}, errors.New("PORT must not be empty")
	}
	if strings.TrimSpace(cfg.DatabaseFile) == "" {
		return Config{}, errors.New("DATABASE_FILE must not be empty")
	}

	return cfg, nil
}

func firstNonEmpty(values ...string) string {
	for _, v := range values {
		if strings.TrimSpace(v) != "" {
			return v
		}
	}
	return ""
}

func truthy(v string) bool {
	switch strings.ToLower(strings.TrimSpace(v)) {
	case "1", "true", "yes", "y", "on":
		return true
	default:
		return false
	}
}
