package controller

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"time"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"

	mnegw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
	pgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
	agw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
	tgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
)

var (
	// command-line options:
	// gRPC server endpoint
	grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:55055", "gRPC server endpoint")
)

func stopHttpServer() error {
	log.Info("shutting down http server")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	err := c.httpServer.Shutdown(ctx)
	return err
}

func run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// Register gRPC server endpoint
	// Note: Make sure the gRPC server is running properly and accessible
	mux := runtime.NewServeMux()

	err := registerHttpHandler(mux)

	if err != nil {
		return err
	}

	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
	err = pgw.RegisterPndServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = agw.RegisterAuthServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = agw.RegisterUserServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = agw.RegisterRoleServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = tgw.RegisterTopologyServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = mnegw.RegisterNetworkElementServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	err = tgw.RegisterRoutingTableServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	// Set the HTTP server of core to the new server
	c.httpServer = &http.Server{Addr: ":8080", Handler: mux}
	// Start HTTP server (and proxy calls to gRPC server endpoint)
	return c.httpServer.ListenAndServe()
}

func startHttpServer() {
	go func() {
		if err := run(); err != nil {
			log.Info(err)
		}
	}()

	log.Info("Server exiting")
}

func registerHttpHandler(mux *runtime.ServeMux) error {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered in f", r)
		}
	}()

	err := liveCheckHandler(mux)
	if err != nil {
		return err
	}

	err = readyCheckHandler(mux)
	if err != nil {
		return err
	}

	err = metricsHandler(mux)
	if err != nil {
		return err
	}

	return nil
}

func liveCheckHandler(mux *runtime.ServeMux) error {
	err := mux.HandlePath("GET", "/livez", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
		w.WriteHeader(http.StatusOK)
	})

	if err != nil {
		return status.Errorf(codes.Internal, "%v", err)
	}

	return nil
}

func readyCheckHandler(mux *runtime.ServeMux) error {
	err := mux.HandlePath("GET", "/readyz", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
		w.WriteHeader(http.StatusOK)
	})

	if err != nil {
		return status.Errorf(codes.Internal, "%v", err)
	}

	return nil
}

func metricsHandler(mux *runtime.ServeMux) error {
	err := mux.HandlePath("GET", "/metrics", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
		promhttp.Handler()
	})

	if err != nil {
		return status.Errorf(codes.Internal, "%v", err)
	}

	return nil
}
