package service

import (
	"context"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	"net/http"

	gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"

	"git.apinb.com/bsm-sdk/core/conf"
	"git.apinb.com/bsm-sdk/core/env"
	"git.apinb.com/bsm-sdk/core/print"
	"git.apinb.com/bsm-sdk/core/vars"
	clientv3 "go.etcd.io/etcd/client/v3"
	"google.golang.org/grpc"
)

type (
	// RegisterFn defines the method to register a server.
	RegisterFn func(*grpc.Server)

	Service struct {
		GrpcSrv *grpc.Server
		Opts    *Options
	}

	Options struct {
		Addr        string
		EtcdClient  *clientv3.Client
		MsConf      *conf.MicroServiceConf
		GatewayConf *conf.GatewayConf
		GatewayCtx  context.Context
		GatewayMux  *gwRuntime.ServeMux
	}
)

func New(srv *grpc.Server, opts *Options) *Service {
	return &Service{GrpcSrv: srv, Opts: opts}
}

func Addr(ip string, port int) string {
	return net.JoinHostPort(ip, strconv.Itoa(port))
}

func (s *Service) Start() {
	print.Info("[BSM - %s] Service Starting ...", vars.ServiceKey)

	// register to etcd.
	if s.Opts.MsConf != nil && s.Opts.MsConf.Enable {
		if s.Opts.EtcdClient == nil {
			print.Error("[BSM Register] Etcd Client is nil.")
			os.Exit(1)
		}
		print.Info("[BSM - %s] Registering Service to Etcd ...", vars.ServiceKey)
		// get methods
		methods := FoundGrpcMethods(s.GrpcSrv)

		// set router key
		routerKey := vars.ServiceRootPrefix + "Router/" + env.Runtime.Workspace + "/" + strings.ToLower(vars.ServiceKey) + "/" + s.Opts.Addr

		// register to etcd
		register, err := RegisterService(s.Opts.EtcdClient, routerKey, methods, vars.ServiceLease)
		if err != nil {
			log.Panicf("[ERROR] %s Service Register:%s \n", vars.ServiceKey, err.Error())
		}

		anonKey := vars.ServiceRootPrefix + "Router/" + env.Runtime.Workspace + "/"

		register.SetAnonymous(anonKey, s.Opts.MsConf.Anonymous)

		// service register lease
		go register.ListenLeaseRespChan()
	}

	// run grpc srv.
	tcpListen, err := net.Listen("tcp", s.Opts.Addr)
	if err != nil {
		panic(err)
	}

	go func() {
		if err := s.GrpcSrv.Serve(tcpListen); err != nil {
			panic(err)
		}
	}()
	print.Success("[BSM - %s] Grpc %s Runing Success !", vars.ServiceKey, s.Opts.Addr)

	if s.Opts.GatewayConf != nil && s.Opts.GatewayConf.Enable {
		addr := Addr("0.0.0.0", s.Opts.GatewayConf.Port)
		go s.Gateway(s.Opts.Addr, addr)

		print.Success("[BSM - %s] Http %s Runing Success!", vars.ServiceKey, addr)
	}

	select {}
}

func (s *Service) Gateway(grpcAddr string, httpAddr string) {
	// 1. 定义一个context
	_, cancel := context.WithCancel(s.Opts.GatewayCtx)
	defer cancel()

	http.ListenAndServe(httpAddr, s.Opts.GatewayMux)
}

func (s *Service) Stop() {
	s.GrpcSrv.GracefulStop()
}

// found grpc methods.
func FoundGrpcMethods(s *grpc.Server) []string {
	var mothods []string
	for key, srv := range s.GetServiceInfo() {
		srvName := strings.Split(key, ".")[1]
		for _, mn := range srv.Methods {
			mothods = append(mothods, srvName+"."+mn.Name)
		}
	}
	return mothods
}