package route

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/tidbcloud/service-proxy/pkg/config"
)

// PathType indicates which path pattern matched.
type PathType int

const (
	PathTypeUnknown PathType = iota
	PathTypeCluster
	PathTypeService
)

const namespacePrefix = "tidb"

var (
	// clusterPattern matches /proxy/cluster/{clusterID}/{service}/*
	clusterPattern = regexp.MustCompile(`^/proxy/cluster/([^/]+)/([^/]+)(/.*)?$`)

	// servicePattern matches /proxy/services/{service}/namespaces/{namespace}/ports/{port}/*
	servicePattern = regexp.MustCompile(`^/proxy/services/([^/]+)/namespaces/([^/]+)/ports/([^/]+)(/.*)?$`)
)

// PathVars holds extracted path variables from request URL parsing.
//
// For cluster routes (/proxy/cluster/{clusterID}/{service}/*):
//   - ClusterID: extracted from URL
//   - ServiceKey: the service identifier from URL (e.g., "pd", "tikv", "tibr")
//   - Namespace: derived as "tidb" + ClusterID
//   - ResolvedService: actual K8s service name (filled by service resolution)
//
// For service routes (/proxy/services/{service}/namespaces/{namespace}/ports/{port}/*):
//   - ServiceKey: the K8s service name from URL
//   - Namespace: from URL
//   - Port: from URL
//   - ResolvedService: same as ServiceKey (no resolution needed)
type PathVars struct {
	ClusterID       string            // Cluster ID (cluster routes only)
	ServiceKey      string            // Service identifier from URL (e.g., "pd", "tikv")
	ResolvedService string            // Actual K8s service name after resolution
	Namespace       string            // Kubernetes namespace
	Port            string            // Service port (service routes only)
	Remaining       string            // Remaining path after route prefix match
	PathParams      map[string]string // Dynamic path parameters (e.g., {tibr_id} → "123")
}

// parsePath parses a request path into PathVars and returns the matched PathType.
// It performs no external I/O and returns PathTypeUnknown on mismatch.
func parsePath(path string) (*PathVars, PathType, error) {
	if path == "" {
		return nil, PathTypeUnknown, fmt.Errorf("path is empty")
	}

	if matches := clusterPattern.FindStringSubmatch(path); matches != nil {
		return &PathVars{
			ClusterID:  matches[1],
			ServiceKey: matches[2],
			Namespace:  fmt.Sprintf("%s%s", namespacePrefix, matches[1]),
			Remaining:  matches[3],
		}, PathTypeCluster, nil
	}

	if matches := servicePattern.FindStringSubmatch(path); matches != nil {
		serviceName := matches[1]
		return &PathVars{
			ServiceKey:      serviceName,
			ResolvedService: serviceName, // For service routes, service is already resolved
			Namespace:       matches[2],
			Port:            matches[3],
			Remaining:       matches[4],
		}, PathTypeService, nil
	}

	return nil, PathTypeUnknown, fmt.Errorf("path does not match any known pattern: %s", path)
}

// parsePrefixPattern parses a prefix pattern with path parameters into a regex.
func parsePrefixPattern(prefix string) (*regexp.Regexp, []string, error) {
	if !strings.Contains(prefix, "{") {
		return nil, nil, nil
	}

	params, err := config.ExtractPathParams(prefix)
	if err != nil {
		return nil, nil, err
	}

	// Build regex pattern
	normalized := prefix
	if !strings.HasPrefix(normalized, "/") {
		normalized = "/" + normalized
	}
	// Remove trailing slash since (/.*)?$ already matches optional path suffix
	normalized = strings.TrimSuffix(normalized, "/")

	var builder strings.Builder
	builder.WriteString("^")

	remain := normalized
	for len(remain) > 0 {
		start := strings.Index(remain, "{")
		if start == -1 {
			builder.WriteString(regexp.QuoteMeta(remain))
			break
		}
		builder.WriteString(regexp.QuoteMeta(remain[:start]))

		end := strings.Index(remain[start:], "}")
		// No need to check error here since config.ExtractPathParams already validated
		builder.WriteString("([^/]+)")
		remain = remain[start+end+1:]
	}

	builder.WriteString("(/.*)?$")

	compiled, err := regexp.Compile(builder.String())
	if err != nil {
		return nil, nil, fmt.Errorf("compile prefix pattern %q: %w", prefix, err)
	}
	return compiled, params, nil
}

// compileServicePattern compiles a service name pattern into a regular expression.
// Returns nil if the pattern is empty (no pattern matching required).
func compileServicePattern(pattern string) (*regexp.Regexp, error) {
	if pattern == "" {
		return nil, nil
	}
	re, err := regexp.Compile(pattern)
	if err != nil {
		return nil, fmt.Errorf("invalid serviceNamePattern %q: %w", pattern, err)
	}
	return re, nil
}
