package setupcmd

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/nexuscrm/automation"
	"github.com/nexuscrm/backend/pkg/serverruntime"
	database "github.com/nexuscrm/db"
	"golang.org/x/sys/unix"
)

const (
	connectorRemoteIdentityRecoveryTestDownstream = "1111111111111111111111111111111111111111"
	connectorRemoteIdentityRecoveryTestUpstream   = "2222222222222222222222222222222222222222"
	connectorRemoteIdentityRecoveryTestOfficial   = "3333333333333333333333333333333333333333"
)

var connectorRemoteIdentityRecoveryTestProof = connectorRemoteIdentityRecoveryRuntimeProof{
	RuntimeBindingSHA256: strings.Repeat("8", 64),
	ReleaseJSONSHA256:    strings.Repeat("5", 64),
	RuntimeEnvSHA256:     strings.Repeat("6", 64),
	SetupBinarySHA256:    strings.Repeat("7", 64),
	DeployedTupleSHA256:  strings.Repeat("9", 64),
}

type connectorRemoteIdentityRecoveryRunnerStub struct {
	result      automation.ConnectorInboundRemoteIdentityRecoveryBatchResult
	err         error
	dryRunCalls int
	applyCalls  int
	lastRequest automation.ConnectorInboundRemoteIdentityRecoveryBatchRequest
	onRun       func()
}

func (s *connectorRemoteIdentityRecoveryRunnerStub) RecoverBatch(
	_ context.Context,
	request automation.ConnectorInboundRemoteIdentityRecoveryBatchRequest,
) (automation.ConnectorInboundRemoteIdentityRecoveryBatchResult, error) {
	s.applyCalls++
	s.lastRequest = request
	if s.onRun != nil {
		s.onRun()
	}
	return s.result, s.err
}

func (s *connectorRemoteIdentityRecoveryRunnerStub) DryRunBatch(
	_ context.Context,
	request automation.ConnectorInboundRemoteIdentityRecoveryBatchRequest,
) (automation.ConnectorInboundRemoteIdentityRecoveryBatchResult, error) {
	s.dryRunCalls++
	s.lastRequest = request
	if s.onRun != nil {
		s.onRun()
	}
	return s.result, s.err
}

func connectorRemoteIdentityRecoveryTestManifest() []byte {
	return []byte(fmt.Sprintf(`{
  "schema_version": 1,
  "operation": "hubspot-authored-identity-recovery",
  "issue": 3765,
  "environment": "prod",
  "approval_ref": "approval-3765",
  "deployed_tuple": {
    "deployment_id":"deploy-1",
    "release_tag":"release-1",
    "downstream_commit":"%s",
    "upstream_commit":"%s",
    "official_app_packs_commit":"%s",
    "final_pack_hash":"%s"
  },
  "runtime_binding": {
    "release_json_sha256":"%s",
    "runtime_env_sha256":"%s",
    "setup_binary_sha256":"%s"
  },
  "selector": {
    "connector_owner_key": "hubspot-owner",
    "connector_namespace": "hubspot-crm-connector",
    "destination_slot_id": "hubspot-contact"
  },
  "census": {"eligible":2,"excluded_deleted_owner":2},
  "excluded_set_sha256": "%s",
  "candidates": [
    {"remote_id":"remote-2","expected_source_object":"Lead","expected_source_record_id":"00Q-2"},
    {"remote_id":"remote-1","expected_source_object":"Lead","expected_source_record_id":"00Q-1"}
  ]
}`,
		connectorRemoteIdentityRecoveryTestDownstream,
		connectorRemoteIdentityRecoveryTestUpstream,
		connectorRemoteIdentityRecoveryTestOfficial,
		strings.Repeat("4", 64),
		connectorRemoteIdentityRecoveryTestProof.ReleaseJSONSHA256,
		connectorRemoteIdentityRecoveryTestProof.RuntimeEnvSHA256,
		connectorRemoteIdentityRecoveryTestProof.SetupBinarySHA256,
		strings.Repeat("a", 64),
	))
}

func connectorRemoteIdentityRecoveryTestDecodedManifest(t *testing.T) connectorRemoteIdentityRecoveryManifest {
	t.Helper()
	manifest, _, err := decodeConnectorRemoteIdentityRecoveryManifest(connectorRemoteIdentityRecoveryTestManifest())
	if err != nil {
		t.Fatalf("decode manifest: %v", err)
	}
	return manifest
}

func connectorRemoteIdentityRecoveryTestResult(outcome string, committed, readback, enrollment bool) automation.ConnectorInboundRemoteIdentityRecoveryBatchResult {
	return automation.ConnectorInboundRemoteIdentityRecoveryBatchResult{
		Outcome:           outcome,
		Committed:         committed,
		ReadbackVerified:  readback,
		EnrollmentAllowed: enrollment,
		RequestedCount:    2,
		RecoveredCount:    2,
		Items: []automation.ConnectorInboundRemoteIdentityRecoveryBatchItemResult{
			{RemoteID: "remote-1", SourceObject: "Lead", SourceRecordID: "00Q-1", LinkKey: "private-link-1", EvidenceID: "private-evidence-1"},
			{RemoteID: "remote-2", SourceObject: "Lead", SourceRecordID: "00Q-2", LinkKey: "private-link-2", EvidenceID: "private-evidence-2"},
		},
	}
}

func connectorRemoteIdentityRecoveryWriteTestFile(t *testing.T, name string, data []byte) (string, string) {
	t.Helper()
	path := filepath.Join(t.TempDir(), name)
	if err := os.WriteFile(path, data, 0o600); err != nil {
		t.Fatalf("write %s: %v", name, err)
	}
	sum := sha256.Sum256(data)
	return path, hex.EncodeToString(sum[:])
}

func connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA string) []string {
	return []string{
		"--manifest", manifestPath,
		"--manifest-sha256", manifestSHA,
		"--release-json", "/proc/self/fd/10",
		"--release-json-sha256", connectorRemoteIdentityRecoveryTestProof.ReleaseJSONSHA256,
		"--runtime-env", "/proc/self/fd/11",
		"--runtime-env-sha256", connectorRemoteIdentityRecoveryTestProof.RuntimeEnvSHA256,
		"--setup-binary-sha256", connectorRemoteIdentityRecoveryTestProof.SetupBinarySHA256,
	}
}

func connectorRemoteIdentityRecoveryTestVerifier(calls *int, proof connectorRemoteIdentityRecoveryRuntimeProof, err error) connectorRemoteIdentityRecoveryRuntimeVerifier {
	return func(context.Context, *database.TiDBConnection, serverruntime.ProductHooks, connectorRemoteIdentityRecoveryArgs, connectorRemoteIdentityRecoveryManifest) (connectorRemoteIdentityRecoveryRuntimeProof, error) {
		(*calls)++
		return proof, err
	}
}

func connectorRemoteIdentityRecoveryDecodeTestReceipt(t *testing.T, data []byte) connectorRemoteIdentityRecoveryReceipt {
	t.Helper()
	var receipt connectorRemoteIdentityRecoveryReceipt
	if err := json.Unmarshal(data, &receipt); err != nil {
		t.Fatalf("decode receipt %s: %v", data, err)
	}
	return receipt
}

func connectorRemoteIdentityRecoveryAssertTestReceiptKeys(t *testing.T, data []byte, extra ...string) {
	t.Helper()
	want := []string{
		"schema_version", "command", "mode", "manifest_sha256", "runtime_binding_sha256",
		"release_json_sha256", "runtime_env_sha256", "setup_binary_sha256", "deployed_tuple_sha256",
		"outcome", "committed", "readback_verified", "enrollment_allowed", "result_contract_valid",
		"requested_count", "recovered_count", "finalized_link_count", "consumed_evidence_count",
		"resolver_match_count", "items",
	}
	want = append(want, extra...)
	var object map[string]json.RawMessage
	if err := json.Unmarshal(data, &object); err != nil {
		t.Fatalf("decode receipt keys: %v", err)
	}
	if len(object) != len(want) {
		t.Fatalf("receipt keys = %#v, want %#v", object, want)
	}
	for _, key := range want {
		if _, ok := object[key]; !ok {
			t.Fatalf("receipt missing key %q: %s", key, data)
		}
	}
}

func TestRunConnectorRemoteIdentityRecoveryCommandDryRunWritesBoundIdentifierFreeReceipt(t *testing.T) {
	manifest := connectorRemoteIdentityRecoveryTestManifest()
	manifestPath, manifestSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "manifest.json", manifest)
	runner := &connectorRemoteIdentityRecoveryRunnerStub{result: connectorRemoteIdentityRecoveryTestResult(
		automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeDryRunVerified, false, false, false,
	)}
	built, verified := 0, 0
	var stdout, stderr bytes.Buffer
	code := runConnectorRemoteIdentityRecoveryCommand(
		context.Background(), nil, serverruntime.ProductHooks{}, connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA),
		&stdout, &stderr,
		connectorRemoteIdentityRecoveryTestVerifier(&verified, connectorRemoteIdentityRecoveryTestProof, nil),
		func(*database.TiDBConnection, serverruntime.ProductHooks) (connectorRemoteIdentityRecoveryBatchRunner, error) {
			built++
			if verified != 1 {
				t.Fatalf("runner built before runtime verification")
			}
			return runner, nil
		},
	)
	if code != 0 || built != 1 || verified != 1 || runner.dryRunCalls != 1 || runner.applyCalls != 0 {
		t.Fatalf("code/built/verified/dry/apply = %d/%d/%d/%d/%d stderr=%s", code, built, verified, runner.dryRunCalls, runner.applyCalls, stderr.String())
	}
	if strings.Contains(stdout.String(), "private-link") || strings.Contains(stdout.String(), "private-evidence") ||
		strings.Contains(stdout.String(), "link_key") || strings.Contains(stdout.String(), "evidence_id") {
		t.Fatalf("receipt leaked internal durable identifiers: %s", stdout.String())
	}
	receipt := connectorRemoteIdentityRecoveryDecodeTestReceipt(t, stdout.Bytes())
	connectorRemoteIdentityRecoveryAssertTestReceiptKeys(t, stdout.Bytes())
	if receipt.Mode != "dry_run" || receipt.Outcome != automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeDryRunVerified ||
		receipt.Committed || receipt.ReadbackVerified || receipt.EnrollmentAllowed || !receipt.ResultContractValid ||
		receipt.RuntimeBindingSHA256 != connectorRemoteIdentityRecoveryTestProof.RuntimeBindingSHA256 ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.FinalizedLinkCount, 2) ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.ConsumedEvidenceCount, 2) ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.ResolverMatchCount, 0) {
		t.Fatalf("dry-run receipt = %#v", receipt)
	}
}

func TestRunConnectorRemoteIdentityRecoveryCommandApplyRequiresSameRuntimeBoundDryRun(t *testing.T) {
	manifestBytes := connectorRemoteIdentityRecoveryTestManifest()
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	manifestPath, manifestSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "manifest.json", manifestBytes)
	dryRunReceipt := connectorRemoteIdentityRecoveryReceiptFromResult(
		connectorRemoteIdentityRecoveryArgs{}, manifestSHA, connectorRemoteIdentityRecoveryTestProof, manifest,
		connectorRemoteIdentityRecoveryTestResult(automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeDryRunVerified, false, false, false), true,
	)
	var receiptBytes bytes.Buffer
	if err := writeConnectorRemoteIdentityRecoveryReceipt(&receiptBytes, dryRunReceipt); err != nil {
		t.Fatalf("write dry-run receipt: %v", err)
	}
	dryRunPath, dryRunSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "dry-run-receipt.json", receiptBytes.Bytes())
	runner := &connectorRemoteIdentityRecoveryRunnerStub{result: connectorRemoteIdentityRecoveryTestResult(
		automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeCommittedVerified, true, true, true,
	)}
	args := append(connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA),
		"--apply", "--dry-run-receipt", dryRunPath, "--dry-run-receipt-sha256", dryRunSHA,
		"--backup-marker", strings.Repeat("b", 64))
	verified := 0
	var stdout, stderr bytes.Buffer
	code := runConnectorRemoteIdentityRecoveryCommand(
		context.Background(), nil, serverruntime.ProductHooks{}, args, &stdout, &stderr,
		connectorRemoteIdentityRecoveryTestVerifier(&verified, connectorRemoteIdentityRecoveryTestProof, nil),
		func(*database.TiDBConnection, serverruntime.ProductHooks) (connectorRemoteIdentityRecoveryBatchRunner, error) {
			return runner, nil
		},
	)
	if code != 0 || verified != 1 || runner.applyCalls != 1 || runner.dryRunCalls != 0 {
		t.Fatalf("code/verified/apply/dry = %d/%d/%d/%d stderr=%s", code, verified, runner.applyCalls, runner.dryRunCalls, stderr.String())
	}
	receipt := connectorRemoteIdentityRecoveryDecodeTestReceipt(t, stdout.Bytes())
	connectorRemoteIdentityRecoveryAssertTestReceiptKeys(t, stdout.Bytes(), "dry_run_receipt_sha256", "backup_marker")
	if receipt.Mode != "apply" || receipt.Outcome != automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeCommittedVerified ||
		!receipt.Committed || !receipt.ReadbackVerified || !receipt.EnrollmentAllowed || !receipt.ResultContractValid ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.ResolverMatchCount, 2) || receipt.DryRunReceiptSHA256 != dryRunSHA {
		t.Fatalf("apply receipt = %#v", receipt)
	}
}

func TestRunConnectorRemoteIdentityRecoveryCommandRejectsDryRunRuntimeBindingDriftBeforeBuilder(t *testing.T) {
	manifestBytes := connectorRemoteIdentityRecoveryTestManifest()
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	manifestPath, manifestSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "manifest.json", manifestBytes)
	dryProof := connectorRemoteIdentityRecoveryTestProof
	dryProof.RuntimeBindingSHA256 = strings.Repeat("a", 64)
	dryRunReceipt := connectorRemoteIdentityRecoveryReceiptFromResult(
		connectorRemoteIdentityRecoveryArgs{}, manifestSHA, dryProof, manifest,
		connectorRemoteIdentityRecoveryTestResult(automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeDryRunVerified, false, false, false), true,
	)
	var receiptBytes bytes.Buffer
	_ = writeConnectorRemoteIdentityRecoveryReceipt(&receiptBytes, dryRunReceipt)
	dryRunPath, dryRunSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "dry-run-receipt.json", receiptBytes.Bytes())
	args := append(connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA),
		"--apply", "--dry-run-receipt", dryRunPath, "--dry-run-receipt-sha256", dryRunSHA,
		"--backup-marker", strings.Repeat("b", 64))
	verified, built := 0, 0
	var stdout, stderr bytes.Buffer
	code := runConnectorRemoteIdentityRecoveryCommand(
		context.Background(), nil, serverruntime.ProductHooks{}, args, &stdout, &stderr,
		connectorRemoteIdentityRecoveryTestVerifier(&verified, connectorRemoteIdentityRecoveryTestProof, nil),
		func(*database.TiDBConnection, serverruntime.ProductHooks) (connectorRemoteIdentityRecoveryBatchRunner, error) {
			built++
			return nil, nil
		},
	)
	if code != 1 || verified != 1 || built != 0 || !strings.Contains(stderr.String(), "does not prove") {
		t.Fatalf("code/verified/built/stderr = %d/%d/%d/%s", code, verified, built, stderr.String())
	}
	receipt := connectorRemoteIdentityRecoveryDecodeTestReceipt(t, stdout.Bytes())
	if receipt.BlockReason != connectorRemoteIdentityRecoveryBlockDryRunReceiptInvalid ||
		receipt.RuntimeBindingSHA256 != connectorRemoteIdentityRecoveryTestProof.RuntimeBindingSHA256 {
		t.Fatalf("binding drift receipt = %#v", receipt)
	}
}

func TestRunConnectorRemoteIdentityRecoveryCommandRuntimeMismatchPreventsBuilder(t *testing.T) {
	manifestPath, manifestSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "manifest.json", connectorRemoteIdentityRecoveryTestManifest())
	verified, built := 0, 0
	var stdout, stderr bytes.Buffer
	code := runConnectorRemoteIdentityRecoveryCommand(
		context.Background(), nil, serverruntime.ProductHooks{}, connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA),
		&stdout, &stderr,
		connectorRemoteIdentityRecoveryTestVerifier(&verified, connectorRemoteIdentityRecoveryRuntimeProof{}, errors.New("old setup binary")),
		func(*database.TiDBConnection, serverruntime.ProductHooks) (connectorRemoteIdentityRecoveryBatchRunner, error) {
			built++
			return nil, nil
		},
	)
	if code != 1 || verified != 1 || built != 0 || !strings.Contains(stderr.String(), "old setup binary") {
		t.Fatalf("code/verified/built/stderr = %d/%d/%d/%s", code, verified, built, stderr.String())
	}
	receipt := connectorRemoteIdentityRecoveryDecodeTestReceipt(t, stdout.Bytes())
	if receipt.BlockReason != connectorRemoteIdentityRecoveryBlockRuntimeUnavailable || strings.Contains(stdout.String(), "old setup binary") {
		t.Fatalf("runtime mismatch receipt = %s", stdout.String())
	}
}

func TestConnectorRemoteIdentityRecoveryPreservesValidCommittedReadbackBlock(t *testing.T) {
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	result := connectorRemoteIdentityRecoveryTestResult(automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeCommittedReadbackBlocked, true, false, false)
	result.BlockReason = "authoritative resolver unavailable"
	validated, contractValid, err := validateConnectorRemoteIdentityRecoveryRuntimeResult(
		connectorRemoteIdentityRecoveryArgs{apply: true}, manifest, result,
		automation.ErrConnectorInboundRemoteIdentityRecoveryBatchReadbackBlocked,
	)
	if !errors.Is(err, automation.ErrConnectorInboundRemoteIdentityRecoveryBatchReadbackBlocked) || !contractValid {
		t.Fatalf("validated error/contract = %v/%t", err, contractValid)
	}
	receipt := connectorRemoteIdentityRecoveryReceiptFromResult(
		connectorRemoteIdentityRecoveryArgs{apply: true, backupMarker: "backup", dryRunReceiptSHA256: strings.Repeat("d", 64)},
		strings.Repeat("e", 64), connectorRemoteIdentityRecoveryTestProof, manifest, validated, contractValid,
	)
	if receipt.Outcome != automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeCommittedReadbackBlocked ||
		!receipt.Committed || receipt.ReadbackVerified || receipt.EnrollmentAllowed || !receipt.ResultContractValid ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.RecoveredCount, 2) ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.ResolverMatchCount, 0) ||
		receipt.BlockReason != connectorRemoteIdentityRecoveryBlockCommittedReadback || len(receipt.Items) != 2 {
		t.Fatalf("committed readback block receipt = %#v", receipt)
	}
}

func TestConnectorRemoteIdentityRecoveryCommittedInvalidResultIsSanitizedWithoutFakeRollback(t *testing.T) {
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	result := automation.ConnectorInboundRemoteIdentityRecoveryBatchResult{
		Outcome: "unexpected", Committed: true, ReadbackVerified: true, EnrollmentAllowed: true,
		RequestedCount: 99, RecoveredCount: 1,
		Items: []automation.ConnectorInboundRemoteIdentityRecoveryBatchItemResult{
			{RemoteID: "remote-1", SourceObject: "Lead", SourceRecordID: "00Q-1", LinkKey: "secret", EvidenceID: "secret"},
			{RemoteID: "remote-2", SourceObject: "Lead", SourceRecordID: "wrong"},
			{RemoteID: "rogue", SourceObject: "Lead", SourceRecordID: "00Q-rogue"},
		},
		BlockReason: "raw internal diagnostic",
	}
	validated, contractValid, err := validateConnectorRemoteIdentityRecoveryRuntimeResult(
		connectorRemoteIdentityRecoveryArgs{apply: true}, manifest, result, nil,
	)
	if err == nil || contractValid {
		t.Fatalf("invalid result error/contract = %v/%t", err, contractValid)
	}
	receipt := connectorRemoteIdentityRecoveryReceiptFromResult(
		connectorRemoteIdentityRecoveryArgs{apply: true}, strings.Repeat("e", 64), connectorRemoteIdentityRecoveryTestProof,
		manifest, validated, contractValid,
	)
	if receipt.Outcome != automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeCommittedReadbackBlocked ||
		!receipt.Committed || receipt.ReadbackVerified || receipt.EnrollmentAllowed || receipt.ResultContractValid ||
		receipt.BlockReason != connectorRemoteIdentityRecoveryBlockRuntimeContractInvalid ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.RequestedCount, 2) ||
		!connectorRemoteIdentityRecoveryCountEquals(receipt.RecoveredCount, 1) || receipt.ResolverMatchCount != nil ||
		len(receipt.Items) != 1 || receipt.Items[0].RemoteID != "remote-1" {
		t.Fatalf("invalid committed receipt = %#v", receipt)
	}
	if strings.Contains(receipt.BlockReason, "diagnostic") {
		t.Fatalf("invalid receipt leaked diagnostic: %#v", receipt)
	}

	result.RecoveredCount = 99
	validated, contractValid, _ = validateConnectorRemoteIdentityRecoveryRuntimeResult(connectorRemoteIdentityRecoveryArgs{apply: true}, manifest, result, nil)
	receipt = connectorRemoteIdentityRecoveryReceiptFromResult(connectorRemoteIdentityRecoveryArgs{apply: true}, "", connectorRemoteIdentityRecoveryTestProof, manifest, validated, contractValid)
	if receipt.RecoveredCount != nil || receipt.FinalizedLinkCount != nil || receipt.ConsumedEvidenceCount != nil {
		t.Fatalf("out-of-range committed counts must be null: %#v", receipt)
	}
}

func TestDecodeConnectorRemoteIdentityRecoveryManifestRejectsAliasesUnknownAndDuplicateAuthority(t *testing.T) {
	for _, raw := range []string{
		`{"schema_version":1,"selector":{"owner":"x","namespace":"y","dest":"z"},"candidates":[{"remote_id":"r","expected_source_object":"Lead","expected_source_record_id":"00Q"}]}`,
		`{"schema_version":1,"selector":{"connector_owner_key":"x","connector_namespace":"y","destination_slot_id":"z"},"candidates":[{"remote_id":"r","expected_source_object":"Lead","expected_source_record_id":"00Q","email":"not-authority@example.invalid"}]}`,
		`{"schema_version":1,"schema_version":1}`,
	} {
		if _, _, err := decodeConnectorRemoteIdentityRecoveryManifest([]byte(raw)); err == nil {
			t.Fatalf("manifest %s unexpectedly accepted", raw)
		}
	}
}

func TestValidateConnectorRemoteIdentityRecoveryRuntimeAuthoritiesBindsPathsAndTuple(t *testing.T) {
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	deploymentRoot := t.TempDir()
	versionRoot := filepath.Join(deploymentRoot, "releases", "release-1")
	if err := os.MkdirAll(filepath.Join(versionRoot, "env"), 0o700); err != nil {
		t.Fatal(err)
	}
	releasePath := filepath.Join(versionRoot, "release.json")
	runtimeEnvPath := filepath.Join(versionRoot, "env", "runtime.env")
	backendPath := filepath.Join(versionRoot, "run_backend.sh")
	setupPath := filepath.Join(versionRoot, "run", "bin", "setup-recovery")
	if err := os.MkdirAll(filepath.Dir(setupPath), 0o700); err != nil {
		t.Fatal(err)
	}
	for _, path := range []string{releasePath, runtimeEnvPath, backendPath, setupPath} {
		if err := os.WriteFile(path, []byte("x"), 0o600); err != nil {
			t.Fatal(err)
		}
	}
	if err := os.MkdirAll(filepath.Join(deploymentRoot, "current", "env"), 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(runtimeEnvPath, filepath.Join(deploymentRoot, "current", "env", "runtime.env")); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(backendPath, filepath.Join(deploymentRoot, "current", "run_backend.sh")); err != nil {
		t.Fatal(err)
	}
	release := connectorRemoteIdentityRecoveryReleaseAuthority{
		ReleaseTag: manifest.DeployedTuple.ReleaseTag, ResolvedDownstreamRef: manifest.DeployedTuple.DownstreamCommit,
		ResolvedUpstreamRef: manifest.DeployedTuple.UpstreamCommit, ResolvedOfficialRef: manifest.DeployedTuple.OfficialAppPacksCommit,
		FinalPackHash: manifest.DeployedTuple.FinalPackHash, TagTargetCommit: manifest.DeployedTuple.DownstreamCommit, VersionRoot: versionRoot,
	}
	runtimeEnv := map[string]string{
		"DEPLOY_ID": manifest.DeployedTuple.DeploymentID, "RELEASE_TAG": manifest.DeployedTuple.ReleaseTag,
		"DOWNSTREAM_REF": manifest.DeployedTuple.DownstreamCommit, "UPSTREAM_REF": manifest.DeployedTuple.UpstreamCommit,
		"OFFICIAL_APP_PACKS_REF": manifest.DeployedTuple.OfficialAppPacksCommit,
		"TAG_TARGET_COMMIT":      manifest.DeployedTuple.DownstreamCommit, "RELEASE_EXECUTOR_COMMIT": manifest.DeployedTuple.DownstreamCommit,
		"ENV_ROOT": versionRoot, "NEXUSCRM_SETUP_BIN": setupPath,
	}
	setupInfo, err := os.Stat(setupPath)
	if err != nil {
		t.Fatal(err)
	}
	process := connectorRemoteIdentityRecoveryProcessIdentity{TargetPath: setupPath, VCSRevision: manifest.DeployedTuple.UpstreamCommit, FileInfo: setupInfo}
	resolvedReleasePath, _ := filepath.EvalSymlinks(releasePath)
	resolvedRuntimeEnvPath, _ := filepath.EvalSymlinks(runtimeEnvPath)
	resolvedSetupPath, _ := filepath.EvalSymlinks(setupPath)
	process.TargetPath = resolvedSetupPath
	if err := validateConnectorRemoteIdentityRecoveryRuntimeAuthorities(manifest, release, resolvedReleasePath, runtimeEnv, resolvedRuntimeEnvPath, process); err != nil {
		t.Fatalf("valid runtime authorities: %v", err)
	}
	if err := validateConnectorRemoteIdentityRecoveryRuntimeAuthorities(manifest, release, resolvedReleasePath+".replaced", runtimeEnv, resolvedRuntimeEnvPath, process); err == nil || !strings.Contains(err.Error(), "release.json descriptor") {
		t.Fatalf("replaced release descriptor error = %v", err)
	}
	process.VCSModified = true
	if err := validateConnectorRemoteIdentityRecoveryRuntimeAuthorities(manifest, release, resolvedReleasePath, runtimeEnv, resolvedRuntimeEnvPath, process); err == nil || !strings.Contains(err.Error(), "modified") {
		t.Fatalf("modified build error = %v", err)
	}
}

func TestValidateConnectorRemoteIdentityRecoveryActiveCurrentRejectsRetainedOldRelease(t *testing.T) {
	deploymentRoot := t.TempDir()
	oldVersionRoot := filepath.Join(deploymentRoot, "releases", "release-old")
	activeVersionRoot := filepath.Join(deploymentRoot, "releases", "release-active")
	for _, versionRoot := range []string{oldVersionRoot, activeVersionRoot} {
		if err := os.MkdirAll(filepath.Join(versionRoot, "env"), 0o700); err != nil {
			t.Fatal(err)
		}
		for _, path := range []string{
			filepath.Join(versionRoot, "env", "runtime.env"),
			filepath.Join(versionRoot, "run_backend.sh"),
		} {
			if err := os.WriteFile(path, []byte(filepath.Base(versionRoot)), 0o600); err != nil {
				t.Fatal(err)
			}
		}
	}
	if err := os.MkdirAll(filepath.Join(deploymentRoot, "current", "env"), 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(
		filepath.Join(activeVersionRoot, "env", "runtime.env"),
		filepath.Join(deploymentRoot, "current", "env", "runtime.env"),
	); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(
		filepath.Join(activeVersionRoot, "run_backend.sh"),
		filepath.Join(deploymentRoot, "current", "run_backend.sh"),
	); err != nil {
		t.Fatal(err)
	}

	err := validateConnectorRemoteIdentityRecoveryActiveCurrent(
		oldVersionRoot,
		filepath.Join(oldVersionRoot, "env", "runtime.env"),
	)
	if err == nil || !strings.Contains(err.Error(), "current runtime.env") {
		t.Fatalf("retained old release active-current error = %v", err)
	}
}

func TestRunConnectorRemoteIdentityRecoveryHoldsCutoverLockThroughRunner(t *testing.T) {
	manifestPath, manifestSHA := connectorRemoteIdentityRecoveryWriteTestFile(t, "manifest.json", connectorRemoteIdentityRecoveryTestManifest())
	deploymentRoot := t.TempDir()
	versionRoot := filepath.Join(deploymentRoot, "releases", "release-1")
	if err := os.MkdirAll(versionRoot, 0o700); err != nil {
		t.Fatal(err)
	}
	lockPath := filepath.Join(deploymentRoot, connectorRemoteIdentityRecoveryCutoverLockName)
	lockWasHeld := false
	runner := &connectorRemoteIdentityRecoveryRunnerStub{
		result: connectorRemoteIdentityRecoveryTestResult(
			automation.ConnectorInboundRemoteIdentityRecoveryBatchOutcomeDryRunVerified,
			false,
			false,
			false,
		),
		onRun: func() {
			fd, err := unix.Open(lockPath, unix.O_RDWR|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0)
			if err != nil {
				t.Fatalf("open competing cutover lock: %v", err)
			}
			defer unix.Close(fd)
			err = unix.Flock(fd, unix.LOCK_EX|unix.LOCK_NB)
			lockWasHeld = errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, unix.EAGAIN)
			if err == nil {
				_ = unix.Flock(fd, unix.LOCK_UN)
			}
		},
	}
	verified := 0
	verifier := func(
		context.Context,
		*database.TiDBConnection,
		serverruntime.ProductHooks,
		connectorRemoteIdentityRecoveryArgs,
		connectorRemoteIdentityRecoveryManifest,
	) (connectorRemoteIdentityRecoveryRuntimeProof, error) {
		verified++
		lockFile, err := acquireConnectorRemoteIdentityRecoveryCutoverLock(versionRoot)
		proof := connectorRemoteIdentityRecoveryTestProof
		proof.cutoverLock = lockFile
		return proof, err
	}
	var stdout, stderr bytes.Buffer
	exitCode := runConnectorRemoteIdentityRecoveryCommand(
		context.Background(),
		nil,
		serverruntime.ProductHooks{},
		connectorRemoteIdentityRecoveryTestArgs(manifestPath, manifestSHA),
		&stdout,
		&stderr,
		verifier,
		func(*database.TiDBConnection, serverruntime.ProductHooks) (connectorRemoteIdentityRecoveryBatchRunner, error) {
			return runner, nil
		},
	)
	if exitCode != 0 || verified != 1 || !lockWasHeld {
		t.Fatalf("cutover-fenced command exit/verifier/held = %d/%d/%t stderr=%s", exitCode, verified, lockWasHeld, stderr.String())
	}

	fd, err := unix.Open(lockPath, unix.O_RDWR|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0)
	if err != nil {
		t.Fatal(err)
	}
	defer unix.Close(fd)
	if err := unix.Flock(fd, unix.LOCK_EX|unix.LOCK_NB); err != nil {
		t.Fatalf("cutover lock was not released after command: %v", err)
	}
	_ = unix.Flock(fd, unix.LOCK_UN)
}

func TestReadConnectorRemoteIdentityRecoveryInheritedAuthorityPreservesInodeAcrossSamePathReplacement(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("/proc/self/fd replacement proof is Linux-specific")
	}
	directory := t.TempDir()
	path := filepath.Join(directory, "release.json")
	original := []byte(`{"release_tag":"original"}`)
	if err := os.WriteFile(path, original, 0o600); err != nil {
		t.Fatal(err)
	}
	file, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	defer file.Close()
	if err := os.Rename(path, path+".old"); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(path, []byte(`{"release_tag":"replacement"}`), 0o600); err != nil {
		t.Fatal(err)
	}
	fdPath := fmt.Sprintf("/proc/self/fd/%d", file.Fd())
	data, target, err := readConnectorRemoteIdentityRecoveryInheritedAuthority(fdPath)
	if err != nil {
		t.Fatalf("read inherited authority: %v", err)
	}
	if !bytes.Equal(data, original) || target == path {
		t.Fatalf("descriptor did not retain original inode: data=%s target=%s", data, target)
	}
}

func TestVerifyConnectorRemoteIdentityRecoveryCompiledSourceLocksAcceptsIndependentRevision(t *testing.T) {
	hooks := serverruntime.ProductHooks{Capabilities: serverruntime.ProductCapabilities{OfficialRepoSourceRevisions: []serverruntime.OfficialRepoSourceRevision{
		{Name: "SalesforceCRM", Revision: connectorRemoteIdentityRecoveryTestOfficial},
	}}}
	if err := verifyConnectorRemoteIdentityRecoveryCompiledSourceLocks(hooks); err != nil {
		t.Fatalf("verify compiled source locks: %v", err)
	}
}

func TestVerifyConnectorRemoteIdentityRecoveryCompiledSourceLocksRejectsInvalidRegistrations(t *testing.T) {
	tests := []struct {
		name          string
		registrations []serverruntime.OfficialRepoSourceRevision
		want          string
	}{
		{name: "missing", want: "no official app-pack source revisions"},
		{name: "blank name", registrations: []serverruntime.OfficialRepoSourceRevision{{Revision: connectorRemoteIdentityRecoveryTestOfficial}}, want: "empty name"},
		{name: "mutable revision", registrations: []serverruntime.OfficialRepoSourceRevision{{Name: "SalesforceCRM", Revision: "main"}}, want: "immutable 40-hex"},
		{name: "case insensitive duplicate", registrations: []serverruntime.OfficialRepoSourceRevision{
			{Name: "SalesforceCRM", Revision: connectorRemoteIdentityRecoveryTestOfficial},
			{Name: "salesforcecrm", Revision: connectorRemoteIdentityRecoveryTestOfficial},
		}, want: "duplicated"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			hooks := serverruntime.ProductHooks{Capabilities: serverruntime.ProductCapabilities{OfficialRepoSourceRevisions: tt.registrations}}
			err := verifyConnectorRemoteIdentityRecoveryCompiledSourceLocks(hooks)
			if err == nil || !strings.Contains(err.Error(), tt.want) {
				t.Fatalf("error = %v, want containing %q", err, tt.want)
			}
		})
	}
}

func TestConnectorRemoteIdentityRecoveryCanonicalRuntimeBindingMatchesL2Contract(t *testing.T) {
	manifest := connectorRemoteIdentityRecoveryTestDecodedManifest(t)
	deployedTupleSHA, err := hashConnectorRemoteIdentityRecoveryCanonicalJSON(connectorRemoteIdentityRecoveryTupleMap(manifest.DeployedTuple))
	if err != nil {
		t.Fatal(err)
	}
	runtimeBindingSHA, err := hashConnectorRemoteIdentityRecoveryCanonicalJSON(map[string]any{
		"deployed_tuple": connectorRemoteIdentityRecoveryTupleMap(manifest.DeployedTuple),
		"runtime_binding": map[string]any{
			"release_json_sha256": manifest.RuntimeBinding.ReleaseJSONSHA256,
			"runtime_env_sha256":  manifest.RuntimeBinding.RuntimeEnvSHA256,
			"setup_binary_sha256": manifest.RuntimeBinding.SetupBinarySHA256,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if deployedTupleSHA != "659cbb42b058ca3e97f0c5a258c11524727f7c24a1a4fca4bbf6d51e155d5db3" ||
		runtimeBindingSHA != "9043c2ddb085ad792a938eed65b05d9823d0ec2e4fc4139e59343c14f8c737c8" {
		t.Fatalf("canonical hashes = %s/%s", deployedTupleSHA, runtimeBindingSHA)
	}
}
