package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	"code.osinet.fr/fgm/kurz/web"
	"github.com/spf13/viper"

	"code.osinet.fr/fgm/kurz/api"
	"code.osinet.fr/fgm/kurz/domain"
	"code.osinet.fr/fgm/kurz/infrastructure"
	"github.com/gorilla/mux"
	"github.com/spf13/cobra"
)

var cmdServe = &cobra.Command{
	Args:  cobra.NoArgs,
	Long:  "Start HTTP Server",
	Run:   serveHandler,
	Short: "Top-level command for HTTP Serving.",
	Use:   "serve",
}

// db is the database connection shared by "serve *" commands.
var db *sql.DB

func init() {
	cmd.AddCommand(cmdServe)
}

func ensureInfrastructure(db *sql.DB) *sql.DB {
	if db != nil {
		err := db.Ping()
		if err != nil {
			db = nil
		}
	}

	if db != nil {
		return db
	}

	dbDriver, dbDsn := infrastructure.ParseDbCred()
	db, err := infrastructure.DbDial(dbDriver, dbDsn)
	if err != nil {
		panic(err)
	}

	domain.RegisterRepositories(
		infrastructure.MySQLShortURLRepository{DB: db},
		infrastructure.MySQLTargetURLRepository{DB: db},
	)

	return db
}

// serveHandler handles Web paths.
func serveHandler(_ *cobra.Command, args []string) {
	db = ensureInfrastructure(db)
	defer db.Close()
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Server initializing in %s\n", cwd)

	// This default is the relative position of the assets from the project root during development.
	viper.SetDefault("web.assetsPath", "web/public")
	assetsPath := viper.Get("web.assetsPath").(string)

	siteBaseURL := viper.Get("web.siteBaseUrl").(string)

	viper.SetDefault("web.assetsBaseURL", siteBaseURL)
	assetsBaseURL := viper.Get("web.assetsBaseUrl").(string)

	webConfig := viper.Get("web").(map[string]interface{})
	router := mux.NewRouter()
	api.SetupRoutes(router)
	web.SetupRoutes(router, siteBaseURL, assetsBaseURL, assetsPath)
	web.BuildGlobals(webConfig)
	http.Handle("/", router)

	listenAddress := viper.Get("web.listenAddress").(string)

	// Start a server that can handle a SIGINT to shutdown.
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt)

	server := &http.Server{Addr: listenAddress, Handler: router}
	go func() {
		log.Printf("Listening on %s, exposed on %s", listenAddress, siteBaseURL)
		err := server.ListenAndServe()
		log.Fatal(err)
	}()
	<-stop

	log.Println("Shutting down server")
	ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
	server.Shutdown(ctx)
	log.Println("Server gracefully stopped")
}