package auth

import (
	"context"
	"fmt"
	"path"
	"sync"

	cc "git.loafle.net/commons_go/config"
	"git.loafle.net/commons_go/logging"
	crr "git.loafle.net/commons_go/rpc/registry"
	ooccn "git.loafle.net/overflow/overflow_commons_go/config/noauthprobe"
	oocmci "git.loafle.net/overflow/overflow_commons_go/modules/commons/interfaces"
	oogwc "git.loafle.net/overflow/overflow_gateway_websocket/client"
	"git.loafle.net/overflow/overflow_probes/auth/client"
	oopas "git.loafle.net/overflow/overflow_probes/auth/service"
	"git.loafle.net/overflow/overflow_probes/config"
)

func New() AuthManager {
	am := &authManagers{}

	return am
}

type AuthManager interface {
	oocmci.EndableStarter
	oocmci.Stopper
}

type authManagers struct {
	doneChan chan<- error

	cClient oogwc.Client

	configPath string
	config     ooccn.NoAuthProbeConfig

	serviceDoneChan chan error

	stopChan chan struct{}
	stopWg   sync.WaitGroup
}

func (am *authManagers) EndableStart(doneChan chan<- error) error {
	if nil != am.stopChan {
		logging.Logger().Panicf("Auth: auth is already running. Stop it before starting it again")
	}

	am.configPath = path.Join(*config.ConfigDir, ooccn.ConfigFileName)

	conf := cc.New()
	if cc.Exists(am.configPath) {
		if err := conf.Load(&am.config, am.configPath); nil != err {
			return fmt.Errorf("Auth: Loading of NoAuth config file[%s] failed error[%v]", am.configPath, err)
		}
	}

	if nil != am.config.DenyDate {
		return fmt.Errorf("Cannot start because this probe have been denied from overFlow at %s", am.config.DenyDate.String())
	}

	am.serviceDoneChan = make(chan error, 1)

	rpcRegistry := crr.NewRPCRegistry()

	napService := &oopas.NoAuthProbeService{
		DoneChan:   am.serviceDoneChan,
		ConfigPath: am.configPath,
		Config:     am.config,
	}
	rpcRegistry.RegisterService(napService, "")

	ch := client.NewClientHandler(rpcRegistry)
	sb := client.NewSocketBuilder(napService)
	if nil == sb {
		return fmt.Errorf("Auth: Cannot create SocketBuilder")
	}
	am.cClient = client.NewClient(ch, sb)

	am.doneChan = doneChan
	am.stopChan = make(chan struct{})

	am.stopWg.Add(1)
	go am.handleAuth()

	return nil
}

func (am *authManagers) Stop(ctx context.Context) error {
	am.destroy(nil)

	return nil
}

func (am *authManagers) destroy(err error) {
	if am.stopChan == nil {
		logging.Logger().Warnf("Auth: auth must be started before stopping it")
	}
	close(am.stopChan)
	am.stopWg.Wait()
	am.stopChan = nil

	am.cClient.Close()
	close(am.serviceDoneChan)

	logging.Logger().Infof("Auth: stopped")
	am.doneChan <- err
}

func (am *authManagers) handleAuth() {
	var err error
	defer func() {
		am.stopWg.Done()
		am.destroy(err)
	}()

	if err = am.cClient.Connect(); nil != err {
		return
	}

	for {
		select {
		case err = <-am.serviceDoneChan:
			return
		case <-am.stopChan:
			return
		}
	}
}
