package controlplane

import (
	"context"
	"fmt"

	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

// applyAllSourceRevisionAdvanceSnapshots is an opaque, attempt-bound
// observation of the source-backed state that must be restored if the selected
// execution atom fails. It performs no registration, execution, restoration,
// or terminal lifecycle transition.
type applyAllSourceRevisionAdvanceSnapshots struct {
	identity *applyAllSourceRevisionAdvanceSnapshotsIdentity
	witness  applyAllSourceRevisionAdvanceSnapshotsProvenance
}

type applyAllSourceRevisionAdvanceSnapshotsIdentity struct {
	attempt                applyAllSourceRevisionAdvanceAttempt
	selectedAtomClaim      *applyAllSourceRevisionAdvanceSelectedAtomClaim
	childCloneRules        *packs.ObjectSpecChildCloneRulePackOwnershipSnapshot
	exclusiveSiblingFields *packs.ObjectSpecExclusiveSiblingFieldPackOwnershipSnapshot
	connectedApps          *packs.ConnectedAppPackSnapshot
	provenance             *applyAllSourceRevisionAdvanceSnapshotsProvenance
	minted                 bool
}

// applyAllSourceRevisionAdvanceSnapshotsProvenance binds the three opaque
// pack-owned snapshots to the exact validated attempt identity that selected
// their database and target. All other execution authority remains owned by
// that attempt and is validated transitively rather than duplicated here.
type applyAllSourceRevisionAdvanceSnapshotsProvenance struct {
	attemptIdentity        *applyAllSourceRevisionAdvanceAttemptIdentity
	selectedAtomClaim      *applyAllSourceRevisionAdvanceSelectedAtomClaim
	childCloneRules        *packs.ObjectSpecChildCloneRulePackOwnershipSnapshot
	exclusiveSiblingFields *packs.ObjectSpecExclusiveSiblingFieldPackOwnershipSnapshot
	connectedApps          *packs.ConnectedAppPackSnapshot
}

type applyAllSourceRevisionAdvanceSnapshotsState struct {
	attempt                applyAllSourceRevisionAdvanceAttemptState
	childCloneRules        *packs.ObjectSpecChildCloneRulePackOwnershipSnapshot
	exclusiveSiblingFields *packs.ObjectSpecExclusiveSiblingFieldPackOwnershipSnapshot
	connectedApps          *packs.ConnectedAppPackSnapshot
}

// captureApplyAllSourceRevisionAdvanceSnapshots observes the three existing
// pack-owned rollback surfaces in their lifecycle acquisition order.
func captureApplyAllSourceRevisionAdvanceSnapshots(
	ctx context.Context,
	attempt applyAllSourceRevisionAdvanceAttempt,
) (applyAllSourceRevisionAdvanceSnapshots, error) {
	state, err := requireApplyAllSourceRevisionAdvanceAttempt(attempt, "snapshot")
	if err != nil {
		return applyAllSourceRevisionAdvanceSnapshots{}, err
	}
	childCloneRules, err := packs.SnapshotObjectSpecChildCloneRulePackOwnershipForPack(
		ctx, state.database, state.target,
	)
	if err != nil {
		return applyAllSourceRevisionAdvanceSnapshots{}, applyAllSourceRevisionAdvanceAttemptError(
			"snapshot", state.advance,
			fmt.Errorf("capture ChildCloneRules ownership: %w", err),
		)
	}
	exclusiveSiblingFields, err := packs.SnapshotObjectSpecExclusiveSiblingFieldPackOwnershipForPack(
		ctx, state.database, state.target,
	)
	if err != nil {
		return applyAllSourceRevisionAdvanceSnapshots{}, applyAllSourceRevisionAdvanceAttemptError(
			"snapshot", state.advance,
			fmt.Errorf("capture ExclusiveSiblingFields ownership: %w", err),
		)
	}
	connectedApps, err := packs.SnapshotConnectedAppPackState(
		ctx, state.database, state.target.Name(),
	)
	if err != nil {
		return applyAllSourceRevisionAdvanceSnapshots{}, applyAllSourceRevisionAdvanceAttemptError(
			"snapshot", state.advance,
			fmt.Errorf("capture ConnectedApp state: %w", err),
		)
	}
	identity := &applyAllSourceRevisionAdvanceSnapshotsIdentity{
		attempt: attempt, selectedAtomClaim: attempt.identity.selectedAtomClaim,
		childCloneRules:        childCloneRules,
		exclusiveSiblingFields: exclusiveSiblingFields, connectedApps: connectedApps,
		minted: true,
	}
	identity.provenance = &applyAllSourceRevisionAdvanceSnapshotsProvenance{
		attemptIdentity: attempt.identity, selectedAtomClaim: identity.selectedAtomClaim,
		childCloneRules:        childCloneRules,
		exclusiveSiblingFields: exclusiveSiblingFields, connectedApps: connectedApps,
	}
	snapshots := applyAllSourceRevisionAdvanceSnapshots{
		identity: identity,
		witness:  *identity.provenance,
	}
	if _, err := requireApplyAllSourceRevisionAdvanceSnapshots(snapshots); err != nil {
		return applyAllSourceRevisionAdvanceSnapshots{}, err
	}
	return snapshots, nil
}

func requireApplyAllSourceRevisionAdvanceSnapshots(
	snapshots applyAllSourceRevisionAdvanceSnapshots,
) (applyAllSourceRevisionAdvanceSnapshotsState, error) {
	identity := snapshots.identity
	if identity == nil || !identity.minted || identity.provenance == nil {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, fmt.Errorf(
			"source-backed snapshots were not minted from an exact attempt",
		)
	}
	provenance := identity.provenance
	witness := snapshots.witness
	if identity.attempt.identity == nil || identity.attempt.identity != provenance.attemptIdentity ||
		identity.attempt.identity != witness.attemptIdentity {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, fmt.Errorf(
			"source-backed snapshots changed attempt identity",
		)
	}
	if identity.selectedAtomClaim == nil ||
		identity.selectedAtomClaim != identity.attempt.identity.selectedAtomClaim ||
		identity.selectedAtomClaim != provenance.selectedAtomClaim ||
		identity.selectedAtomClaim != witness.selectedAtomClaim {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, fmt.Errorf(
			"source-backed snapshots changed selected-atom claim",
		)
	}
	if identity.childCloneRules == nil || identity.exclusiveSiblingFields == nil || identity.connectedApps == nil {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, fmt.Errorf(
			"source-backed snapshots are incomplete",
		)
	}
	if identity.childCloneRules != provenance.childCloneRules || identity.childCloneRules != witness.childCloneRules ||
		identity.exclusiveSiblingFields != provenance.exclusiveSiblingFields ||
		identity.exclusiveSiblingFields != witness.exclusiveSiblingFields ||
		identity.connectedApps != provenance.connectedApps || identity.connectedApps != witness.connectedApps {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, fmt.Errorf(
			"source-backed snapshot identity changed",
		)
	}
	attemptState, err := requireApplyAllSourceRevisionAdvanceAttempt(identity.attempt, "use snapshots for")
	if err != nil {
		return applyAllSourceRevisionAdvanceSnapshotsState{}, err
	}
	return applyAllSourceRevisionAdvanceSnapshotsState{
		attempt: attemptState, childCloneRules: identity.childCloneRules,
		exclusiveSiblingFields: identity.exclusiveSiblingFields,
		connectedApps:          identity.connectedApps,
	}, nil
}
