// Package catalogsystemprotocol owns the pure canonical protocol shared by the
// durable system-slot writer and its retained owner verifier.
package catalogsystemprotocol

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"strings"
	"unicode/utf8"

	systemauthorityv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/systemlayoutbundleauthorityv1"
)

const (
	outcomeDigestDomain = "nexuscrm.catalog-system-layout-bundle-slot-command-outcome/v1"
	maxPrincipalIDBytes = 255
)

// Result is the closed terminal result grammar of one accepted system-slot
// command.
type Result string

const (
	ResultApplied                Result = "APPLIED"
	ResultExpectedPriorMismatch  Result = "EXPECTED_PRIOR_MISMATCH"
	ResultAuthorityStateConflict Result = "AUTHORITY_STATE_CONFLICT"
)

// ValidateResult rejects every value outside the closed result grammar.
func ValidateResult(result Result) error {
	switch result {
	case ResultApplied, ResultExpectedPriorMismatch, ResultAuthorityStateConflict:
		return nil
	default:
		return fmt.Errorf("invalid outcome result %q", result)
	}
}

// ValidatePrincipal validates the complete authenticated principal and audit
// binding persisted by the system-slot authority.
func ValidatePrincipal(principalID, auditDigest string) error {
	if principalID == "" ||
		len(principalID) > maxPrincipalIDBytes ||
		!utf8.ValidString(principalID) ||
		strings.TrimSpace(principalID) != principalID {
		return fmt.Errorf("PrincipalId is invalid")
	}
	if err := ValidateDigest(auditDigest); err != nil {
		return fmt.Errorf("PrincipalAuditDigest is invalid")
	}
	return nil
}

// ValidateDigest validates the canonical lowercase SHA-256 text grammar.
func ValidateDigest(value string) error {
	const prefix = "sha256:"
	if len(value) != len(prefix)+64 || !strings.HasPrefix(value, prefix) {
		return fmt.Errorf("must use sha256:<64 lowercase hex>")
	}
	hexText := value[len(prefix):]
	if strings.ToLower(hexText) != hexText {
		return fmt.Errorf("digest hex must be lowercase")
	}
	if _, err := hex.DecodeString(hexText); err != nil {
		return fmt.Errorf("digest hex is invalid")
	}
	return nil
}

// OutcomeDigest validates and hashes the complete immutable command/outcome
// binding. No SQL, source, slot state, or evidence is part of this grammar.
func OutcomeDigest(
	commandID systemauthorityv1.CommandID,
	requestDigest string,
	principalID string,
	auditDigest string,
	result Result,
) (string, error) {
	if err := systemauthorityv1.ValidateCommandID(commandID); err != nil {
		return "", err
	}
	if err := ValidateDigest(requestDigest); err != nil {
		return "", err
	}
	if err := ValidatePrincipal(principalID, auditDigest); err != nil {
		return "", err
	}
	if err := ValidateResult(result); err != nil {
		return "", err
	}
	var canonical bytes.Buffer
	writeString(&canonical, outcomeDigestDomain)
	writeString(&canonical, string(commandID))
	writeString(&canonical, requestDigest)
	writeString(&canonical, principalID)
	writeString(&canonical, auditDigest)
	writeString(&canonical, string(result))
	digest := sha256.Sum256(canonical.Bytes())
	return "sha256:" + hex.EncodeToString(digest[:]), nil
}

func writeString(out *bytes.Buffer, value string) {
	var encoded [binary.MaxVarintLen64]byte
	count := binary.PutUvarint(encoded[:], uint64(len(value)))
	out.Write(encoded[:count])
	out.WriteString(value)
}
