package identityv1

import (
	"context"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
)

func ValidateRequest(ctx context.Context, request Request) error {
	if ctx == nil {
		return invalid(InvalidComponentDeadline)
	}
	if err := ctx.Err(); err != nil {
		if err == context.DeadlineExceeded {
			return &Failure{Category: FailureDeadlineExceeded}
		}
		return &Failure{Category: FailureCanceled}
	}
	if _, ok := ctx.Deadline(); !ok {
		return invalid(InvalidComponentDeadline)
	}
	if !validOpaque(request.AppInstallationID, MaxAppInstallationIdentifierBytes) {
		if len(request.AppInstallationID) > MaxAppInstallationIdentifierBytes {
			return exhausted(LimitAppInstallationID, MaxAppInstallationIdentifierBytes)
		}
		return invalid(InvalidComponentAppInstallationID)
	}
	if len(request.RequestedCapabilities) == 0 {
		return invalid(InvalidComponentCapability)
	}
	if len(request.RequestedCapabilities) > MaxRequestedCapabilities {
		return exhausted(LimitCapabilityCount, MaxRequestedCapabilities)
	}
	// P6's intersection helper is the single owner of capability grammar,
	// canonical ordering, duplicate rejection and size ceilings. Repeating the
	// same set on all axes validates without inventing an Exchange-only grammar.
	if _, err := delegationv1.IntersectCapabilities(delegationv1.CapabilityGrantSets{
		Workload: request.RequestedCapabilities, AppInstallation: request.RequestedCapabilities,
		Session: request.RequestedCapabilities, Requested: request.RequestedCapabilities,
	}); err != nil {
		for _, capability := range request.RequestedCapabilities {
			if len(capability) > MaxCapabilityIdentifierBytes {
				return exhausted(LimitCapabilityID, MaxCapabilityIdentifierBytes)
			}
		}
		return invalid(InvalidComponentCapability)
	}
	return nil
}

func ValidateResponse(response Response) error {
	if response.OpaqueDelegation == "" || len(response.OpaqueDelegation) > MaxOpaqueDelegationBytes ||
		!utf8.ValidString(response.OpaqueDelegation) || response.ExpiresAtUnixSecond <= 0 {
		return &Failure{Category: FailureInternal}
	}
	return nil
}

func validOpaque(value string, maximum int) bool {
	if value == "" || len(value) > maximum || !utf8.ValidString(value) || strings.TrimSpace(value) != value {
		return false
	}
	for _, r := range value {
		if unicode.IsControl(r) {
			return false
		}
	}
	return true
}

func invalid(component InvalidComponent) *Failure {
	return &Failure{Category: FailureInvalidRequest, InvalidComponent: component}
}

func exhausted(kind LimitKind, maximum int) *Failure {
	return &Failure{Category: FailureResourceExhausted, LimitKind: kind, Maximum: uint64(maximum)}
}
