package catalogsystem

import (
	"context"
	"errors"
	"fmt"

	layoutbundlekeyv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundlekeyv1"
	layoutbundleownerverificationv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundleownerverificationv1"
	systemauthorityv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/systemlayoutbundleauthorityv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogpublication"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogverificationtx"
)

var errVerifiedSystemEvidenceHasNoJSON = errors.New(
	"verified system layout-bundle evidence capability has no JSON form",
)

// SystemLayoutBundleOwnerVerification is the complete success output for one
// exact source-required system-owned BundleKey. The values alone grant no
// authority; consumers must retain and validate Capability.
type SystemLayoutBundleOwnerVerification struct {
	Evidence       systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1
	EvidenceDigest string
	Receipt        layoutbundleownerverificationv1.Receipt
	ReceiptDigest  string
	Capability     *VerifiedSystemLayoutBundleEvidenceCapability
}

// VerifiedSystemLayoutBundleEvidenceCapability is an opaque, process-local
// proof that the system authority derived Evidence and Receipt while their
// exact source and system-slot read fences were live.
type VerifiedSystemLayoutBundleEvidenceCapability struct {
	authority      *authorityIdentity
	claim          *catalogpublication.CandidateSourceSystemVerification
	evidence       systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1
	evidenceDigest string
	receipt        layoutbundleownerverificationv1.Receipt
	receiptDigest  string
	slotDigest     string
}

// VerifyLayoutBundleOwner revalidates the exact locked source membership,
// ACTIVE system slot, and immutable creation lineage. It accepts no caller
// evidence, receipt, SQL, query callback, or generic proof adapter.
func (a *Authority) VerifyLayoutBundleOwner(
	ctx context.Context,
	source *catalogpublication.CandidateSourceVerificationLease,
	key layoutbundlekeyv1.BundleKey,
) (SystemLayoutBundleOwnerVerification, error) {
	if a == nil || a.db == nil || a.identity == nil || source == nil {
		return SystemLayoutBundleOwnerVerification{}, ErrConflict
	}
	claim, err := source.BeginSystemBundleKeyVerification(key)
	if err != nil {
		return SystemLayoutBundleOwnerVerification{}, mapSystemOwnerVerificationError(err)
	}
	complete := false
	defer func() {
		if !complete {
			claim.Abort()
		}
	}()

	ownerFact, err := catalogverificationtx.VerifySystemOwnerClaim(ctx, a.db, claim)
	if err != nil {
		return SystemLayoutBundleOwnerVerification{}, mapSystemOwnerVerificationError(err)
	}
	sourceFacts, err := claim.Facts()
	if err != nil {
		return SystemLayoutBundleOwnerVerification{}, mapSystemOwnerVerificationError(err)
	}
	if err := validateSystemOwnerFactAgainstSource(sourceFacts, ownerFact); err != nil {
		return SystemLayoutBundleOwnerVerification{}, err
	}

	evidence, evidenceDigest, receipt, receiptDigest, err :=
		deriveSystemLayoutBundleOwnerValues(sourceFacts, ownerFact)
	if err != nil {
		return SystemLayoutBundleOwnerVerification{}, err
	}
	if err := catalogverificationtx.CompleteSystemClaim(claim, receipt); err != nil {
		return SystemLayoutBundleOwnerVerification{}, mapSystemOwnerVerificationError(err)
	}

	capability := &VerifiedSystemLayoutBundleEvidenceCapability{
		authority:      a.identity,
		claim:          claim,
		evidence:       evidence,
		evidenceDigest: evidenceDigest,
		receipt:        receipt,
		receiptDigest:  receiptDigest,
		slotDigest:     ownerFact.SlotDigest,
	}
	if err := capability.Validate(); err != nil {
		return SystemLayoutBundleOwnerVerification{}, err
	}
	complete = true
	return SystemLayoutBundleOwnerVerification{
		Evidence:       evidence,
		EvidenceDigest: evidenceDigest,
		Receipt:        receipt,
		ReceiptDigest:  receiptDigest,
		Capability:     capability,
	}, nil
}

func validateSystemOwnerFactAgainstSource(
	sourceFacts catalogverificationtx.SystemSourceFacts,
	ownerFact catalogverificationtx.SystemOwnerFact,
) error {
	if sourceFacts.SourceCatalogSnapshot.OrgID != sourceFacts.OrgID ||
		portableSystemBundleKey(ownerFact.Slot.BundleKey) != sourceFacts.BundleKey {
		return fmt.Errorf(
			"%w: system owner fact differs from the exact source OrgID or BundleKey",
			ErrConflict,
		)
	}
	if err := systemauthorityv1.ValidateSlotValue(ownerFact.Slot); err != nil {
		return fmt.Errorf("%w: locked system slot value: %v", ErrCorrupt, err)
	}
	if ownerFact.Slot.State != systemauthorityv1.SlotStateActive {
		return fmt.Errorf("%w: locked system slot is not ACTIVE", ErrCorrupt)
	}
	slotDigest, err := systemauthorityv1.SlotValueDigest(ownerFact.Slot)
	if err != nil || slotDigest != ownerFact.SlotDigest {
		return fmt.Errorf("%w: locked system slot digest differs", ErrCorrupt)
	}
	return nil
}

func deriveSystemLayoutBundleOwnerValues(
	sourceFacts catalogverificationtx.SystemSourceFacts,
	ownerFact catalogverificationtx.SystemOwnerFact,
) (
	systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1,
	string,
	layoutbundleownerverificationv1.Receipt,
	string,
	error,
) {
	evidence := systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1{
		EvidenceVersion:       systemauthorityv1.EvidenceVersion,
		SourceCatalogSnapshot: sourceFacts.SourceCatalogSnapshot,
		BundleKey:             ownerFact.Slot.BundleKey,
		SlotValueVersion:      ownerFact.Slot.ValueVersion,
		SlotState:             ownerFact.Slot.State,
	}
	evidenceDigest, err := systemauthorityv1.EvidenceDigest(evidence)
	if err != nil {
		return systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1{}, "", layoutbundleownerverificationv1.Receipt{}, "", fmt.Errorf(
			"%w: derive system authority evidence: %v",
			ErrCorrupt,
			err,
		)
	}
	receipt := layoutbundleownerverificationv1.Receipt{
		ReceiptVersion:              layoutbundleownerverificationv1.ReceiptVersion,
		OrgID:                       sourceFacts.OrgID,
		WriterCommandID:             sourceFacts.WriterCommandID,
		WriterReservationGeneration: sourceFacts.WriterReservationGeneration,
		SourceRequestDigest:         sourceFacts.SourceRequestDigest,
		RequiredBundleKeySetDigest:  sourceFacts.RequiredBundleKeySetDigest,
		BundleKey:                   sourceFacts.BundleKey,
		EvidenceVersion:             evidence.EvidenceVersion,
		EvidenceDigest:              evidenceDigest,
		VerificationState:           layoutbundleownerverificationv1.VerificationStateVerified,
	}
	receiptDigest, err := layoutbundleownerverificationv1.ReceiptDigest(receipt)
	if err != nil {
		return systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1{}, "", layoutbundleownerverificationv1.Receipt{}, "", fmt.Errorf(
			"%w: derive system owner receipt: %v",
			ErrCorrupt,
			err,
		)
	}
	return evidence, evidenceDigest, receipt, receiptDigest, nil
}

// Validate proves that the exact source claim, system owner fence, evidence,
// receipt, and private slot digest bound when this capability was minted are
// still live and coherent.
func (capability *VerifiedSystemLayoutBundleEvidenceCapability) Validate() error {
	if capability == nil ||
		capability.authority == nil ||
		capability.claim == nil {
		return ErrConflict
	}
	if err := systemauthorityv1.ValidateEvidence(capability.evidence); err != nil {
		return fmt.Errorf("%w: bound system evidence: %v", ErrCorrupt, err)
	}
	evidenceDigest, err := systemauthorityv1.EvidenceDigest(capability.evidence)
	if err != nil || evidenceDigest != capability.evidenceDigest {
		return fmt.Errorf("%w: bound system evidence digest differs", ErrCorrupt)
	}
	if err := layoutbundleownerverificationv1.ValidateReceipt(capability.receipt); err != nil {
		return fmt.Errorf("%w: bound system owner receipt: %v", ErrCorrupt, err)
	}
	receiptDigest, err := layoutbundleownerverificationv1.ReceiptDigest(capability.receipt)
	if err != nil || receiptDigest != capability.receiptDigest {
		return fmt.Errorf("%w: bound system owner receipt digest differs", ErrCorrupt)
	}

	evidenceKey := portableSystemBundleKey(capability.evidence.BundleKey)
	if capability.receipt.BundleKey != evidenceKey ||
		capability.receipt.EvidenceVersion != capability.evidence.EvidenceVersion ||
		capability.receipt.EvidenceDigest != capability.evidenceDigest {
		return fmt.Errorf("%w: bound system evidence and receipt differ", ErrCorrupt)
	}
	slot := systemauthorityv1.SystemLayoutBundleSlotV1{
		ValueVersion: capability.evidence.SlotValueVersion,
		BundleKey:    capability.evidence.BundleKey,
		State:        capability.evidence.SlotState,
	}
	slotDigest, err := systemauthorityv1.SlotValueDigest(slot)
	if err != nil || slotDigest != capability.slotDigest {
		return fmt.Errorf("%w: bound system slot digest differs", ErrCorrupt)
	}

	sourceFacts, err := capability.claim.Facts()
	if err != nil ||
		sourceFacts.SourceCatalogSnapshot != capability.evidence.SourceCatalogSnapshot ||
		sourceFacts.BundleKey != capability.receipt.BundleKey ||
		sourceFacts.OrgID != capability.receipt.OrgID ||
		sourceFacts.WriterCommandID != capability.receipt.WriterCommandID ||
		sourceFacts.WriterReservationGeneration != capability.receipt.WriterReservationGeneration ||
		sourceFacts.SourceRequestDigest != capability.receipt.SourceRequestDigest ||
		sourceFacts.RequiredBundleKeySetDigest != capability.receipt.RequiredBundleKeySetDigest {
		return ErrConflict
	}
	// Re-prove durable session and advisory-lock ownership last, immediately
	// before the capability is accepted or any bound value is returned.
	if !capability.claim.Active() {
		return ErrConflict
	}
	return nil
}

// Evidence returns the exact immutable evidence only while the capability is
// still valid.
func (capability *VerifiedSystemLayoutBundleEvidenceCapability) Evidence() (
	systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1,
	error,
) {
	if err := capability.Validate(); err != nil {
		return systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1{}, err
	}
	return capability.evidence, nil
}

// Receipt returns the exact source-bound receipt only while the capability is
// still valid.
func (capability *VerifiedSystemLayoutBundleEvidenceCapability) Receipt() (
	layoutbundleownerverificationv1.Receipt,
	error,
) {
	if err := capability.Validate(); err != nil {
		return layoutbundleownerverificationv1.Receipt{}, err
	}
	return capability.receipt, nil
}

// EvidenceDigest returns the exact evidence digest bound by the capability.
func (capability *VerifiedSystemLayoutBundleEvidenceCapability) EvidenceDigest() (
	string,
	error,
) {
	if err := capability.Validate(); err != nil {
		return "", err
	}
	return capability.evidenceDigest, nil
}

// ReceiptDigest returns the exact singular receipt digest bound by the
// capability.
func (capability *VerifiedSystemLayoutBundleEvidenceCapability) ReceiptDigest() (
	string,
	error,
) {
	if err := capability.Validate(); err != nil {
		return "", err
	}
	return capability.receiptDigest, nil
}

// MarshalJSON rejects persistence of a live system owner fence.
func (*VerifiedSystemLayoutBundleEvidenceCapability) MarshalJSON() ([]byte, error) {
	return nil, errVerifiedSystemEvidenceHasNoJSON
}

// UnmarshalJSON rejects caller construction of a live system owner fence.
func (*VerifiedSystemLayoutBundleEvidenceCapability) UnmarshalJSON([]byte) error {
	return errVerifiedSystemEvidenceHasNoJSON
}

func portableSystemBundleKey(
	key systemauthorityv1.BundleKey,
) layoutbundlekeyv1.BundleKey {
	return layoutbundlekeyv1.BundleKey{
		SlotOwnerKind:      key.SlotOwnerKind,
		SlotOwnerNamespace: key.SlotOwnerNamespace,
		LocalComponentKey:  key.LocalComponentKey,
	}
}

func mapSystemOwnerVerificationError(err error) error {
	switch {
	case errors.Is(err, catalogverificationtx.ErrSystemOwnerNotFound):
		return fmt.Errorf("%w: system owner fact is absent", ErrNotFound)
	case errors.Is(err, catalogverificationtx.ErrSystemOwnerCorrupt):
		return fmt.Errorf("%w: %v", ErrCorrupt, err)
	case errors.Is(err, catalogverificationtx.ErrSystemOwnerConflict),
		errors.Is(err, catalogverificationtx.ErrClosed),
		errors.Is(err, catalogverificationtx.ErrAuthorityMismatch),
		errors.Is(err, catalogpublication.ErrSourceConflict),
		errors.Is(err, catalogpublication.ErrSourceNotFound),
		errors.Is(err, catalogpublication.ErrSourceNotReady):
		return fmt.Errorf("%w: %v", ErrConflict, err)
	case errors.Is(err, catalogpublication.ErrSourceCorrupt):
		return fmt.Errorf("%w: %v", ErrCorrupt, err)
	default:
		return err
	}
}
