package probe

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 (
	probeEntryPoint   = "/probe"
	metricsEntryPoint = "/metrics"
)

type Probe interface {
	commons.Handler
}

type probe struct {
	configDir       string
	probeConfigPath string
	probeConfig     config.ProbeConfig

	probeEntryURL   string
	metricsEntryURL string

	probeClient   client.Client
	metricsClient client.Client

	shutdownChan chan bool
}

func New(configDir string) (Probe, error) {
	p := &probe{
		configDir:    configDir,
		shutdownChan: make(chan bool),
	}
	var err error

	if p.probeEntryURL, err = opuu.Join(config.Config.Central.URL, probeEntryPoint); nil != err {
		return nil, err
	}
	if p.metricsEntryURL, err = opuu.Join(config.Config.Central.URL, metricsEntryPoint); nil != err {
		return nil, err
	}

	p.probeConfigPath = path.Join(configDir, config.ProbeConfigFileName)

	conf := lfcc.New()
	if !lfcc.Exists(p.probeConfigPath) {
		return nil, fmt.Errorf("Probe: Config file[%s] is not exist", p.probeConfigPath)
	}

	if err = conf.Load(&p.probeConfig, p.probeConfigPath); nil != err {
		return nil, fmt.Errorf("Probe: Loading of Probe config file[%s] failed error[%v]", p.probeConfigPath, err)
	}

	return p, nil
}

func (p *probe) Serve() error {
	var err error

	if err = p.connectToCentral(); nil != err {
		return err
	}

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

	return err
}

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

func (p *probe) connectToCentral() error {
	header := http.Header{}
	header[module.ProbeHeader_ProbeKey] = []string{*p.probeConfig.ID}

	var res *http.Response
	var err error
	p.probeClient = client.New()
	if res, err = p.probeClient.Dial(p.probeEntryURL, header, 4096, 4096); nil != err {
		return err
	}
	logging.Logger.Debug(fmt.Sprintf("Probe: Connect Probe HTTP Status[%s]", res.Status))
	p.probeClient.OnNotify(p.onNotify)

	p.metricsClient = client.New()
	if res, err = p.metricsClient.Dial(p.metricsEntryURL, header, 4096, 4096); nil != err {
		return err
	}
	logging.Logger.Debug(fmt.Sprintf("Probe: Connect Metrics HTTP Status[%s]", res.Status))

	return nil
}

func (p *probe) onNotify(method string, params []string) {
	switch method {
	case module.NoAuthProbeService_AcceptNoAuthProbe:

		break
	case module.NoAuthProbeService_DenyNoauthProbe:

		break
	}

}
