package applicationresultv1

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
)

// CanonicalExecutionRefBytes is the v1 cross-language execution-ref grammar:
//
//   - ASCII domain prefix "nexuscrm-runtime-execution-ref-v1";
//   - each string is unsigned-varint length followed by exact UTF-8 bytes;
//   - fields retain the fixed order below.
//
// No transport serialization or query-time normalization participates.
func CanonicalExecutionRefBytes(ref ExecutionRef) ([]byte, error) {
	if !validExecutionRef(ref) {
		return nil, invalid()
	}
	var out bytes.Buffer
	writeBytes(&out, []byte("nexuscrm-runtime-execution-ref-v1"))
	writeString(&out, ref.ExecutionID)
	writeString(&out, ref.PackGenerationID)
	writeString(&out, ref.ServingEpochDigest)
	writeString(&out, ref.DefinitionRef)
	writeString(&out, ref.DefinitionVersion)
	writeString(&out, ref.InputDigest)
	return out.Bytes(), nil
}

func ExecutionRefDigest(ref ExecutionRef) (string, error) {
	raw, err := CanonicalExecutionRefBytes(ref)
	if err != nil {
		return "", err
	}
	return sha256Digest(raw), nil
}

// ComputeResultDigest hashes the exact decoded Runtime-owned result payload,
// not a database encoding or transport wrapper. Every terminal outcome has a
// nonempty typed payload, including noop, rejected, and failed.
func ComputeResultDigest(payload []byte) (string, error) {
	if len(payload) == 0 {
		return "", invalid()
	}
	if len(payload) > MaxResultPayloadBytes {
		return "", exhausted(MaxResultPayloadBytes)
	}
	return sha256Digest(payload), nil
}

// ComputeRuntimeCommitReceipt binds the command, immutable ExecutionRef, and
// complete terminal semantic result. Runtime returns this deterministic receipt
// only after re-observing the committed terminal row; it is not caller authority
// and requires no parallel receipt column.
func ComputeRuntimeCommitReceipt(result ApplicationResult) (string, error) {
	if !validApplicationResultContent(result) {
		return "", invalid()
	}
	var out bytes.Buffer
	writeBytes(&out, []byte("nexuscrm-runtime-application-result-commit-v1"))
	writeString(&out, result.CommandID)
	writeString(&out, result.ExecutionRefDigest)
	writeString(&out, string(result.Outcome))
	writeString(&out, result.ResultDigest)
	writeString(&out, string(result.FailureCode))
	return sha256Digest(out.Bytes()), nil
}

func sha256Digest(value []byte) string {
	digest := sha256.Sum256(value)
	return "sha256:" + hex.EncodeToString(digest[:])
}

func writeString(out *bytes.Buffer, value string) { writeBytes(out, []byte(value)) }

func writeBytes(out *bytes.Buffer, value []byte) {
	writeUint(out, uint64(len(value)))
	out.Write(value)
}

func writeUint(out *bytes.Buffer, value uint64) {
	var encoded [binary.MaxVarintLen64]byte
	n := binary.PutUvarint(encoded[:], value)
	out.Write(encoded[:n])
}
