package monitor

import (
	"crypto/tls"
	"errors"
	"net/url"
	"time"

	"github.com/valyala/fasthttp"
	"github.com/yusing/godoxy/internal/types"
	"github.com/yusing/goutils/version"
)

type HTTPHealthMonitor struct {
	*monitor
	method string
}

var pinger = &fasthttp.Client{
	ReadTimeout:                   5 * time.Second,
	WriteTimeout:                  3 * time.Second,
	MaxConnDuration:               0,
	DisableHeaderNamesNormalizing: true,
	DisablePathNormalizing:        true,
	TLSConfig: &tls.Config{
		InsecureSkipVerify: true,
	},
	MaxConnsPerHost:          1,
	NoDefaultUserAgentHeader: true,
}

func NewHTTPHealthMonitor(url *url.URL, config *types.HealthCheckConfig) *HTTPHealthMonitor {
	mon := new(HTTPHealthMonitor)
	mon.monitor = newMonitor(url, config, mon.CheckHealth)
	if config.UseGet {
		mon.method = fasthttp.MethodGet
	} else {
		mon.method = fasthttp.MethodHead
	}
	return mon
}

func (mon *HTTPHealthMonitor) CheckHealth() (types.HealthCheckResult, error) {
	req := fasthttp.AcquireRequest()
	defer fasthttp.ReleaseRequest(req)

	resp := fasthttp.AcquireResponse()
	defer fasthttp.ReleaseResponse(resp)

	req.SetRequestURI(mon.url.Load().JoinPath(mon.config.Path).String())
	req.Header.SetMethod(mon.method)
	req.Header.Set("User-Agent", "GoDoxy/"+version.Get().String())
	req.Header.Set("Accept", "text/plain,text/html,*/*;q=0.8")
	req.Header.Set("Accept-Encoding", "identity")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Pragma", "no-cache")
	req.SetConnectionClose()

	start := time.Now()
	respErr := pinger.DoTimeout(req, resp, mon.config.Timeout)
	lat := time.Since(start)

	switch {
	case respErr != nil:
		// treat TLS error as healthy
		var tlsErr *tls.CertificateVerificationError
		if ok := errors.As(respErr, &tlsErr); !ok {
			return types.HealthCheckResult{
				Latency: lat,
				Detail:  respErr.Error(),
			}, nil
		}
	case resp.StatusCode() == fasthttp.StatusServiceUnavailable:
		return types.HealthCheckResult{
			Latency: lat,
			Detail:  fasthttp.StatusMessage(resp.StatusCode()),
		}, nil
	}

	return types.HealthCheckResult{
		Latency: lat,
		Healthy: true,
	}, nil
}
