package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

// Config menyimpan pengaturan server yang dibaca dari lingkungan.
type Config struct {
	Addr         string
	StaticDir    string
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

// ConfigFromEnv membaca konfigurasi dari variabel lingkungan dengan nilai
// bawaan yang aman sehingga server langsung jalan tanpa pengaturan apa pun.
func ConfigFromEnv() Config {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	staticDir := os.Getenv("STATIC_DIR")
	if staticDir == "" {
		staticDir = "static"
	}
	return Config{
		Addr:         ":" + port,
		StaticDir:    staticDir,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
}

// NewServer merakit http.Server lengkap dengan handler hasil middleware chain.
func NewServer(cfg Config, handler http.Handler) *http.Server {
	return &http.Server{
		Addr:         cfg.Addr,
		Handler:      handler,
		ReadTimeout:  cfg.ReadTimeout,
		WriteTimeout: cfg.WriteTimeout,
	}
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "galat:", err)
		os.Exit(1)
	}
}

// run menjalankan server sampai menerima sinyal berhenti, lalu mematikan
// server dengan mulus. Logika dipisah ke fungsi run agar mudah diperiksa.
func run() error {
	cfg := ConfigFromEnv()

	handlers, err := NewHandlers()
	if err != nil {
		return fmt.Errorf("menyiapkan handler: %w", err)
	}

	logger := log.New(os.Stdout, "", 0)
	mux := NewRouter(handlers, cfg.StaticDir)
	handler := Chain(mux,
		NewRequestID(),
		NewLogging(logger.Writer()),
		NewRecover(logger.Writer()),
	)

	srv := NewServer(cfg, handler)

	// signal.NotifyContext membatalkan context ketika proses menerima
	// SIGINT (Ctrl+C) atau SIGTERM (perintah kill).
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()

	errCh := make(chan error, 1)
	go func() {
		// ListenAndServe mengembalikan http.ErrServerClosed setelah
		// Shutdown dipanggil; nilai itu bukan galat sungguhan.
		errCh <- srv.ListenAndServe()
	}()

	logger.Printf("web-server mendengarkan di http://localhost%s", cfg.Addr)

	select {
	case err := <-errCh:
		if err != nil && !errors.Is(err, http.ErrServerClosed) {
			return fmt.Errorf("server berhenti: %w", err)
		}
		return nil
	case <-ctx.Done():
		logger.Print("terima sinyal berhenti, mematikan server dengan mulus")
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := srv.Shutdown(shutdownCtx); err != nil {
			return fmt.Errorf("graceful shutdown gagal: %w", err)
		}
		logger.Print("server berhenti")
		return nil
	}
}
