package license

import (
	"context"
	"net/http"
	"time"
)

// ProbeResult classifies the outcome of a connectivity probe.
type ProbeResult int

const (
	// ProbeResultUnknown means only 1/3 external endpoints were reachable —
	// network state is ambiguous; treat as断网 (give grace period).
	ProbeResultUnknown ProbeResult = 0
	// ProbeResultNetworkDown means 0/3 endpoints reachable — genuine outage.
	ProbeResultNetworkDown ProbeResult = 1
	// ProbeResultSelectiveBlock means ≥2/3 endpoints reachable while all
	// auth centers are unreachable — intentional blocking detected.
	ProbeResultSelectiveBlock ProbeResult = 2
)

// defaultProbeEndpoints are well-known Chinese internet services used to
// distinguish "genuine outage" from "auth server selectively blocked".
var defaultProbeEndpoints = []string{
	"https://www.baidu.com",
	"https://cloud.tencent.com",
	"https://www.aliyun.com",
}

// ConnectivityProber probes public internet reachability.
type ConnectivityProber interface {
	Probe(ctx context.Context) ProbeResult
}

// HTTPConnectivityProber sends HEAD requests to a list of endpoints and
// counts how many respond with a non-5xx status.
type HTTPConnectivityProber struct {
	endpoints  []string
	httpClient *http.Client
}

// NewHTTPConnectivityProber creates a prober. If no endpoints are given the
// default Chinese CDN endpoints are used.
func NewHTTPConnectivityProber(endpoints ...string) *HTTPConnectivityProber {
	if len(endpoints) == 0 {
		endpoints = append([]string(nil), defaultProbeEndpoints...)
	}
	return &HTTPConnectivityProber{
		endpoints: endpoints,
		httpClient: &http.Client{
			Timeout: 5 * time.Second,
			CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
				return http.ErrUseLastResponse
			},
		},
	}
}

// Probe runs HEAD requests against all configured endpoints concurrently.
// Returns SelectiveBlock if ≥2 are reachable, NetworkDown if 0, Unknown otherwise.
func (p *HTTPConnectivityProber) Probe(ctx context.Context) ProbeResult {
	type result struct{ ok bool }
	ch := make(chan result, len(p.endpoints))
	for _, ep := range p.endpoints {
		go func(url string) {
			req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
			if err != nil {
				ch <- result{false}
				return
			}
			resp, err := p.httpClient.Do(req)
			if err != nil {
				ch <- result{false}
				return
			}
			_ = resp.Body.Close()
			ch <- result{resp.StatusCode < 500}
		}(ep)
	}
	reachable := 0
	for range p.endpoints {
		if (<-ch).ok {
			reachable++
		}
	}
	switch {
	case reachable >= 2:
		return ProbeResultSelectiveBlock
	case reachable == 0:
		return ProbeResultNetworkDown
	default:
		return ProbeResultUnknown
	}
}
