package main

import (
	"errors"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"

	"context"
	"jokes-bapak2-api/core/joke"
	"jokes-bapak2-api/routes"

	"github.com/go-redis/redis/v8"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"

	"time"

	"github.com/allegro/bigcache/v3"
	"github.com/getsentry/sentry-go"
	"github.com/go-chi/chi/v5"
	"github.com/rs/cors"
)

func main() {
	redisURL, ok := os.LookupEnv("REDIS_URL")
	if !ok {
		redisURL = "redis://@localhost:6379"
	}

	minioHost, ok := os.LookupEnv("MINIO_HOST")
	if !ok {
		minioHost = "localhost:9000"
	}

	minioRegion, ok := os.LookupEnv("MINIO_REGION")
	if !ok {
		minioRegion = ""
	}

	minioSecure, ok := os.LookupEnv("MINIO_SECURE")
	if !ok {
		minioSecure = "false"
	}

	minioID, ok := os.LookupEnv("MINIO_ACCESS_ID")
	if !ok {
		minioID = "minio"
	}

	minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY")
	if !ok {
		minioSecret = "password"
	}

	minioToken, ok := os.LookupEnv("MINIO_TOKEN")
	if !ok {
		minioToken = ""
	}

	sentryDsn, ok := os.LookupEnv("SENTRY_DSN")
	if !ok {
		sentryDsn = ""
	}

	port, ok := os.LookupEnv("PORT")
	if !ok {
		port = "5000"
	}

	hostname, ok := os.LookupEnv("HOSTNAME")
	if !ok {
		hostname = "127.0.0.1"
	}

	environment, ok := os.LookupEnv("ENVIRONMENT")
	if !ok {
		environment = "development"
	}

	// Setup In Memory
	memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
	if err != nil {
		log.Panicln(err)
	}
	defer memory.Close()

	// Setup MinIO
	minioClient, err := minio.New(minioHost, &minio.Options{
		Creds:  credentials.NewStaticV4(minioID, minioSecret, minioToken),
		Region: minioRegion,
		Secure: minioSecure == "true",
	})
	if err != nil {
		log.Fatalf("setting up minio client: %s", err.Error())
		return
	}

	parsedRedisURL, err := redis.ParseURL(redisURL)
	if err != nil {
		log.Fatalf("parsing redis url: %s", err.Error())
		return
	}

	redisClient := redis.NewClient(parsedRedisURL)
	defer func() {
		err := redisClient.Close()
		if err != nil {
			log.Printf("closing redis client: %s", err.Error())
		}
	}()

	// Setup Sentry
	err = sentry.Init(sentry.ClientOptions{
		Dsn:              sentryDsn,
		Environment:      environment,
		AttachStacktrace: true,
		// Enable printing of SDK debug messages.
		// Useful when getting started or trying to figure something out.
		Debug: environment != "production",
	})
	if err != nil {
		log.Fatalf("setting up sentry: %s", err.Error())
		return
	}
	defer sentry.Flush(2 * time.Second)

	setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4))
	defer setupCancel()

	_, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory)
	if err != nil {
		log.Fatalf("getting initial joke data: %s", err.Error())
		return
	}

	healthRouter := routes.Health(minioClient, redisClient)
	jokeRouter := routes.Joke(minioClient, redisClient, memory)

	router := chi.NewRouter()

	router.Use(cors.New(cors.Options{
		AllowedMethods:   []string{http.MethodGet},
		AllowCredentials: false,
		MaxAge:           int(60 * 60 * 24 * 365),
		Debug:            false,
	}).Handler)

	router.Mount("/health", healthRouter)
	router.Mount("/", jokeRouter)

	server := &http.Server{
		Handler:           router,
		Addr:              net.JoinHostPort(hostname, port),
		ReadTimeout:       time.Minute,
		WriteTimeout:      time.Minute,
		IdleTimeout:       time.Second * 30,
		ReadHeaderTimeout: time.Minute,
	}

	exitSignal := make(chan os.Signal, 1)
	signal.Notify(exitSignal, os.Interrupt)

	go func() {
		err := server.ListenAndServe()
		if err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatalf("listening http server: %v", err)
		}
	}()

	<-exitSignal

	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30)
	defer shutdownCancel()

	err = server.Shutdown(shutdownCtx)
	if err != nil {
		log.Printf("shutting down http server: %v", err)
	}
}
