package auth

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"path"

	lfcc "git.loafle.net/commons_go/config"
	"git.loafle.net/commons_go/logging"
	"git.loafle.net/overflow/overflow_probes/central/api/module"

	"git.loafle.net/overflow/overflow_probes/central/client"
	"git.loafle.net/overflow/overflow_probes/commons"
	"git.loafle.net/overflow/overflow_probes/config"
	opuu "git.loafle.net/overflow/overflow_probes/util/url"
)

const (
	noAuthEntryPoint = "/auth"
)

type AuthHandler interface {
	commons.Handler
}

type authHandlers struct {
	c         client.Client
	entryURL  string
	configDir string

	noAuthConfigPath string
	noAuthConfig     config.NoAuthProbeConfig

	probeConfigPath string
	probeConfig     config.ProbeConfig

	shutdownChan chan bool
	acceptedChan chan bool
	deniedChan   chan error
}

func New(configDir string) (AuthHandler, error) {
	var err error

	h := &authHandlers{
		configDir:    configDir,
		shutdownChan: make(chan bool),
		acceptedChan: make(chan bool),
		deniedChan:   make(chan error),
	}

	if h.entryURL, err = opuu.Join(config.Config.Central.URL, noAuthEntryPoint); nil != err {
		return nil, err
	}

	h.noAuthConfigPath = path.Join(configDir, config.NoAuthProbeConfigFileName)
	h.probeConfigPath = path.Join(configDir, config.ProbeConfigFileName)

	conf := lfcc.New()
	if lfcc.Exists(h.noAuthConfigPath) {
		if err = conf.Load(&h.noAuthConfig, h.noAuthConfigPath); nil != err {
			return nil, fmt.Errorf("Auth: Loading of NoAuth config file[%s] failed error[%v]", h.noAuthConfigPath, err)
		}
	} else {
		if err = lfcc.Save(h.noAuthConfig, h.noAuthConfigPath, true); nil != err {
			return nil, fmt.Errorf("Auth: Saving of NoAuth config file[%s] failed error[%v]", h.noAuthConfigPath, err)
		}
	}

	return h, nil
}

func (h *authHandlers) Serve() error {
	if nil != h.noAuthConfig.DenyDate {
		return fmt.Errorf("Cannot start because this probe have been denied from overFlow[%s]", h.noAuthConfig.DenyDate.String())
	}

	h.c = client.New()
	h.c.OnNotify(h.onNotify)

	var err error
	if nil != h.noAuthConfig.TempKey && "" != *h.noAuthConfig.TempKey {
		err = h.serveConnect(*h.noAuthConfig.TempKey)
	} else {
		err = h.serveRegistration()
	}
	if nil != err {
		return err
	}

ListenLoop:
	for {
		select {
		case <-h.shutdownChan:
			return errors.New("Shutting down")
		case <-h.acceptedChan:
			break ListenLoop
		case err := <-h.deniedChan:
			return err
		}
	}

	return nil
}

func (h *authHandlers) Shutdown(ctx context.Context) error {
	h.shutdownChan <- true
	return nil
}

func (h *authHandlers) serveRegistration() error {
	var err error
	header := http.Header{}

	var enc string
	if enc, err = getRegistHeader(); nil != err {
		return err
	}
	header[module.NoAuthProbeHeader_NoAuthRegist] = []string{enc}

	var res *http.Response
	if res, err = h.c.Dial(h.entryURL, header, 4096, 4096); nil != err {
		return err
	}

	tempKey := res.Header.Get(module.NoAuthProbeHeader_SetNoAuthID)
	h.noAuthConfig.TempKey = &tempKey
	if err = lfcc.Save(h.noAuthConfig, h.noAuthConfigPath, true); nil != err {
		return err
	}

	return nil
}

func (h *authHandlers) serveConnect(noAuthTempKey string) error {
	var err error
	header := http.Header{}
	header[module.NoAuthProbeHeader_NoAuthID] = []string{noAuthTempKey}

	var res *http.Response
	if res, err = h.c.Dial(h.entryURL, header, 4096, 4096); nil != err {
		return err
	}

	logging.Logger.Debug(fmt.Sprintf("Auth: Connect HTTP Status[%s]", res.Status))

	return nil
}
