package controlplane

import (
	"context"
	"database/sql"
	"fmt"
)

// applyAllSourceRevisionAdvanceExecutionAdmission is the only value that may
// carry a validated execution proof past the final installed-row reread. It is
// inert: it exposes no execution method and performs no writes.
type applyAllSourceRevisionAdvanceExecutionAdmission struct {
	identity *applyAllSourceRevisionAdvanceExecutionAdmissionIdentity
}

type applyAllSourceRevisionAdvanceExecutionAdmissionIdentity struct {
	proof      applyAllSourceRevisionAdvanceExecutionProof
	provenance applyAllSourceRevisionAdvanceExecutionProofProvenance
	minted     bool
}

func cloneApplyAllSourceRevisionAdvanceExecutionAdmission(
	admission applyAllSourceRevisionAdvanceExecutionAdmission,
) applyAllSourceRevisionAdvanceExecutionAdmission {
	if admission.identity == nil {
		return applyAllSourceRevisionAdvanceExecutionAdmission{}
	}
	return applyAllSourceRevisionAdvanceExecutionAdmission{
		identity: &applyAllSourceRevisionAdvanceExecutionAdmissionIdentity{
			proof:      cloneApplyAllSourceRevisionAdvanceExecutionProof(admission.identity.proof),
			provenance: cloneApplyAllSourceRevisionAdvanceExecutionProofProvenance(admission.identity.provenance),
			minted:     admission.identity.minted,
		},
	}
}

// admitApplyAllSourceRevisionAdvanceExecution must be called while the same
// control-plane advisory lock that covered proof construction remains held
// through admission consumption. The ApplyAll orchestrator owns and validates
// that scope; this inner boundary neither acquires nor accepts a separate lock.
func admitApplyAllSourceRevisionAdvanceExecution(
	ctx context.Context,
	db *sql.DB,
	proof applyAllSourceRevisionAdvanceExecutionProof,
) (applyAllSourceRevisionAdvanceExecutionAdmission, error) {
	if err := validateApplyAllSourceRevisionAdvanceExecutionProof(proof); err != nil {
		return applyAllSourceRevisionAdvanceExecutionAdmission{}, err
	}

	identity := &applyAllSourceRevisionAdvanceExecutionAdmissionIdentity{
		proof:      cloneApplyAllSourceRevisionAdvanceExecutionProof(proof),
		provenance: cloneApplyAllSourceRevisionAdvanceExecutionProofProvenance(proof.provenance),
		minted:     true,
	}
	if len(proof.entries) == 0 {
		admission := applyAllSourceRevisionAdvanceExecutionAdmission{identity: identity}
		if err := validateApplyAllSourceRevisionAdvanceExecutionAdmission(admission); err != nil {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, err
		}
		return admission, nil
	}
	if _, err := proof.activeRuntimeIdentity.requireDatabase(db); err != nil {
		return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
			ApplyAllSourceRevisionAdvancePlanCompose, -1, "", fmt.Errorf("admit execution proof: %w", err),
		)
	}
	admissionDB := identity.proof.provenance.activeRuntimeDatabase

	for index := range identity.proof.entries {
		entry := &identity.proof.entries[index]
		advance := entry.current.advance
		row, ok, err := getInstalledPackRowByNameForSourceRevisionAdvance(ctx, admissionDB, advance.Name)
		if err != nil {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
				ApplyAllSourceRevisionAdvancePlanRead, advance.index, advance.Name,
				fmt.Errorf("admit execution proof row reread: %w", err),
			)
		}
		if !ok {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
				ApplyAllSourceRevisionAdvancePlanState, advance.index, advance.Name,
				fmt.Errorf("installed pack row disappeared before execution admission"),
			)
		}
		if row != entry.current.row {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
				ApplyAllSourceRevisionAdvancePlanState, advance.index, advance.Name,
				fmt.Errorf("installed pack row changed before execution admission"),
			)
		}
		pending, err := classifyApplyAllSourceRevisionAdvance(
			row, advance.SourceRevisionAdvanceAuthorization,
		)
		if err != nil {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
				ApplyAllSourceRevisionAdvancePlanState, advance.index, advance.Name,
				fmt.Errorf("reclassify installed pack row for execution admission: %w", err),
			)
		}
		if !equalApplyAllSourceRevisionAdvancePending(pending, entry.current.pending) {
			return applyAllSourceRevisionAdvanceExecutionAdmission{}, applyAllSourceRevisionAdvancePlanError(
				ApplyAllSourceRevisionAdvancePlanState, advance.index, advance.Name,
				fmt.Errorf("pending source revision advance changed before execution admission"),
			)
		}

		entry.current.row = row
		entry.current.pending = nil
		if pending != nil {
			freshPending := *pending
			entry.current.pending = &freshPending
		}
	}
	admission := applyAllSourceRevisionAdvanceExecutionAdmission{identity: identity}
	if err := validateApplyAllSourceRevisionAdvanceExecutionAdmission(admission); err != nil {
		return applyAllSourceRevisionAdvanceExecutionAdmission{}, err
	}
	return admission, nil
}

func validateApplyAllSourceRevisionAdvanceExecutionAdmission(
	admission applyAllSourceRevisionAdvanceExecutionAdmission,
) error {
	if admission.identity == nil || !admission.identity.minted {
		return applyAllSourceRevisionAdvancePlanError(
			ApplyAllSourceRevisionAdvancePlanCompose, -1, "",
			fmt.Errorf("execution admission was not minted by its admission boundary"),
		)
	}
	if err := validateApplyAllSourceRevisionAdvanceExecutionProof(admission.identity.proof); err != nil {
		return err
	}
	witnessed := admission.identity.proof
	witnessed.provenance = admission.identity.provenance
	return validateApplyAllSourceRevisionAdvanceExecutionProof(witnessed)
}
