// Copyright 2019-2020 Axetroy. All rights reserved. Apache License 2.0.
package app

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/axetroy/wsm/internal/app/config"
	"github.com/axetroy/wsm/internal/app/db"
	"github.com/axetroy/wsm/internal/app/shell"
	"github.com/axetroy/wsm/internal/library/session"
)

func Serve() error {
	port := config.Http.Port

	s := &http.Server{
		Addr:           ":" + port,
		Handler:        Router,
		ReadTimeout:    15 * time.Second,
		WriteTimeout:   15 * time.Second,
		IdleTimeout:    60 * time.Second,
		MaxHeaderBytes: 1 << 20, // 10M
	}

	done := make(chan bool, 1)
	quit := make(chan os.Signal, 1)

	signal.Notify(quit, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	go gracefullShutdown(s, quit, done)

	if config.Http.TLS != nil {
		TLSConfig := &tls.Config{
			MinVersion:               tls.VersionTLS11,
			CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
		}

		TLSProto := make(map[string]func(*http.Server, *tls.Conn, http.Handler))

		s.TLSConfig = TLSConfig
		s.TLSNextProto = TLSProto

		log.Printf("Listen on:  %s\n", s.Addr)

		if err := s.ListenAndServeTLS(config.Http.TLS.Cert, config.Http.TLS.Key); err != nil {
			log.Fatalln(err)
		}
	} else {
		log.Printf("Listen on port:  %s\n", s.Addr)

		if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalln(err)
		}
	}

	<-done
	log.Println("Server stopped")

	shell.SteamMap.ForEach(func(key string, stream *session.WebsocketStream) {
		if stream == nil {
			return
		}

		if err := stream.Write2Log(); err != nil {
			fmt.Println(err)
		}
	})

	_ = db.Db.Close()

	os.Exit(0)

	return nil
}

func gracefullShutdown(server *http.Server, quit <-chan os.Signal, done chan<- bool) {
	<-quit
	log.Println("Server is shutting down...")

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	server.SetKeepAlivesEnabled(false)
	if err := server.Shutdown(ctx); err != nil {
		log.Fatalf("Could not gracefully shutdown the server: %v\n", err)
	}
	close(done)
}
