package health

import (
	"context"
	"crypto/tls"
	"errors"
	"net/http"

	"github.com/yusing/go-proxy/internal/net/types"
)

type HTTPHealthMonitor struct {
	*monitor
	method string
	pinger *http.Client
}

func NewHTTPHealthMonitor(ctx context.Context, name string, url types.URL, config HealthCheckConfig) HealthMonitor {
	mon := new(HTTPHealthMonitor)
	mon.monitor = newMonitor(ctx, name, url, &config, mon.checkHealth)
	mon.pinger = &http.Client{Timeout: config.Timeout}
	if config.UseGet {
		mon.method = http.MethodGet
	} else {
		mon.method = http.MethodHead
	}
	return mon
}

func (mon *HTTPHealthMonitor) checkHealth() (healthy bool, detail string, err error) {
	req, reqErr := http.NewRequestWithContext(
		mon.ctx,
		mon.method,
		mon.URL.String(),
		nil,
	)
	if reqErr != nil {
		err = reqErr
		return
	}
	req.Header.Set("Connection", "close")

	resp, respErr := mon.pinger.Do(req)
	if respErr == nil {
		resp.Body.Close()
	}

	switch {
	case respErr != nil:
		// treat tls error as healthy
		var tlsErr *tls.CertificateVerificationError
		if ok := errors.As(respErr, &tlsErr); !ok {
			detail = respErr.Error()
			return
		}
	case resp.StatusCode == http.StatusServiceUnavailable:
		detail = resp.Status
		return
	}

	healthy = true
	return
}
