package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	_ "git.xeserv.us/xena/route/internal"
	"git.xeserv.us/xena/route/internal/middleware"
	"git.xeserv.us/xena/route/internal/routecrypto"
	"github.com/Xe/ln"
	"github.com/caarlos0/env"
	"github.com/facebookgo/flagenv"
	_ "github.com/joho/godotenv/autoload"
	"github.com/lucas-clemente/quic-go/h2quic"
)

var (
	sslCertKey = flag.String("ssl-cert-key", "", "if set encrypt SSL certs with this key")
)

func main() {
	flag.Parse()
	flagenv.Parse()
	rand.Seed(time.Now().Unix())

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	certKey, err := routecrypto.ParseKey(*sslCertKey)
	if err != nil {
		ln.FatalErr(ctx, err, ln.Action("parse cert key"))
	}

	scfg := Config{}
	err = env.Parse(&scfg)
	if err != nil {
		ln.FatalErr(ctx, err, ln.Action("parsing environment for config"))
	}
	scfg.CertKey = certKey

	s, err := New(scfg)
	if err != nil {
		ln.FatalErr(ctx, err, ln.Action("create server instance"))
	}

	go setupQuic(ctx, s, scfg)
	go setupTLS(ctx, s, scfg)
	go setupHTTP(ctx, s, scfg)

	ch := make(chan os.Signal, 2)

	go func() {
		val := <-ch

		ln.Log(ctx, ln.F{"signal": val.String()}, ln.Action("signal recieved"))
		cancel()
	}()

	signal.Notify(ch, os.Interrupt)

	<-ctx.Done()

	fmt.Printf("%s is now waiting for final shutdown, press ^C again to kill it now\n", os.Args[0])
	time.Sleep(30 * time.Second)
}

func setupHTTP(ctx context.Context, s *Server, scfg Config) {
	f := ln.F{
		"kind": "http",
		"addr": scfg.WebAddr,
	}

	// listen on HTTP listener
	l, err := net.Listen("tcp", scfg.WebAddr)
	if err != nil {
		ln.FatalErr(ctx, err, f, ln.Action("listening on HTTP port"))
	}
	defer l.Close()

	hs := &http.Server{
		Handler: s.Manager.HTTPHandler(http.HandlerFunc(insecureRedirect)),
		Addr:    scfg.WebAddr,
	}

	go ln.FatalErr(ctx, hs.Serve(l))
	ln.Log(ctx, f, ln.Action("http server listen"))

	for {
		select {
		case <-ctx.Done():
			ln.Log(ctx, f, ln.Action("shutdown signal recieved"))
			hs.SetKeepAlivesEnabled(false)

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

			ln.Log(ctx, f, ln.Action("shutdown complete"))
			return
		}
	}
}

func setupQuic(ctx context.Context, s *Server, scfg Config) {
	f := ln.F{
		"kind": "quic",
		"addr": scfg.QuicAddr,
	}

	qs := &h2quic.Server{
		Server: &http.Server{
			Handler: middleware.Trace("http-quic")(s),
			Addr:    scfg.QuicAddr,
			TLSConfig: &tls.Config{
				GetCertificate: s.GetCertificate,
			},

			IdleTimeout:       5 * time.Minute,
			ReadHeaderTimeout: time.Second,
		},
	}

	s.QuicServer = qs

	go ln.FatalErr(context.Background(), qs.ListenAndServe())
	ln.Log(ctx, f, ln.Action("http server listen"))

	for {
		select {
		case <-ctx.Done():
			ln.Log(ctx, f, ln.Action("shutdown signal recieved"))
			qs.SetKeepAlivesEnabled(false)

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

			ln.Log(ctx, f, ln.Action("shutdown complete"))
			return
		}
	}
}

func setupTLS(ctx context.Context, s *Server, scfg Config) {
	f := ln.F{
		"kind": "https",
		"addr": scfg.SSLAddr,
	}

	hs := &http.Server{
		Handler: middleware.Trace("https")(s),
		Addr:    scfg.SSLAddr,
		TLSConfig: &tls.Config{
			GetCertificate: s.GetCertificate,
		},

		IdleTimeout:       5 * time.Minute,
		ReadHeaderTimeout: time.Second,
	}

	go ln.FatalErr(context.Background(), hs.ListenAndServeTLS("", ""))
	ln.Log(ctx, f, ln.Action("http server listen"))

	for {
		select {
		case <-ctx.Done():
			ln.Log(ctx, f, ln.Action("shutdown signal recieved"))
			hs.SetKeepAlivesEnabled(false)

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

			ln.Log(ctx, f, ln.Action("shutdown complete"))
			return
		}
	}
}
