package main

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

// Config menyimpan seluruh pengaturan layanan pemendek URL.
type Config struct {
	// Port adalah nomor port TCP tempat server HTTP mendengarkan.
	Port string
	// BaseURL adalah alamat publik layanan, misalnya https://short.example.com.
	// Short URL dibangun dari nilai ini.
	BaseURL string
	// DatabaseURL adalah connection string PostgreSQL.
	DatabaseURL string
}

// Nilai default ketika environment variable tidak disetel.
const (
	defaultPort    = "8080"
	defaultBaseURL = "http://localhost:8080"
)

// Load membaca environment variable, menerapkan default, lalu
// memvalidasi hasilnya secara fail-fast sebelum server dibangun.
func Load() (Config, error) {
	cfg := Config{
		Port:        envOr("APP_PORT", defaultPort),
		BaseURL:     strings.TrimSuffix(envOr("APP_BASE_URL", defaultBaseURL), "/"),
		DatabaseURL: firstNonEmpty(os.Getenv("TEST_DATABASE_URL"), os.Getenv("DATABASE_URL")),
	}

	if cfg.DatabaseURL == "" {
		return cfg, errors.New("DATABASE_URL kosong: setel misalnya DATABASE_URL=postgres://user:pass@localhost:5432/urls?sslmode=disable")
	}
	if err := cekPort(cfg.Port); err != nil {
		return cfg, err
	}
	if err := cekBaseURL(cfg.BaseURL); err != nil {
		return cfg, err
	}
	return cfg, nil
}

// envOr mengembalikan nilai environment variable name, atau fallback
// ketika variabel tersebut tidak disetel atau kosong.
func envOr(name, fallback string) string {
	if v := os.Getenv(name); v != "" {
		return v
	}
	return fallback
}

// firstNonEmpty mengembalikan string pertama yang tidak kosong.
// TEST_DATABASE_URL diutamakan agar integration test bisa mengarah
// ke database khusus tanpa mengubah DATABASE_URL produksi.
func firstNonEmpty(values ...string) string {
	for _, v := range values {
		if v != "" {
			return v
		}
	}
	return ""
}

// cekPort memastikan port berupa bilangan bulat antara 1 dan 65535.
func cekPort(port string) error {
	n, err := strconv.Atoi(port)
	if err != nil {
		return fmt.Errorf("APP_PORT tidak valid: %q bukan bilangan bulat", port)
	}
	if n < 1 || n > 65535 {
		return fmt.Errorf("APP_PORT tidak valid: %d di luar rentang 1-65535", n)
	}
	return nil
}

// cekBaseURL memastikan BaseURL bisa diparse dan memakai skema http
// atau https; short URL yang dibangun dari nilai lain akan rusak.
func cekBaseURL(base string) error {
	u, err := url.Parse(base)
	if err != nil {
		return fmt.Errorf("APP_BASE_URL tidak valid: %v", err)
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return fmt.Errorf("APP_BASE_URL tidak valid: skema harus http atau https, didapat %q", u.Scheme)
	}
	if u.Host == "" {
		return errors.New("APP_BASE_URL tidak valid: host kosong")
	}
	return nil
}
