package catalogsystem

import (
	"fmt"

	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogsystemprotocol"
)

func completeOutcome(command durableCommand, result Result) (durableOutcome, error) {
	outcome := durableOutcome{
		CommandID:     command.CommandID,
		RequestDigest: command.RequestDigest,
		Principal:     command.Principal,
		Result:        result,
	}
	digest, err := outcomeDigest(outcome)
	if err != nil {
		return durableOutcome{}, err
	}
	outcome.OutcomeDigest = digest
	return outcome, nil
}

func validateDurableOutcome(outcome durableOutcome, command durableCommand) error {
	if outcome.CommandID != command.CommandID ||
		outcome.RequestDigest != command.RequestDigest ||
		outcome.Principal != command.Principal {
		return fmt.Errorf("%w: outcome binding differs from command", ErrCorrupt)
	}
	if !validResult(outcome.Result) {
		return fmt.Errorf("%w: invalid outcome result %q", ErrCorrupt, outcome.Result)
	}
	if err := validateDigest(outcome.OutcomeDigest); err != nil {
		return fmt.Errorf("%w: invalid outcome digest: %v", ErrCorrupt, err)
	}
	computed, err := outcomeDigest(outcome)
	if err != nil {
		return fmt.Errorf("%w: cannot recompute outcome: %v", ErrCorrupt, err)
	}
	if computed != outcome.OutcomeDigest {
		return fmt.Errorf("%w: outcome digest differs", ErrCorrupt)
	}
	return nil
}

func outcomeDigest(outcome durableOutcome) (string, error) {
	return catalogsystemprotocol.OutcomeDigest(
		outcome.CommandID,
		outcome.RequestDigest,
		outcome.Principal.PrincipalID,
		outcome.Principal.AuditDigest,
		catalogsystemprotocol.Result(outcome.Result),
	)
}

func publicOutcome(outcome durableOutcome) commandOutcome {
	return commandOutcome{
		CommandID:     outcome.CommandID,
		RequestDigest: outcome.RequestDigest,
		Result:        outcome.Result,
		OutcomeDigest: outcome.OutcomeDigest,
	}
}

func validResult(result Result) bool {
	return catalogsystemprotocol.ValidateResult(
		catalogsystemprotocol.Result(result),
	) == nil
}
