package cmd

import (
	"context"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"slices"
	"strings"
	"syscall"
	"time"

	cpaws "github.com/tidbcloud/controlplane9/internal/aws"
	"github.com/tidbcloud/controlplane9/internal/cluster"
	"github.com/tidbcloud/controlplane9/internal/config"
	"github.com/tidbcloud/controlplane9/internal/host"
	cptilt "github.com/tidbcloud/controlplane9/internal/tilt"
)

const destroyCleanupHandoffReceiptVersion = "cp9.destroy-cleanup-handoff"
const destroyCleanupSessionGuardVersion = "cp9.destroy-cleanup-session-guard"

var errDestroyCloudCleanupHandoffGenerationUnavailable = errors.New(
	"cleanup workflow dispatch requires an immutable session generation timestamp",
)

type destroyCloudCleanupHandoffReceipt struct {
	Version             string                              `json:"version"`
	Status              string                              `json:"status"`
	PreparedAt          time.Time                           `json:"prepared_at"`
	AcceptedAt          time.Time                           `json:"accepted_at,omitzero"`
	Owner               string                              `json:"owner"`
	SessionID           string                              `json:"session_id"`
	AccountID           string                              `json:"account_id"`
	Regions             []string                            `json:"regions"`
	GenerationID        string                              `json:"generation_id"`
	ResourceFingerprint string                              `json:"resource_fingerprint"`
	DispatchID          string                              `json:"dispatch_id"`
	Repo                string                              `json:"repo"`
	Workflow            string                              `json:"workflow"`
	Ref                 string                              `json:"ref"`
	KnownResources      []cpaws.Resource                    `json:"known_resources,omitempty"`
	GCP                 *destroyGCPRegionalCleanupScope     `json:"gcp,omitempty"`
	Handoff             *destroyCloudCleanupWorkflowHandoff `json:"handoff,omitempty"`
}

type destroyGCPRegionalCleanupScope struct {
	ProjectID                  string   `json:"project_id"`
	Region                     string   `json:"region"`
	Zone                       string   `json:"zone"`
	InstanceName               string   `json:"instance_name"`
	Network                    string   `json:"network"`
	Subnet                     string   `json:"subnet"`
	Address                    string   `json:"address"`
	FirewallRules              []string `json:"firewall_rules"`
	O11YPSCSubnet              string   `json:"o11y_psc_subnet"`
	O11YInstanceGroup          string   `json:"o11y_instance_group"`
	O11YHealthCheck            string   `json:"o11y_health_check"`
	O11YBackendService         string   `json:"o11y_backend_service"`
	O11YForwardingRule         string   `json:"o11y_forwarding_rule"`
	O11YServiceAttachment      string   `json:"o11y_service_attachment"`
	O11YBucket                 string   `json:"o11y_bucket"`
	RuntimeServiceAccountEmail string   `json:"runtime_service_account_email"`
	TTLExpiresAt               string   `json:"ttl_expires_at"`
}

type destroyCloudCleanupSessionGuard struct {
	Version     string    `json:"version"`
	RecordedAt  time.Time `json:"recorded_at"`
	ReceiptPath string    `json:"receipt_path"`
}

var errDestroyCloudCleanupDispatchNotSent = errors.New("cleanup workflow dispatch was definitely not sent")

func destroyCloudCleanupDispatchNotSent(err error) error {
	if err == nil {
		return nil
	}
	return fmt.Errorf("%w: %w", errDestroyCloudCleanupDispatchNotSent, err)
}

func isDestroyCloudCleanupDispatchDefinitelyNotSent(err error) bool {
	return errors.Is(err, errDestroyCloudCleanupDispatchNotSent) || errors.Is(err, exec.ErrNotFound)
}

func encodeDestroyCloudCleanupScopeReceipt(receipt *destroyCloudCleanupHandoffReceipt) (string, error) {
	data, err := json.Marshal(receipt)
	if err != nil {
		return "", fmt.Errorf("marshal cleanup handoff scope: %w", err)
	}
	return base64.StdEncoding.EncodeToString(data), nil
}

func ensureDestroyCloudCleanupHandoff(
	ctx context.Context,
	destroyCtx *v1alpha2DestroyContext,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	prepared, path, err := prepareDestroyCloudCleanupHandoffReceipt(destroyCtx)
	if err != nil {
		if errors.Is(err, errDestroyCloudCleanupHandoffGenerationUnavailable) {
			return reuseAcceptedDestroyCloudCleanupHandoffWithoutSessionState(destroyCtx)
		}
		return nil, path, false, err
	}
	if existing, loadErr := loadDestroyCloudCleanupHandoffReceipt(path); loadErr == nil {
		return reuseDestroyCloudCleanupHandoffReceipt(ctx, destroyCtx, path, existing, prepared)
	} else if !errors.Is(loadErr, os.ErrNotExist) {
		return nil, path, false, loadErr
	}

	created, err := createDestroyCloudCleanupHandoffReceipt(path, prepared)
	if err != nil {
		return nil, path, false, fmt.Errorf("persist prepared cleanup handoff receipt: %w", err)
	}
	if !created {
		existing, err := loadDestroyCloudCleanupHandoffReceipt(path)
		if err != nil {
			return nil, path, false, fmt.Errorf("load concurrently prepared cleanup handoff receipt: %w", err)
		}
		return reuseDestroyCloudCleanupHandoffReceipt(ctx, destroyCtx, path, existing, prepared)
	}
	return dispatchPreparedDestroyCloudCleanupHandoff(ctx, destroyCtx, path, prepared)
}

// acceptedDestroyCloudCleanupHandoffExists identifies a durable handoff that
// may already have removed either remote runtime. A retry must not require a
// vanished Dedicated API or GCE host after cleanup ownership transferred.
func acceptedDestroyCloudCleanupHandoffExists(destroyCtx *v1alpha2DestroyContext) (bool, error) {
	prepared, path, err := prepareDestroyCloudCleanupHandoffReceipt(destroyCtx)
	if err != nil {
		return false, err
	}
	existing, err := loadDestroyCloudCleanupHandoffReceipt(path)
	if errors.Is(err, os.ErrNotExist) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	if err := validateDestroyCloudCleanupHandoffReceipt(existing, prepared); err != nil {
		return false, err
	}
	if existing.Status != "accepted" {
		return false, nil
	}
	if existing.Handoff == nil {
		return false, fmt.Errorf("accepted cleanup handoff receipt %s has no durable handoff", path)
	}
	if err := validateAcceptedDestroyCloudCleanupHandoff(existing.Handoff, existing); err != nil {
		return false, err
	}
	return true, nil
}

func reuseDestroyCloudCleanupHandoffReceipt(
	ctx context.Context,
	destroyCtx *v1alpha2DestroyContext,
	path string,
	existing *destroyCloudCleanupHandoffReceipt,
	prepared *destroyCloudCleanupHandoffReceipt,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	if err := validateDestroyCloudCleanupHandoffReceipt(existing, prepared); err != nil {
		return nil, path, false, err
	}
	if existing.Status == "accepted" && existing.Handoff != nil {
		return existing.Handoff, path, true, nil
	}
	if existing.Status == "prepared" {
		return dispatchPreparedDestroyCloudCleanupHandoff(ctx, destroyCtx, path, existing)
	}
	handoff, reconcileErr := destroyCloudCleanupWorkflowReconcile(ctx, destroyCtx, existing)
	if reconcileErr != nil {
		return nil, path, false, fmt.Errorf(
			"cleanup handoff receipt %s remains prepared; exact dispatch %q is not yet observable and duplicate dispatch is refused: %w",
			path,
			existing.DispatchID,
			reconcileErr,
		)
	}
	if err := acceptDestroyCloudCleanupHandoffReceipt(path, existing, handoff); err != nil {
		return nil, path, false, err
	}
	return handoff, path, true, nil
}

func dispatchPreparedDestroyCloudCleanupHandoff(
	ctx context.Context,
	destroyCtx *v1alpha2DestroyContext,
	path string,
	receipt *destroyCloudCleanupHandoffReceipt,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	claim, claimed, err := claimDestroyCloudCleanupHandoffDispatch(path)
	if err != nil {
		return nil, path, false, err
	}
	if !claimed {
		return waitForDestroyCloudCleanupHandoffDispatchClaim(ctx, destroyCtx, path, receipt)
	}
	return dispatchClaimedDestroyCloudCleanupHandoff(ctx, destroyCtx, path, receipt, claim)
}

func dispatchClaimedDestroyCloudCleanupHandoff(
	ctx context.Context,
	destroyCtx *v1alpha2DestroyContext,
	path string,
	receipt *destroyCloudCleanupHandoffReceipt,
	claim *os.File,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	defer releaseDestroyCloudCleanupHandoffDispatchClaim(claim)
	if err := saveDestroyCloudCleanupSessionGuard(path); err != nil {
		return nil, path, false, fmt.Errorf("persist same-session up guard before cleanup dispatch: %w", err)
	}
	receipt.Status = "dispatching"
	if err := saveDestroyCloudCleanupHandoffReceipt(path, receipt); err != nil {
		return nil, path, false, fmt.Errorf("mark cleanup handoff dispatch attempt: %w", err)
	}
	handoff, dispatchErr := destroyCloudCleanupWorkflowDispatch(ctx, destroyCtx, receipt)
	if dispatchErr != nil {
		if isDestroyCloudCleanupDispatchDefinitelyNotSent(dispatchErr) {
			receipt.Status = "prepared"
			if err := saveDestroyCloudCleanupHandoffReceipt(path, receipt); err != nil {
				return nil, path, false, fmt.Errorf(
					"cleanup workflow request was not sent, but its receipt could not be released for retry: %w",
					err,
				)
			}
			if err := invalidateDestroyCloudCleanupSessionGuard(receipt.SessionID, path); err != nil {
				return nil, path, false, fmt.Errorf(
					"cleanup workflow request was not sent, but its same-session up guard could not be released: %w",
					err,
				)
			}
			return nil, path, false, fmt.Errorf(
				"cleanup workflow request was not sent; prepared receipt retained for safe retry: %w",
				dispatchErr,
			)
		}
		return nil, path, false, fmt.Errorf(
			"cleanup workflow dispatch result is ambiguous; dispatching receipt retained for exact-dispatch reconciliation: %w",
			dispatchErr,
		)
	}
	if err := acceptDestroyCloudCleanupHandoffReceipt(path, receipt, handoff); err != nil {
		return nil, path, false, err
	}
	return handoff, path, false, nil
}

func claimDestroyCloudCleanupHandoffDispatch(path string) (*os.File, bool, error) {
	claimPath := destroyCloudCleanupHandoffDispatchClaimPath(path)
	file, err := os.OpenFile(claimPath, os.O_CREATE|os.O_RDWR, 0o600)
	if err != nil {
		return nil, false, fmt.Errorf("open cleanup handoff dispatch claim: %w", err)
	}
	if err := syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		_ = file.Close()
		if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) {
			return nil, false, nil
		}
		return nil, false, fmt.Errorf("lock cleanup handoff dispatch claim: %w", err)
	}
	return file, true, nil
}

func releaseDestroyCloudCleanupHandoffDispatchClaim(claim *os.File) {
	if claim == nil {
		return
	}
	_ = syscall.Flock(int(claim.Fd()), syscall.LOCK_UN)
	_ = claim.Close()
}

func waitForDestroyCloudCleanupHandoffDispatchClaim(
	ctx context.Context,
	destroyCtx *v1alpha2DestroyContext,
	path string,
	want *destroyCloudCleanupHandoffReceipt,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()
	for {
		existing, err := loadDestroyCloudCleanupHandoffReceipt(path)
		if err != nil {
			return nil, path, false, fmt.Errorf("reload claimed cleanup handoff receipt: %w", err)
		}
		if existing.Status != "prepared" {
			return reuseDestroyCloudCleanupHandoffReceipt(ctx, destroyCtx, path, existing, want)
		}
		claim, claimed, err := claimDestroyCloudCleanupHandoffDispatch(path)
		if err != nil {
			return nil, path, false, err
		}
		if claimed {
			return dispatchClaimedDestroyCloudCleanupHandoff(ctx, destroyCtx, path, existing, claim)
		}
		select {
		case <-ctx.Done():
			return nil, path, false, ctx.Err()
		case <-ticker.C:
		}
	}
}

func destroyCloudCleanupHandoffDispatchClaimPath(receiptPath string) string {
	return receiptPath + ".dispatch-claim"
}

func acceptDestroyCloudCleanupHandoffReceipt(
	path string,
	receipt *destroyCloudCleanupHandoffReceipt,
	handoff *destroyCloudCleanupWorkflowHandoff,
) error {
	if err := validateAcceptedDestroyCloudCleanupHandoff(handoff, receipt); err != nil {
		return err
	}
	receipt.Status = "accepted"
	receipt.AcceptedAt = time.Now().UTC()
	receipt.Handoff = handoff
	if err := saveDestroyCloudCleanupHandoffReceipt(path, receipt); err != nil {
		return fmt.Errorf(
			"cleanup workflow dispatch was accepted but its durable receipt could not be committed; refusing local teardown: %w",
			err,
		)
	}
	return nil
}

func prepareDestroyCloudCleanupHandoffReceipt(
	destroyCtx *v1alpha2DestroyContext,
) (*destroyCloudCleanupHandoffReceipt, string, error) {
	if destroyCtx == nil || destroyCtx.ReapState == nil {
		return nil, "", fmt.Errorf("destroy reaper state is required")
	}
	scope := destroyCtx.ReapState.Scope.Normalize()
	if err := scope.Validate(); err != nil {
		return nil, "", err
	}
	accountID := ""
	if destroyCtx.AWSState != nil {
		accountID = strings.TrimSpace(destroyCtx.AWSState.Account)
	}
	if accountID == "" {
		accountID = v1alpha2DestroyReaperAccountID(destroyCtx.Resolved)
	}
	if accountID == "" {
		return nil, "", fmt.Errorf("cleanup workflow dispatch requires an AWS account id")
	}
	regions := destroyReapRegions(destroyCtx.Resolved, destroyCtx.AWSState)
	if len(regions) == 0 {
		regions = append([]string(nil), scope.Regions...)
	}
	regions = normalizedDestroyHandoffValues(regions)
	if len(regions) == 0 {
		return nil, "", fmt.Errorf("cleanup workflow dispatch requires a region")
	}
	generationID, err := destroyHandoffGenerationID(destroyCtx)
	if err != nil {
		return nil, "", err
	}
	knownResources, err := destroyHandoffKnownResources(destroyCtx, accountID)
	if err != nil {
		return nil, "", err
	}
	gcpScope, err := destroyHandoffGCPScope(destroyCtx)
	if err != nil {
		return nil, "", err
	}
	resourceFingerprint, err := destroyHandoffResourceFingerprint(knownResources, gcpScope)
	if err != nil {
		return nil, "", err
	}
	dispatchID := destroyHandoffDispatchID(scope, accountID, generationID, resourceFingerprint)
	receipt := &destroyCloudCleanupHandoffReceipt{
		Version:             destroyCleanupHandoffReceiptVersion,
		Status:              "prepared",
		PreparedAt:          time.Now().UTC(),
		Owner:               scope.Owner,
		SessionID:           scope.SessionID,
		AccountID:           accountID,
		Regions:             regions,
		GenerationID:        generationID,
		ResourceFingerprint: resourceFingerprint,
		DispatchID:          dispatchID,
		Repo:                destroyCloudCleanupWorkflowRepo(),
		Workflow:            destroyCloudCleanupWorkflowFile(),
		Ref:                 destroyCloudCleanupWorkflowRef(),
		KnownResources:      knownResources,
		GCP:                 gcpScope,
	}
	return receipt, destroyCloudCleanupHandoffReceiptPath(scope, dispatchID), nil
}

func destroyHandoffGenerationID(destroyCtx *v1alpha2DestroyContext) (string, error) {
	if destroyCtx != nil && destroyCtx.AWSState != nil && !destroyCtx.AWSState.CreatedAt.IsZero() {
		return destroyCtx.AWSState.CreatedAt.UTC().Format(time.RFC3339Nano), nil
	}
	if destroyCtx != nil && destroyCtx.State != nil && !destroyCtx.State.CreatedAt.IsZero() {
		return destroyCtx.State.CreatedAt.UTC().Format(time.RFC3339Nano), nil
	}
	if destroyCtx != nil && destroyCtx.ReapState != nil && destroyCtx.ReapState.ConfigScoped &&
		!destroyCtx.ReapState.UpdatedAt.IsZero() {
		return destroyCtx.ReapState.UpdatedAt.UTC().Format(time.RFC3339Nano), nil
	}
	return "", errDestroyCloudCleanupHandoffGenerationUnavailable
}

func reuseAcceptedDestroyCloudCleanupHandoffWithoutSessionState(
	destroyCtx *v1alpha2DestroyContext,
) (*destroyCloudCleanupWorkflowHandoff, string, bool, error) {
	if destroyCtx == nil || destroyCtx.ReapState == nil {
		return nil, "", false, fmt.Errorf("destroy reaper state is required")
	}
	scope := destroyCtx.ReapState.Scope.Normalize()
	if err := scope.Validate(); err != nil {
		return nil, "", false, err
	}
	stateDir := strings.TrimSpace(destroyCtx.StateDir)
	if stateDir == "" {
		return nil, "", false, errDestroyCloudCleanupHandoffGenerationUnavailable
	}
	if _, err := os.Stat(stateDir); err == nil {
		return nil, "", false, fmt.Errorf(
			"durable local cleanup closeout cannot be reused while session root %s still exists",
			stateDir,
		)
	} else if !errors.Is(err, os.ErrNotExist) {
		return nil, "", false, fmt.Errorf("verify local session root removal: %w", err)
	}
	guard, receipt, guardPath, err := loadDestroyCloudCleanupSessionGuard(scope.SessionID)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, "", false, errDestroyCloudCleanupHandoffGenerationUnavailable
		}
		return nil, "", false, err
	}
	if receipt.Owner != scope.Owner {
		return nil, guard.ReceiptPath, false, fmt.Errorf(
			"durable cleanup session guard %s does not match the requested owner",
			guardPath,
		)
	}
	accountID := ""
	if destroyCtx.AWSState != nil {
		accountID = strings.TrimSpace(destroyCtx.AWSState.Account)
	}
	if accountID == "" {
		accountID = v1alpha2DestroyReaperAccountID(destroyCtx.Resolved)
	}
	if receipt.Version != destroyCleanupHandoffReceiptVersion || receipt.Status != "accepted" || receipt.Handoff == nil ||
		(accountID != "" && receipt.AccountID != accountID) ||
		!destroyHandoffReceiptCoversRequestedRegions(receipt.Regions, scope.Regions) ||
		validateAcceptedDestroyCloudCleanupHandoff(receipt.Handoff, receipt) != nil {
		return nil, guard.ReceiptPath, false, fmt.Errorf(
			"durable cleanup session guard %s no longer proves an exact accepted session generation",
			guardPath,
		)
	}
	return receipt.Handoff, guard.ReceiptPath, true, nil
}

func saveDestroyCloudCleanupSessionGuard(receiptPath string) error {
	receipt, err := loadDestroyCloudCleanupHandoffReceipt(receiptPath)
	if err != nil {
		return fmt.Errorf("load cleanup receipt for same-session up guard: %w", err)
	}
	if receipt.Version != destroyCleanupHandoffReceiptVersion || strings.TrimSpace(receipt.SessionID) == "" ||
		strings.TrimSpace(receipt.GenerationID) == "" || strings.TrimSpace(receipt.DispatchID) == "" {
		return fmt.Errorf("cleanup receipt %s cannot guard an exact session generation", receiptPath)
	}
	guard := &destroyCloudCleanupSessionGuard{
		Version:     destroyCleanupSessionGuardVersion,
		RecordedAt:  time.Now().UTC(),
		ReceiptPath: receiptPath,
	}
	return saveDestroyCloudCleanupJSON(
		destroyCloudCleanupSessionGuardPath(receipt.SessionID),
		guard,
	)
}

func invalidateDestroyCloudCleanupSessionGuard(sessionID, receiptPath string) error {
	guard, _, path, err := loadDestroyCloudCleanupSessionGuard(sessionID)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil
		}
		return err
	}
	if strings.TrimSpace(receiptPath) != "" && guard.ReceiptPath != receiptPath {
		return fmt.Errorf("refusing to invalidate a newer cleanup session guard")
	}
	if err := os.Remove(path); err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil
		}
		return fmt.Errorf("invalidate prior cleanup session guard: %w", err)
	}
	if err := syncDestroyCloudCleanupHandoffReceiptDir(path); err != nil {
		return fmt.Errorf("commit prior cleanup session guard invalidation: %w", err)
	}
	return nil
}

func destroyCloudCleanupSessionGuardPath(sessionID string) string {
	return filepath.Join(
		cluster.DefaultStateDir,
		"destroy-handoffs",
		"session-guards",
		safeDestroySummaryFileName(sessionID)+".json",
	)
}

func loadDestroyCloudCleanupSessionGuard(
	sessionID string,
) (*destroyCloudCleanupSessionGuard, *destroyCloudCleanupHandoffReceipt, string, error) {
	path := destroyCloudCleanupSessionGuardPath(sessionID)
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, nil, path, err
	}
	var guard destroyCloudCleanupSessionGuard
	if err := json.Unmarshal(data, &guard); err != nil {
		return nil, nil, path, fmt.Errorf("parse durable cleanup session guard %s: %w", path, err)
	}
	if guard.Version != destroyCleanupSessionGuardVersion || strings.TrimSpace(guard.ReceiptPath) == "" {
		return nil, nil, path, fmt.Errorf("durable cleanup session guard %s is incomplete or mismatched", path)
	}
	receipt, err := loadDestroyCloudCleanupHandoffReceipt(guard.ReceiptPath)
	if err != nil {
		return nil, nil, path, fmt.Errorf("load exact cleanup session guard receipt: %w", err)
	}
	if receipt.Version != destroyCleanupHandoffReceiptVersion || receipt.SessionID != strings.TrimSpace(sessionID) ||
		strings.TrimSpace(receipt.Owner) == "" || strings.TrimSpace(receipt.AccountID) == "" ||
		strings.TrimSpace(receipt.GenerationID) == "" || strings.TrimSpace(receipt.DispatchID) == "" {
		return nil, nil, path, fmt.Errorf("durable cleanup session guard %s no longer identifies its exact receipt", path)
	}
	return &guard, receipt, path, nil
}

func guardV1Alpha2UpAgainstActiveDestroy(
	ctx context.Context,
	sessionID, stateDir string,
	selected *host.Session,
) error {
	guard, receipt, _, err := loadDestroyCloudCleanupSessionGuard(sessionID)
	if err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			return fmt.Errorf(
				"cannot verify whether remote cleanup for session %q finished; refusing up: %w",
				sessionID,
				err,
			)
		}
		return nil
	}
	if receipt.Status != "accepted" || receipt.Handoff == nil ||
		validateAcceptedDestroyCloudCleanupHandoff(receipt.Handoff, receipt) != nil {
		return fmt.Errorf(
			"remote cleanup for session %q has not produced a verifiable workflow run; refusing up until cleanup is retried",
			sessionID,
		)
	}
	if err := requireSuccessfulDestroyCloudCleanupRun(ctx, sessionID, receipt.Handoff); err != nil {
		return err
	}
	if err := reconcileSuccessfulDestroyLocalRuntime(ctx, sessionID, stateDir, selected); err != nil {
		return fmt.Errorf("reconcile stale local runtime for cleaned session %q: %w", sessionID, err)
	}
	if err := invalidateDestroyCloudCleanupSessionGuard(sessionID, guard.ReceiptPath); err != nil {
		return fmt.Errorf("release completed remote cleanup guard for session %q: %w", sessionID, err)
	}
	return nil
}

func requireSuccessfulDestroyCloudCleanupRun(
	ctx context.Context,
	sessionID string,
	handoff *destroyCloudCleanupWorkflowHandoff,
) error {
	run, err := destroyCloudCleanupWorkflowRunLookup(ctx, handoff.Repo, handoff.RunID)
	if err != nil {
		return fmt.Errorf(
			"cannot verify remote cleanup for session %q; refusing up until cleanup success is confirmed (workflow %s): %w",
			sessionID,
			handoff.RunURL,
			err,
		)
	}
	runURL := firstNonEmptyString(strings.TrimSpace(run.URL), strings.TrimSpace(handoff.RunURL))
	status := strings.ToLower(strings.TrimSpace(run.Status))
	conclusion := strings.ToLower(strings.TrimSpace(run.Conclusion))
	if status != "completed" {
		return fmt.Errorf(
			"remote cleanup for session %q is still %s; refusing up until it finishes successfully and proves zero resources (workflow %s)",
			sessionID,
			firstNonEmptyString(status, "unknown"),
			runURL,
		)
	}
	if conclusion != "success" {
		return fmt.Errorf(
			"remote cleanup for session %q did not prove zero resources (status=%s conclusion=%s); retry or manually complete cleanup before up (workflow %s)",
			sessionID,
			status,
			firstNonEmptyString(conclusion, "unknown"),
			runURL,
		)
	}
	return nil
}

func reconcileSuccessfulDestroyLocalRuntime(
	ctx context.Context,
	sessionID, stateDir string,
	requested *host.Session,
) error {
	if strings.TrimSpace(stateDir) == "" {
		return fmt.Errorf("local state directory is required")
	}
	state, err := loadLifecycleState(stateDir)
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return fmt.Errorf("load prior lifecycle state: %w", err)
	}
	paths := cptilt.PathsForState(stateDir, state)
	if _, err := stopPersistedTiltProcess(ctx, paths.Status, stateDir); err != nil {
		return fmt.Errorf("stop exact persisted Tilt/tunnel runtime: %w", err)
	}
	if _, err := destroyStarterDashboardForward(stateDir, sessionID); err != nil {
		return fmt.Errorf("stop exact persisted Starter dashboard runtime: %w", err)
	}
	var resolved *config.SessionConfig
	resolvedPath := resolvedSessionPathForDestroy(stateDir, state)
	if _, statErr := os.Stat(resolvedPath); statErr == nil {
		loaded, loadErr := config.LoadSessionForDestroy(resolvedPath)
		if loadErr != nil {
			return fmt.Errorf("load prior resolved session for local runtime cleanup: %w", loadErr)
		}
		resolved = loaded
	} else if !errors.Is(statErr, os.ErrNotExist) {
		return fmt.Errorf("stat prior resolved session for local runtime cleanup: %w", statErr)
	}
	prior, err := loadDestroySelectedHost(stateDir, state, resolved)
	if err != nil {
		return fmt.Errorf("load prior selected host for local runtime cleanup: %w", err)
	}
	if prior == nil {
		prior = requested
	}
	if prior != nil && prior.Mode == config.HostModeLocalK3d {
		if err := destroySelectedHost(ctx, prior); err != nil {
			return fmt.Errorf("delete exact prior local k3d host: %w", err)
		}
	}
	if err := os.RemoveAll(stateDir); err != nil {
		return fmt.Errorf("remove prior local session state: %w", err)
	}
	return nil
}

func destroyHandoffReceiptCoversRequestedRegions(receiptRegions, requestedRegions []string) bool {
	receiptRegions = normalizedDestroyHandoffValues(receiptRegions)
	for _, region := range normalizedDestroyHandoffValues(requestedRegions) {
		if !slices.Contains(receiptRegions, region) {
			return false
		}
	}
	return true
}

//nolint:nilnil // A non-GCP session has no GCP cleanup scope.
func destroyHandoffGCPScope(
	destroyCtx *v1alpha2DestroyContext,
) (*destroyGCPRegionalCleanupScope, error) {
	if destroyCtx == nil || !config.DedicatedGCPEnabled(destroyCtx.Resolved) {
		return nil, nil
	}
	createdAt := time.Time{}
	if destroyCtx.State != nil {
		createdAt = destroyCtx.State.CreatedAt
	}
	if createdAt.IsZero() && destroyCtx.AWSState != nil {
		createdAt = destroyCtx.AWSState.CreatedAt
	}
	if createdAt.IsZero() || destroyCtx.Resolved.Host.TTL.Duration <= 0 {
		return nil, fmt.Errorf("GCP cleanup handoff requires lifecycle created_at and positive TTL")
	}

	gcp := destroyCtx.Resolved.Dedicated.GCP
	name := destroyCtx.Resolved.Name + "-gcp-regional"
	o11yName := name + "-o11y"
	scope := &destroyGCPRegionalCleanupScope{
		ProjectID:                  gcp.SeedProjectID,
		Region:                     gcp.Region,
		Zone:                       gcp.Zone,
		InstanceName:               name,
		Network:                    name,
		Subnet:                     name,
		Address:                    name,
		FirewallRules:              []string{name + "-iap-ssh", name + "-kong", o11yName},
		O11YPSCSubnet:              o11yName + "-psc",
		O11YInstanceGroup:          o11yName,
		O11YHealthCheck:            o11yName,
		O11YBackendService:         o11yName,
		O11YForwardingRule:         o11yName,
		O11YServiceAttachment:      o11yName,
		O11YBucket:                 gcp.SeedProjectID + "-" + destroyCtx.Resolved.Name + "-o11y",
		RuntimeServiceAccountEmail: config.DedicatedGCPRuntimeServiceAccountEmail(gcp.SeedProjectID),
		TTLExpiresAt:               createdAt.UTC().Add(destroyCtx.Resolved.Host.TTL.Duration).Format(time.RFC3339),
	}
	if !destroyCtx.GCPHostExists {
		return scope, nil
	}

	selected, err := host.Load(host.GCPRegionalStateDir(destroyCtx.StateDir))
	if err != nil {
		return nil, fmt.Errorf("load GCP host for cleanup handoff: %w", err)
	}
	if selected.Mode != config.HostModeRemoteGCEK3d || selected.Remote == nil || selected.GCP == nil ||
		selected.Remote.InstanceName != scope.InstanceName || selected.Region != scope.Region ||
		selected.GCP.ProjectID != scope.ProjectID || selected.GCP.Zone != scope.Zone ||
		selected.GCP.Network != scope.Network || selected.GCP.Subnet != scope.Subnet ||
		(strings.TrimSpace(selected.GCP.O11YServiceAttachment) != "" &&
			selected.GCP.O11YServiceAttachment != scope.O11YServiceAttachment) ||
		(strings.TrimSpace(selected.GCP.O11YBucket) != "" && selected.GCP.O11YBucket != scope.O11YBucket) ||
		selected.GCP.ServiceAccountEmail != scope.RuntimeServiceAccountEmail {
		return nil, fmt.Errorf("persisted GCP host does not match the cleanup handoff scope")
	}
	return scope, nil
}

func destroyHandoffResourceFingerprint(
	resources []cpaws.Resource,
	gcp *destroyGCPRegionalCleanupScope,
) (string, error) {
	var value any = resources
	if gcp != nil {
		value = struct {
			AWS []cpaws.Resource                `json:"aws"`
			GCP *destroyGCPRegionalCleanupScope `json:"gcp"`
		}{AWS: resources, GCP: gcp}
	}
	data, err := json.Marshal(value)
	if err != nil {
		return "", fmt.Errorf("marshal cleanup handoff known resources: %w", err)
	}
	digest := sha256.Sum256(data)
	return hex.EncodeToString(digest[:]), nil
}

func destroyHandoffDispatchID(scope cpaws.Scope, accountID, generationID, resourceFingerprint string) string {
	scope = scope.Normalize()
	digest := sha256.Sum256([]byte(strings.Join([]string{
		scope.Owner,
		scope.SessionID,
		strings.TrimSpace(accountID),
		strings.TrimSpace(generationID),
		strings.TrimSpace(resourceFingerprint),
	}, "\x00")))
	return hex.EncodeToString(digest[:16])
}

func destroyHandoffKnownResources(destroyCtx *v1alpha2DestroyContext, accountID string) ([]cpaws.Resource, error) {
	if destroyCtx == nil || destroyCtx.ReapState == nil {
		return nil, nil
	}
	scope := destroyCtx.ReapState.Scope.Normalize()
	resources := append([]cpaws.Resource(nil), destroyCtx.ReapState.Survivors...)
	for index, resource := range resources {
		arn := strings.TrimSpace(resource.ARN)
		if arn == "" {
			return nil, fmt.Errorf(
				"persisted cleanup survivor is missing ARN at index %d (type %q); refusing workflow handoff without direct terminal proof",
				index,
				resource.Type,
			)
		}
		resourceType := resource.Type
		if resourceType == "" || resourceType == cpaws.ResourceUnknown {
			resourceType = cpaws.ParseResourceType(arn)
		}
		if !cpaws.SupportsKnownResourceVerification(resourceType) {
			return nil, fmt.Errorf(
				"unsupported persisted cleanup survivor %s (type %q); refusing workflow handoff without direct terminal proof",
				arn,
				resourceType,
			)
		}
	}
	appendARN := func(arn string, resourceType cpaws.ResourceType, region string) {
		arn = strings.TrimSpace(arn)
		if arn == "" {
			return
		}
		resources = append(resources, cpaws.Resource{
			ARN: arn, Type: resourceType, Region: region, Owner: scope.Owner, SessionID: scope.SessionID,
		})
	}
	if destroyCtx.Selected != nil && destroyCtx.Selected.Remote != nil {
		remote := destroyCtx.Selected.Remote
		region := firstNonEmptyString(remote.Region, firstNonEmptyString(scope.Regions...))
		if instanceID := strings.TrimSpace(remote.InstanceID); instanceID != "" {
			appendARN(fmt.Sprintf("arn:aws:ec2:%s:%s:instance/%s", region, accountID, instanceID), cpaws.ResourceEC2Instance, region)
		}
		if profileName := strings.TrimSpace(remote.InstanceProfileName); profileName != "" {
			appendARN(fmt.Sprintf("arn:aws:iam::%s:instance-profile/%s", accountID, profileName), cpaws.ResourceIAMInstanceProfile, "")
		}
		appendARN(remote.RuntimeRoleARN, cpaws.ResourceIAMRole, "")
		appendARN(remote.PublicLoadBalancerARN, cpaws.ResourceLoadBalancer, region)
		appendARN(remote.PublicLoadBalancerTargetGroup, cpaws.ResourceTargetGroup, region)
		appendARN(remote.O11YLoadBalancerARN, cpaws.ResourceLoadBalancer, region)
		appendARN(remote.O11YLoadBalancerTargetGroup, cpaws.ResourceTargetGroup, region)
		if serviceID := strings.TrimSpace(remote.O11YEndpointServiceID); serviceID != "" {
			appendARN(
				fmt.Sprintf("arn:aws:ec2:%s:%s:vpc-endpoint-service/%s", region, accountID, serviceID),
				cpaws.ResourceVPCEndpointService,
				region,
			)
		}
		appendARN(remote.PublicHTTPSLoadBalancerARN, cpaws.ResourceLoadBalancer, region)
		appendARN(remote.PublicHTTPSTargetGroup, cpaws.ResourceTargetGroup, region)
		for _, securityGroupID := range []string{remote.PublicHTTPSSecurityGroup, remote.RemoteSecurityGroup} {
			if securityGroupID = strings.TrimSpace(securityGroupID); securityGroupID != "" {
				appendARN(fmt.Sprintf("arn:aws:ec2:%s:%s:security-group/%s", region, accountID, securityGroupID), cpaws.ResourceSecurityGroup, region)
			}
		}
	}
	if destroyCtx.GCPHostExists {
		selected, err := host.Load(host.GCPRegionalStateDir(destroyCtx.StateDir))
		if err != nil {
			return nil, fmt.Errorf("load GCP host for known-resource cleanup handoff: %w", err)
		}
		if selected.GCP != nil {
			appendARN(selected.GCP.AWSRoleARN, cpaws.ResourceIAMRole, "")
		}
	}
	seen := make(map[string]struct{}, len(resources))
	result := make([]cpaws.Resource, 0, len(resources))
	for _, resource := range resources {
		resource.ARN = strings.TrimSpace(resource.ARN)
		if resource.ARN == "" {
			continue
		}
		if resource.Type == "" || resource.Type == cpaws.ResourceUnknown {
			resource.Type = cpaws.ParseResourceType(resource.ARN)
		}
		if resource.Region == "" {
			resource.Region = cpaws.ParseResourceRegion(resource.ARN)
		}
		if !cpaws.SupportsKnownResourceVerification(resource.Type) {
			return nil, fmt.Errorf(
				"cleanup handoff resource %s (type %q) has no direct terminal proof",
				resource.ARN,
				resource.Type,
			)
		}
		resource.Owner = scope.Owner
		resource.SessionID = scope.SessionID
		key := string(resource.Type) + "\x00" + resource.Region + "\x00" + resource.ARN
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		result = append(result, resource)
	}
	slices.SortFunc(result, func(a, b cpaws.Resource) int {
		return strings.Compare(string(a.Type)+"\x00"+a.Region+"\x00"+a.ARN, string(b.Type)+"\x00"+b.Region+"\x00"+b.ARN)
	})
	return result, nil
}

func destroyCloudCleanupHandoffReceiptPath(scope cpaws.Scope, dispatchID string) string {
	scope = scope.Normalize()
	return filepath.Join(
		cluster.DefaultStateDir,
		"destroy-handoffs",
		safeDestroySummaryFileName(scope.Owner),
		safeDestroySummaryFileName(scope.SessionID),
		safeDestroySummaryFileName(dispatchID)+".json",
	)
}

func normalizedDestroyHandoffValues(values []string) []string {
	seen := make(map[string]struct{}, len(values))
	result := make([]string, 0, len(values))
	for _, value := range values {
		value = strings.TrimSpace(value)
		if value == "" {
			continue
		}
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		result = append(result, value)
	}
	slices.Sort(result)
	return result
}

func validateAcceptedDestroyCloudCleanupHandoff(
	handoff *destroyCloudCleanupWorkflowHandoff,
	receipt *destroyCloudCleanupHandoffReceipt,
) error {
	if handoff == nil || strings.TrimSpace(handoff.Result) != "dispatched" {
		return fmt.Errorf("cleanup workflow dispatch returned no accepted handoff")
	}
	if handoff.RunID <= 0 || strings.TrimSpace(handoff.RunURL) == "" {
		if obs := strings.TrimSpace(handoff.ObservationError); obs != "" {
			return fmt.Errorf("cleanup workflow dispatch returned no durable run identity: %s", obs)
		}
		return fmt.Errorf("cleanup workflow dispatch returned no durable run identity")
	}
	if receipt == nil {
		return fmt.Errorf("prepared cleanup handoff receipt is required")
	}
	if handoff.Owner != receipt.Owner || handoff.SessionID != receipt.SessionID || handoff.AccountID != receipt.AccountID {
		return fmt.Errorf(
			"cleanup workflow accepted mismatched scope owner=%q session=%q account=%q, want owner=%q session=%q account=%q",
			handoff.Owner,
			handoff.SessionID,
			handoff.AccountID,
			receipt.Owner,
			receipt.SessionID,
			receipt.AccountID,
		)
	}
	if handoff.DispatchID != receipt.DispatchID {
		return fmt.Errorf(
			"cleanup workflow accepted mismatched dispatch id %q, want %q",
			handoff.DispatchID,
			receipt.DispatchID,
		)
	}
	if handoff.Repo != receipt.Repo || handoff.Workflow != receipt.Workflow || handoff.Ref != receipt.Ref {
		return fmt.Errorf(
			"cleanup workflow accepted mismatched dispatch destination %q/%q@%q, want %q/%q@%q",
			handoff.Repo,
			handoff.Workflow,
			handoff.Ref,
			receipt.Repo,
			receipt.Workflow,
			receipt.Ref,
		)
	}
	return nil
}

func validateDestroyCloudCleanupHandoffReceipt(
	actual *destroyCloudCleanupHandoffReceipt,
	want *destroyCloudCleanupHandoffReceipt,
) error {
	if actual == nil || want == nil {
		return fmt.Errorf("cleanup handoff receipt is missing")
	}
	if actual.Version != destroyCleanupHandoffReceiptVersion {
		return fmt.Errorf("cleanup handoff receipt has unsupported version %q", actual.Version)
	}
	if strings.TrimSpace(actual.Repo) == "" || strings.TrimSpace(actual.Workflow) == "" || strings.TrimSpace(actual.Ref) == "" {
		return fmt.Errorf("cleanup handoff receipt has no durable workflow destination")
	}
	if actual.Owner != want.Owner || actual.SessionID != want.SessionID || actual.AccountID != want.AccountID ||
		!slices.Equal(actual.Regions, want.Regions) || actual.GenerationID != want.GenerationID ||
		actual.ResourceFingerprint != want.ResourceFingerprint || actual.DispatchID != want.DispatchID {
		return fmt.Errorf("cleanup handoff receipt scope does not match the requested destroy scope")
	}
	return nil
}

func loadDestroyCloudCleanupHandoffReceipt(path string) (*destroyCloudCleanupHandoffReceipt, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var receipt destroyCloudCleanupHandoffReceipt
	if err := json.Unmarshal(data, &receipt); err != nil {
		return nil, fmt.Errorf("parse cleanup handoff receipt %s: %w", path, err)
	}
	return &receipt, nil
}

func createDestroyCloudCleanupHandoffReceipt(path string, receipt *destroyCloudCleanupHandoffReceipt) (bool, error) {
	if receipt == nil {
		return false, fmt.Errorf("cleanup handoff receipt is nil")
	}
	temporaryPath, err := writeDestroyCloudCleanupJSONTemp(path, receipt)
	if err != nil {
		return false, err
	}
	defer os.Remove(temporaryPath)
	if err := os.Link(temporaryPath, path); err != nil {
		if errors.Is(err, os.ErrExist) {
			return false, nil
		}
		return false, fmt.Errorf("claim cleanup handoff receipt: %w", err)
	}
	if err := syncDestroyCloudCleanupHandoffReceiptDir(path); err != nil {
		return false, err
	}
	return true, nil
}

func saveDestroyCloudCleanupHandoffReceipt(path string, receipt *destroyCloudCleanupHandoffReceipt) error {
	return saveDestroyCloudCleanupJSON(path, receipt)
}

func saveDestroyCloudCleanupJSON(path string, value any) error {
	temporaryPath, err := writeDestroyCloudCleanupJSONTemp(path, value)
	if err != nil {
		return err
	}
	defer os.Remove(temporaryPath)
	if err := os.Rename(temporaryPath, path); err != nil {
		return fmt.Errorf("commit cleanup handoff receipt: %w", err)
	}
	return syncDestroyCloudCleanupHandoffReceiptDir(path)
}

func writeDestroyCloudCleanupJSONTemp(path string, value any) (string, error) {
	data, err := json.MarshalIndent(value, "", "  ")
	if err != nil {
		return "", fmt.Errorf("marshal cleanup handoff receipt: %w", err)
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return "", fmt.Errorf("create cleanup handoff receipt dir: %w", err)
	}
	temporary, err := os.CreateTemp(filepath.Dir(path), ".cleanup-handoff.*.tmp")
	if err != nil {
		return "", fmt.Errorf("create cleanup handoff receipt temp file: %w", err)
	}
	temporaryPath := temporary.Name()
	removeOnError := true
	defer func() {
		if removeOnError {
			_ = os.Remove(temporaryPath)
		}
	}()
	if err := temporary.Chmod(0o600); err != nil {
		_ = temporary.Close()
		return "", fmt.Errorf("chmod cleanup handoff receipt temp file: %w", err)
	}
	if _, err := temporary.Write(data); err != nil {
		_ = temporary.Close()
		return "", fmt.Errorf("write cleanup handoff receipt: %w", err)
	}
	if err := temporary.Sync(); err != nil {
		_ = temporary.Close()
		return "", fmt.Errorf("sync cleanup handoff receipt: %w", err)
	}
	if err := temporary.Close(); err != nil {
		return "", fmt.Errorf("close cleanup handoff receipt: %w", err)
	}
	removeOnError = false
	return temporaryPath, nil
}

func syncDestroyCloudCleanupHandoffReceiptDir(path string) error {
	directory, err := os.Open(filepath.Dir(path))
	if err != nil {
		return fmt.Errorf("open cleanup handoff receipt dir: %w", err)
	}
	defer directory.Close()
	if err := directory.Sync(); err != nil {
		return fmt.Errorf("sync cleanup handoff receipt dir: %w", err)
	}
	return nil
}
