package clinic_internal_api

import (
	"context"
	"fmt"
	"net"
	"net/url"
	"regexp"
	"strconv"
	"strings"
)

var o11yClusterIDPattern = regexp.MustCompile(`^[A-Za-z0-9_-]{1,255}$`)

// SlowQueryTarget contains the server-resolved O11y target for BYOC slow
// queries. Callers cannot supply or override the target or routing headers.
type SlowQueryTarget struct {
	BaseURL       string                 `json:"baseUrl"`
	O11yClusterID string                 `json:"o11yClusterId"`
	Headers       SlowQueryTargetHeaders `json:"headers"`
	Provider      string                 `json:"provider"`
	Region        string                 `json:"region"`
	DeployType    string                 `json:"deployType"`
	DeployTypeV2  string                 `json:"deployTypeV2"`
}

// SlowQueryTargetHeaders is the complete allowlist of routing values that
// Data Service may send to the BYOC O11y data service.
type SlowQueryTargetHeaders struct {
	Env           string `json:"env"`
	BizType       string `json:"bizType"`
	OrgID         string `json:"orgId"`
	RegionID      string `json:"regionId"`
	ApplicationID string `json:"applicationId"`
}

// GetSlowQueryTarget resolves a BYOC O11y slow-query target for an owned
// TiDB Cloud organization and cluster.
func (c *Client) GetSlowQueryTarget(ctx context.Context, orgID uint64, clusterID string) (*SlowQueryTarget, error) {
	if orgID == 0 {
		return nil, fmt.Errorf("org_id must be a positive integer")
	}
	if clusterID == "" {
		return nil, fmt.Errorf("cluster_id is required")
	}

	var result SlowQueryTarget
	err := c.getResource(ctx, []string{
		"orgs",
		strconv.FormatUint(orgID, 10),
		"clusters",
		clusterID,
		"targets",
		"slow-query",
	}, nil, &result)
	if err != nil {
		return nil, err
	}
	if err := result.validate(orgID, clusterID); err != nil {
		return nil, fmt.Errorf("validate Clinic internal slow-query target response: %w", err)
	}
	return &result, nil
}

func (t *SlowQueryTarget) validate(orgID uint64, clusterID string) error {
	if t == nil {
		return fmt.Errorf("response is required")
	}
	required := []struct {
		name  string
		value string
	}{
		{name: "baseUrl", value: t.BaseURL},
		{name: "o11yClusterId", value: t.O11yClusterID},
		{name: "headers.env", value: t.Headers.Env},
		{name: "headers.bizType", value: t.Headers.BizType},
		{name: "headers.orgId", value: t.Headers.OrgID},
		{name: "headers.regionId", value: t.Headers.RegionID},
		{name: "headers.applicationId", value: t.Headers.ApplicationID},
		{name: "provider", value: t.Provider},
		{name: "region", value: t.Region},
		{name: "deployType", value: t.DeployType},
		{name: "deployTypeV2", value: t.DeployTypeV2},
	}
	for _, field := range required {
		if strings.TrimSpace(field.value) == "" {
			return fmt.Errorf("%s is required", field.name)
		}
		if strings.TrimSpace(field.value) != field.value {
			return fmt.Errorf("%s must not contain surrounding whitespace", field.name)
		}
	}

	baseURL, err := url.Parse(t.BaseURL)
	if err != nil || baseURL.Opaque != "" || (baseURL.Scheme != "http" && baseURL.Scheme != "https") ||
		baseURL.Host == "" || baseURL.Hostname() == "" {
		return fmt.Errorf("baseUrl must be an absolute HTTP(S) URL")
	}
	if baseURL.User != nil || baseURL.RawQuery != "" || baseURL.ForceQuery || baseURL.Fragment != "" ||
		baseURL.RawFragment != "" || baseURL.RawPath != "" || (baseURL.Path != "" && baseURL.Path != "/") {
		return fmt.Errorf("baseUrl must contain only scheme and host")
	}
	hostname := strings.ToLower(baseURL.Hostname())
	normalizedHostname := strings.TrimSuffix(hostname, ".")
	parsedIP := net.ParseIP(hostname)
	if normalizedHostname == "localhost" || strings.HasSuffix(normalizedHostname, ".localhost") ||
		(parsedIP != nil && parsedIP.IsLoopback()) {
		return fmt.Errorf("baseUrl localhost target is not allowed")
	}

	if !o11yClusterIDPattern.MatchString(t.O11yClusterID) {
		return fmt.Errorf("o11yClusterId is invalid")
	}
	if t.Headers.BizType != "byoc" {
		return fmt.Errorf("headers.bizType must be byoc")
	}
	if t.Headers.OrgID != strconv.FormatUint(orgID, 10) {
		return fmt.Errorf("headers.orgId does not match the requested organization")
	}
	if t.Headers.ApplicationID != clusterID {
		return fmt.Errorf("headers.applicationId does not match the requested cluster")
	}
	if t.Headers.RegionID != t.Provider+"-"+t.Region {
		return fmt.Errorf("headers.regionId does not match provider and region")
	}

	switch {
	case t.DeployType == "byoc" && t.DeployTypeV2 == "byoc":
	case t.DeployType == "byoc-host" && t.DeployTypeV2 == "byoc resource pool":
	default:
		return fmt.Errorf("deploy type is not a consistent BYOC target")
	}
	return nil
}
