package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"src.whiteboxsystems.nl/decozo/okapi"
	"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
)

var rpcPort = "8888"
var srvaddr = "0.0.0.0:" + rpcPort
var extRpcAddr = "localhost:" + rpcPort
var patientIf = "0.0.0.0:8085"

var binFolder = "./bin/arm64"

func loadCert(name string) *tls.Certificate {
	_, err := os.Stat(fmt.Sprintf("certs/%s.crt", name))

	if err != nil {
		_, _, certPem, keyPem, err := cryptoutil.GenCert("whitebox", "whitebox")

		if err != nil {
			panic(err)
		}

		if err != nil {
			panic(err)
		}

		if err := ioutil.WriteFile(fmt.Sprintf("certs/%s.crt", name), []byte(certPem), 0600); err != nil {
			panic(err)
		}
		if err := ioutil.WriteFile(fmt.Sprintf("certs/%s.key", name), []byte(keyPem), 0600); err != nil {
			panic(err)
		}
	}

	certificate, err := tls.LoadX509KeyPair(fmt.Sprintf("certs/%s.crt", name), fmt.Sprintf("certs/%s.key", name))

	if err != nil {
		panic("Load client certification failed: " + err.Error())
	}

	return &certificate
}

func loadKeyPair() credentials.TransportCredentials {
	certificate := loadCert("client")

	tlsConfig := &tls.Config{
		ClientAuth:   tls.RequestClientCert,
		Certificates: []tls.Certificate{*certificate},
	}
	return credentials.NewTLS(tlsConfig)
}

func main() {
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt)
	wg := &sync.WaitGroup{}

	if os.Getenv("BIN_FOLDER") != "" {
		binFolder = os.Getenv("BIN_FOLDER")
	}

	if ext := os.Getenv("EXT_ADDR"); ext != "" {
		extRpcAddr = ext + ":" + rpcPort
	}

	openapisrv := NewServer()
	openapisrv.LoadData("./data/data.db")
	opts := []grpc.ServerOption{
		grpc.Creds(loadKeyPair()),
	}

	grpcServer := grpc.NewServer(opts...)

	go func() {
		lis, err := net.Listen("tcp", srvaddr)
		if err != nil {
			log.Fatalf("failed to listen: %v", err)
		}

		okapi.RegisterOkAPIServer(grpcServer, openapisrv)
		log.Printf("RPC Listening on %v", srvaddr)
		wg.Add(1)
		grpcServer.Serve(lis)
	}()

	srv := NewUIServer(patientIf)
	srv.data = openapisrv.data

	go func() {
		wg.Add(1)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			panic(err)
		}
	}()

	<-stop

	go func() {
		grpcServer.GracefulStop()
		wg.Done()
		log.Println("Shutdown RPC server")
	}()

	go func() {
		log.Println("Shutdown UI server...")
		srv.Shutdown(context.Background())
		wg.Done()
		log.Println("UI Server shutdown...")
	}()

	wg.Wait()
}
