package gateway

import (
	"encoding/base64"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/authidentity"
)

const maxLegacyAGSAuthorizationBytes = 16 << 10

// legacyAGSAuthority exists only for the old unqualified AGS routing branch.
// C2 deletes this shim together with prepareAGSWorkspaceAuthority. Workspace-qualified
// workspace AGS traffic is session-only and never calls this classifier.
type legacyAGSAuthority uint8

const (
	legacyAGSAuthorityUnspecified legacyAGSAuthority = iota
	legacyAGSAuthorityMissing
	legacyAGSAuthorityMalformed
	legacyAGSAuthoritySession
	legacyAGSAuthorityRepositoryCredential
)

func classifyLegacyAGSAuthorization(values []string) legacyAGSAuthority {
	if strictLegacyRepositoryCredential(values) {
		return legacyAGSAuthorityRepositoryCredential
	}
	_, status := authidentity.ParseSessionAuthorization(values)
	switch status {
	case authidentity.SessionAuthorizationMissing:
		return legacyAGSAuthorityMissing
	case authidentity.SessionAuthorizationValid:
		return legacyAGSAuthoritySession
	default:
		return legacyAGSAuthorityMalformed
	}
}

func legacyAGSAuthorityDeclaration(authority legacyAGSAuthority) string {
	switch authority {
	case legacyAGSAuthorityMissing:
		return "anonymous"
	case legacyAGSAuthorityMalformed:
		return "malformed"
	case legacyAGSAuthoritySession:
		return sessionCandidateAuthority
	case legacyAGSAuthorityRepositoryCredential:
		return "repository_credential"
	default:
		return ""
	}
}

func strictLegacyRepositoryCredential(values []string) bool {
	if len(values) != 1 || len(values[0]) > maxLegacyAGSAuthorizationBytes {
		return false
	}
	value := values[0]
	scheme, credential, ok := strings.Cut(value, " ")
	if !ok || credential == "" || !validLegacyAGSCredentialASCII(credential) {
		return false
	}
	switch {
	case strings.EqualFold(scheme, "Bearer"):
		return strings.HasPrefix(credential, "ags_rat_") && len(credential) > len("ags_rat_")
	case strings.EqualFold(scheme, "Token"):
		return true
	case strings.EqualFold(scheme, "Basic"):
		if base64.StdEncoding.DecodedLen(len(credential)) > maxLegacyAGSAuthorizationBytes {
			return false
		}
		decoded, err := base64.StdEncoding.DecodeString(credential)
		if err != nil || len(decoded) == 0 || len(decoded) > maxLegacyAGSAuthorizationBytes {
			return false
		}
		username, password, ok := strings.Cut(string(decoded), ":")
		return ok && username != "" && password != "" && !containsAuthorizationControl(username+password)
	default:
		return false
	}
}

func validLegacyAGSCredentialASCII(value string) bool {
	for index := 0; index < len(value); index++ {
		character := value[index]
		if character >= '0' && character <= '9' ||
			character >= 'A' && character <= 'Z' ||
			character >= 'a' && character <= 'z' ||
			strings.ContainsRune("!#$%&'*+-.^_`|~:/=", rune(character)) {
			continue
		}
		return false
	}
	return true
}

func containsAuthorizationControl(value string) bool {
	for _, character := range value {
		if character < ' ' || character == 0x7f {
			return true
		}
	}
	return false
}
