package tokens

import (
	"fmt"

	"github.com/lightsparkdev/spark/common/logging"
	tokenpb "github.com/lightsparkdev/spark/proto/spark_token"
	sparkerrors "github.com/lightsparkdev/spark/so/errors"

	"github.com/lightsparkdev/spark/so/ent"
)

// Error message constants
const (
	ErrIdentityPublicKeyAuthFailed        = "identity public key authentication failed"
	ErrInvalidPartialTokenTransaction     = "invalid partial token transaction"
	ErrFailedToHashPartialTransaction     = "failed to hash partial token transaction"
	ErrFailedToFetchPartialTransaction    = "failed to fetch partial token transaction data"
	ErrFailedToFetchTransaction           = "failed to fetch transaction"
	ErrFailedToGetUnusedKeyshares         = "failed to get unused signing keyshares"
	ErrNotEnoughUnusedKeyshares           = "not enough unused signing keyshares available"
	ErrFailedToGetNetworkFromProto        = "failed to get network from proto network"
	ErrFailedToExecuteWithNonCoordinator  = "failed to execute start token transaction with non-coordinator operators"
	ErrFailedToExecuteWithCoordinator     = "failed to execute start token transaction with coordinator"
	ErrFailedToGetKeyshareInfo            = "failed to get keyshare info"
	ErrFailedToGetCreationEntityPublicKey = "failed to get creation entity public key"
	ErrFailedToHashFinalTransaction       = "failed to hash final token transaction"
	ErrFailedToConnectToOperator          = "failed to connect to operator: %s"
	ErrFailedToExecuteWithOperator        = "failed to execute start token transaction with operator: %s"
	ErrFailedToGetOperatorList            = "failed to get operator list"
	ErrFailedToSendToLRC20Node            = "failed to send transaction to LRC20 node"
	ErrFailedToUpdateOutputs              = "failed to update outputs after %s"
	ErrFailedToGetKeyshareForOutput       = "failed to get keyshare for output"
	ErrFailedToQueryTokenFreezeStatus     = "failed to query token freeze status"
	ErrTransactionNotCoordinatedBySO      = "transaction not coordinated by this SO"
	ErrFailedToGetOwnedOutputStats        = "failed to get owned output stats"
	ErrFailedToParseRevocationPrivateKey  = "failed to parse revocation private key"
	ErrFailedToValidateRevocationKeys     = "failed to validate revocation keys"
	ErrRevocationKeyMismatch              = "keyshare public key does not match output revocation commitment"
	ErrInvalidOutputs                     = "found invalid outputs"
	ErrInvalidInputs                      = "found invalid inputs"
	ErrFailedToMarshalTokenTransaction    = "failed to marshal token transaction"
	ErrMultipleActiveFreezes              = "multiple active freezes found for this owner and token which should not happen"
	ErrNoActiveFreezes                    = "no active freezes found to thaw"
	ErrAlreadyFrozen                      = "tokens are already frozen for this owner and token"
	ErrFailedToCreateTokenFreeze          = "failed to create token freeze entity"
	ErrFailedToUpdateTokenFreeze          = "failed to update token freeze status to thawed"
	ErrInvalidOutputIDFormat              = "invalid output ID format"
	ErrFailedToQueryTokenTransactions     = "unable to query token transactions"
	ErrInvalidOperatorResponse            = "invalid response from operator"
	ErrTransactionAlreadyFinalized        = "transaction has already been finalized by at least one operator, cannot cancel"
	ErrTooManyOperatorsSigned             = "transaction has been signed by %d operators, which exceeds the cancellation threshold of %d"
	ErrInvalidTransactionStatus           = "transaction is in status %s, but must be in %s status to cancel"
	ErrStoredOperatorSignatureInvalid     = "stored operator signature is invalid"
	ErrFailedToGetRevocationKeyshares     = "failed to get revocation keyshares for transaction"
	ErrFailedToConnectToOperatorForCancel = "failed to connect to operator %s"
	ErrFailedToQueryOperatorForCancel     = "failed to execute query with operator %s"
	ErrFailedToExecuteWithAllOperators    = "failed to execute query with all operators"
	ErrInputIndexOutOfRange               = "input index %d out of range (0-%d)"
	ErrInvalidOwnerSignature              = "invalid owner signature for output"
	ErrInvalidIssuerSignature             = "invalid issuer signature for mint"
	ErrFailedToHashRevocationKeyshares    = "failed to hash revocation keyshares payload"
	ErrTransactionHashMismatch            = "transaction hash in payload (%x) does not match actual transaction hash (%x)"
	ErrOperatorPublicKeyMismatch          = "operator identity public key in payload (%v) does not match this SO's identity public key (%v)"
	ErrInvalidValidityDuration            = "invalid validity duration"
	ErrTransactionPreemptedByExisting     = "transaction pre-empted by existing transaction due to existing transaction having %s (%s)"
	ErrFailedToCancelPreemptedTransaction = "failed to cancel pre-empted transaction"
	ErrFailedToConvertTokenProto          = "failed to convert token proto to spark proto (%s->%s)"
	ErrTokenAlreadyCreatedForIssuer       = "token already created for this issuer"
	ErrFailedToDecodeSparkInvoice         = "failed to decode spark invoice"
	ErrInvalidSparkInvoice                = "invalid spark invoice"
	ErrSparkInvoiceExpired                = "spark invoice expired"
	ErrTransactionPreempted               = "transaction preempted"
)

func FormatErrorWithTransactionEnt(msg string, tokenTransaction *ent.TokenTransaction, err error) error {
	if tokenTransaction == nil {
		return fmt.Errorf("nil token transaction in format error with transaction ent: message: %s, error: %w", msg, err)
	}
	return fmt.Errorf("%s (uuid: %s, hash: %x): %w",
		msg,
		tokenTransaction.ID.String(),
		tokenTransaction.FinalizedTokenTransactionHash,
		err)
}

func FormatErrorWithTransactionProto(msg string, tokenTransaction *tokenpb.TokenTransaction, err error) error {
	formatted := logging.FormatProto("transaction", tokenTransaction)
	if err != nil {
		return fmt.Errorf("%s %s: %w", msg, formatted, err)
	}
	return fmt.Errorf("%s %s", msg, formatted)
}

func FormatErrorWithTransactionProtoAndSparkInvoice(msg string, tokenTransaction *tokenpb.TokenTransaction, sparkInvoice string, err error) error {
	formatted := logging.FormatProto("transaction", tokenTransaction)
	if err != nil {
		return fmt.Errorf("%s %s, spark invoice: %s: %w", msg, formatted, sparkInvoice, err)
	}
	return fmt.Errorf("%s %s, spark invoice: %s", msg, formatted, sparkInvoice)
}

func NewTransactionPreemptedError(tokenTransaction *tokenpb.TokenTransaction, reason, details string) error {
	formattedError := FormatErrorWithTransactionProto(
		fmt.Sprintf(ErrTransactionPreemptedByExisting, reason, details),
		tokenTransaction,
		sparkerrors.AlreadyExistsDuplicateOperation(fmt.Errorf("Inputs cannot be spent: token transaction with these inputs is already in progress or finalized")),
	)
	return sparkerrors.AbortedTransactionPreempted(formattedError)
}

func NewTokenAlreadyCreatedError(tokenTransaction *tokenpb.TokenTransaction) error {
	formattedError := FormatErrorWithTransactionProto(ErrTokenAlreadyCreatedForIssuer, tokenTransaction, nil)
	return sparkerrors.AlreadyExistsDuplicateOperation(formattedError)
}
