package launcherserver

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"sync"
	"time"

	"erupe-ce/config"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
)

// Config struct allows configuring the server.
type Config struct {
	Logger                   *zap.Logger
	DB                       *sqlx.DB
	ErupeConfig              *config.Config
	UseOriginalLauncherFiles bool
}

// Server is the MHF launcher HTTP server.
type Server struct {
	sync.Mutex
	logger                   *zap.Logger
	erupeConfig              *config.Config
	db                       *sqlx.DB
	httpServer               *http.Server
	useOriginalLauncherFiles bool
	isShuttingDown           bool
}

// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
	s := &Server{
		logger:                   config.Logger,
		erupeConfig:              config.ErupeConfig,
		db:                       config.DB,
		useOriginalLauncherFiles: config.UseOriginalLauncherFiles,
		httpServer:               &http.Server{},
	}
	return s
}

// Start starts the server in a new goroutine.
func (s *Server) Start() error {
	// Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth.
	r := mux.NewRouter()

	// Universal serverlist.xml route
	s.setupServerlistRoutes(r)

	// Change the launcher HTML routes if we are using the custom launcher instead of the original.
	if s.useOriginalLauncherFiles {
		s.setupOriginalLauncherRotues(r)
	} else {
		s.setupCustomLauncherRotues(r)
	}

	s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.Launcher.Port)
	s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, r)

	serveError := make(chan error, 1)
	go func() {
		if err := s.httpServer.ListenAndServe(); err != nil {
			// Send error if any.
			serveError <- err
		}
	}()

	// Get the error from calling ListenAndServe, otherwise assume it's good after 250 milliseconds.
	select {
	case err := <-serveError:
		return err
	case <-time.After(250 * time.Millisecond):
		return nil
	}
}

// Shutdown exits the server gracefully.
func (s *Server) Shutdown() {
	s.logger.Debug("Shutting down")

	s.Lock()
	s.isShuttingDown = true
	s.Unlock()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := s.httpServer.Shutdown(ctx); err != nil {
		// Just warn because we are shutting down the server anyway.
		s.logger.Warn("Got error on httpServer shutdown", zap.Error(err))
	}
}
