/*
 * Copyright 2021 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package remotesvr

import (
	"context"
	"net"
	"sync"

	"github.com/cloudwego/kitex/pkg/gofunc"
	"github.com/cloudwego/kitex/pkg/klog"
	"github.com/cloudwego/kitex/pkg/remote"
)

// Server is the interface for remote server.
type Server interface {
	Start() chan error
	Stop() error
	Address() net.Addr
}

type server struct {
	opt      *remote.ServerOption
	listener net.Listener
	transSvr remote.TransServer
	sync.Mutex
}

// NewServer creates a remote server.
func NewServer(opt *remote.ServerOption, transHdlr remote.ServerTransHandler) (Server, error) {
	transSvr := opt.TransServerFactory.NewTransServer(opt, transHdlr)
	s := &server{
		opt:      opt,
		transSvr: transSvr,
	}
	return s, nil
}

// Start starts the server and return chan, the chan receive means server shutdown or err happen
func (s *server) Start() chan error {
	errCh := make(chan error, 1)
	ln, err := s.buildListener()
	if err != nil {
		errCh <- err
		return errCh
	}

	s.Lock()
	s.listener = ln
	s.Unlock()

	gofunc.GoFunc(context.Background(), func() { errCh <- s.transSvr.BootstrapServer(ln) })
	return errCh
}

func (s *server) buildListener() (ln net.Listener, err error) {
	if s.opt.Listener != nil {
		klog.Infof("KITEX: server listen at addr=%s", s.opt.Listener.Addr().String())
		return s.opt.Listener, nil
	}
	addr := s.opt.Address
	if ln, err = s.transSvr.CreateListener(addr); err != nil {
		klog.Errorf("KITEX: server listen failed, addr=%s error=%s", addr.String(), err)
	} else {
		klog.Infof("KITEX: server listen at addr=%s", ln.Addr().String())
	}
	return
}

// Stop stops the server gracefully.
func (s *server) Stop() (err error) {
	s.Lock()
	defer s.Unlock()
	if s.transSvr != nil {
		err = s.transSvr.Shutdown()
		s.listener = nil
	}
	return
}

func (s *server) Address() net.Addr {
	s.Lock()
	defer s.Unlock()
	if s.listener != nil {
		return s.listener.Addr()
	}
	return nil
}
