package main

import (
	"context"
	"errors"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/nexuscrm/backend/internal/runtimeapi"
	"github.com/nexuscrm/backend/internal/runtimeconfig"
	"github.com/nexuscrm/backend/internal/runtimeprocess"
	"github.com/nexuscrm/backend/internal/runtimeproduction"
)

var version = "dev"

func main() {
	ctx, stopSignals := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stopSignals()
	process, closeConnection, err := assemble(ctx, os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}
	defer closeConnection()
	if err := process.Start(); err != nil {
		log.Fatal(err)
	}
	log.Printf("runtime-api started: grpc=%s admin=%s", process.GRPCAddress(), process.AdminAddress())
	select {
	case <-ctx.Done():
	case err := <-wait(process):
		if err != nil {
			log.Printf("runtime-api serve failed: %v", err)
		}
	}
	shutdownCtx, cancel := context.WithTimeout(context.Background(), runtimeapi.ShutdownTimeout)
	defer cancel()
	if err := process.Stop(shutdownCtx); err != nil {
		log.Printf("runtime-api shutdown: %v", err)
	}
}

func assemble(ctx context.Context, args []string) (*runtimeprocess.Process, func() error, error) {
	if len(args) == 1 && args[0] == "bootstrap" {
		config, err := runtimeconfig.LoadAPIBootstrap(os.LookupEnv)
		if err != nil {
			return nil, nil, err
		}
		transport, err := runtimeproduction.LoadGRPCServerTransportCredentials(
			config.TLSCertificateFile, config.TLSPrivateKeyFile,
		)
		if err != nil {
			return nil, nil, err
		}
		process, err := runtimeapi.AssembleNonServingBootstrap(runtimeapi.ProcessConfig{
			Version: version, AdminAddress: config.AdminAddress,
			GRPCAddress: config.GRPCAddress, AdmissionLimit: config.AdmissionLimit,
			ProfilingEnabled:      config.ProfilingEnabled,
			SQLDiagnosticsEnabled: config.SQLDiagnosticsEnabled,
			TransportCredentials:  transport,
		})
		return process, func() error { return nil }, err
	}
	if len(args) != 0 {
		return nil, nil, errors.New("runtime-api accepts only the optional bootstrap mode")
	}
	config, err := runtimeconfig.LoadAPI(os.LookupEnv)
	if err != nil {
		return nil, nil, err
	}
	process, connection, err := runtimeproduction.Assemble(ctx, version, config)
	if err != nil {
		return nil, nil, err
	}
	return process, connection.Close, nil
}

func wait(process interface{ Wait() error }) <-chan error {
	done := make(chan error, 1)
	go func() { done <- process.Wait() }()
	return done
}
