//go:build integration

package controlplane

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/nexuscrm/db"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/fieldprovenance"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

func TestVerifyPathFailsClosedForMissingPhysicalColumnWithoutHashDrift(t *testing.T) {
	ctx := context.Background()
	conn := installJiraLikeSourcePackForRecoveryProof(t, ctx)
	defer conn.Close()

	// Drop a physical column without changing AppliedPayloadHash. The field row
	// still exists, so reconcile must fail closed instead of silently replaying
	// an empty replacement column and hiding lost record data.
	if _, err := conn.DB().ExecContext(ctx, "ALTER TABLE `Issue` DROP COLUMN `Subject`"); err != nil {
		t.Fatalf("drop column: %v", err)
	}

	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("refresh cache: %v", err)
	}

	err = reconcileActiveInstalledPackSourceState(ctx, r, InstallPackOptions{})
	if err == nil {
		t.Fatal("reconcileActiveInstalledPackSourceState = nil, want error for missing physical column")
	}
	if !strings.Contains(err.Error(), "physical column Issue.Subject is missing") {
		t.Fatalf("reconcileActiveInstalledPackSourceState error = %v, want error containing 'physical column Issue.Subject is missing'", err)
	}
}

func TestExplicitApplyAllRepairsMissingPhysicalColumn(t *testing.T) {
	ctx := context.Background()
	conn := installJiraLikeSourcePackForRecoveryProof(t, ctx)
	defer conn.Close()

	// Drop a physical column — the explicit apply-all path (InstallPackFromSource)
	// must still repair it, unlike the background reconcile verify path.
	if _, err := conn.DB().ExecContext(ctx, "ALTER TABLE `Issue` DROP COLUMN `Subject`"); err != nil {
		t.Fatalf("drop column: %v", err)
	}

	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("refresh cache: %v", err)
	}

	source := "local_path:" + jiraLikePackSourceDir(t)
	if err := InstallPackFromSource(ctx, r, source); err != nil {
		t.Fatalf("InstallPackFromSource (repair): %v", err)
	}
	if err := VerifyInstalledPackByName(ctx, conn.DB(), "jira-like-sample", true); err != nil {
		t.Fatalf("verify after repair: %v", err)
	}
}

func TestApplyAllRepairsUndeclaredPackOwnedNonDefaultPageLayoutRows(t *testing.T) {
	ctx := context.Background()
	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	defer cleanup()
	setDatabaseEnvFromConfig(t, cfg)

	conn := openIntegrationConnection(t, cfg)
	if err := InitL0(ctx, conn); err != nil {
		conn.Close()
		t.Fatalf("init-l0: %v", err)
	}
	conn.Close()

	conn = openIntegrationConnection(t, cfg)
	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("initial cache refresh: %v", err)
	}
	if err := InstallPackFromSource(ctx, r, "local_path:"+jiraLikePackSourceDir(t)); err != nil {
		t.Fatalf("install source-backed pack: %v", err)
	}
	if err := VerifyInstalledPackByName(ctx, conn.DB(), "jira-like-sample", true); err != nil {
		t.Fatalf("verify installed source-backed pack: %v", err)
	}
	defer conn.Close()

	if _, err := conn.DB().ExecContext(ctx, `
		INSERT INTO _System_PageLayout
			(Id, ObjectApiName, LayoutName, Type, IsDefault, IsDeleted, Config, Source, OwnerPack, IsCustomized, CreatedDate, LastModifiedDate)
		VALUES
			('legacy_issue_layout', 'Issue', 'Legacy Issue Layout', 'record', 0, 0, JSON_OBJECT('sections', JSON_ARRAY()), 'curated', 'jira-like-sample', 0, NOW(), NOW())
	`); err != nil {
		t.Fatalf("seed stale non-default page_layout row: %v", err)
	}

	verifyErr := Verify(ctx, conn)
	if verifyErr == nil {
		t.Fatal("Verify after stale pack-owned page_layout row = nil, want drift error")
	}
	if !strings.Contains(verifyErr.Error(), "undeclared non-default page_layout row") {
		t.Fatalf("Verify error = %v, want undeclared page_layout drift", verifyErr)
	}
	if !isRepairableInstalledPackApplyAllError(verifyErr) {
		t.Fatalf("Verify error = %v, want ApplyAll-repairable classification", verifyErr)
	}

	if err := ApplyAll(ctx, conn); err != nil {
		t.Fatalf("ApplyAll after stale pack-owned page_layout row: %v", err)
	}

	checkConn := openIntegrationConnection(t, cfg)
	defer checkConn.Close()
	if err := Verify(ctx, checkConn); err != nil {
		t.Fatalf("Verify after ApplyAll page_layout repair = %v", err)
	}
	var deleted int
	if err := checkConn.DB().QueryRowContext(ctx, `
		SELECT COALESCE(IsDeleted, 0)
		FROM _System_PageLayout
		WHERE Id = 'legacy_issue_layout'
	`).Scan(&deleted); err != nil {
		t.Fatalf("query repaired page_layout row: %v", err)
	}
	if deleted != 1 {
		t.Fatalf("legacy_issue_layout IsDeleted = %d, want 1 after ApplyAll repair", deleted)
	}
}

func TestRegisterInstalledRuntimeMetadataRestoresInstalledSourceBackedRuntimeState(t *testing.T) {
	ctx := context.Background()
	conn := installJiraLikeSourcePackForRecoveryProof(t, ctx)
	defer conn.Close()

	assertInstalledSourceBackedIssueFieldProvenance(t)

	resetInstalledRuntimeStateForTest()
	if got := fieldprovenance.ClassifySource("Issue", "Subject"); got == constants.FieldSourceStandardSpec {
		t.Fatalf("field provenance before restart recovery = %q, want source-backed runtime state cleared", got)
	}

	if err := packs.RegisterInstalledRuntimeMetadata(ctx, conn.DB()); err != nil {
		t.Fatalf("register installed runtime metadata: %v", err)
	}

	assertInstalledSourceBackedIssueFieldProvenance(t)
}

func TestRebuildDerivedRestoresInstalledSourceBackedRuntimeState(t *testing.T) {
	ctx := context.Background()
	conn := installJiraLikeSourcePackForRecoveryProof(t, ctx)
	defer conn.Close()

	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("initial cache refresh: %v", err)
	}
	assertInstalledSourceBackedIssueFieldProvenance(t)

	resetInstalledRuntimeStateForTest()
	if got := fieldprovenance.ClassifySource("Issue", "Subject"); got == constants.FieldSourceStandardSpec {
		t.Fatalf("field provenance before rebuild recovery = %q, want source-backed runtime state cleared", got)
	}

	if err := RebuildDerived(ctx, r); err != nil {
		t.Fatalf("rebuild derived: %v", err)
	}

	assertInstalledSourceBackedIssueFieldProvenance(t)
}

func TestVerifyRejectsMissingDerivedArtifactsForInstalledSourceBackedPack(t *testing.T) {
	ctx := context.Background()
	conn := installFormulaSourcePackForRecoveryProof(t, ctx)
	defer conn.Close()

	if _, err := conn.DB().ExecContext(ctx, "DROP VIEW IF EXISTS `v_issue`"); err != nil {
		t.Fatalf("drop formula view v_issue: %v", err)
	}

	err := Verify(ctx, conn)
	if err == nil {
		t.Fatal("Verify = nil, want derived-artifact failure")
	}
	if !strings.Contains(err.Error(), "derived artifacts") || !strings.Contains(err.Error(), "formula view v_issue") {
		t.Fatalf("Verify error = %v, want derived-artifact formula-view failure", err)
	}
}

func TestActiveFormulaFixturePackInstallsCompilesAndRebuilds(t *testing.T) {
	ctx := context.Background()
	conn := installSourcePackForRecoveryProofWithPackName(t, ctx, "local_path:"+activeFormulaSmokePackSourceDir(t), "active-formula-smoke")
	defer conn.Close()

	if err := VerifyInstalledPackByName(ctx, conn.DB(), "active-formula-smoke", true); err != nil {
		t.Fatalf("verify installed active-formula-smoke pack: %v", err)
	}

	var viewCount int
	if err := conn.DB().QueryRowContext(ctx, `
		SELECT COUNT(*)
		  FROM information_schema.VIEWS
		 WHERE TABLE_SCHEMA = DATABASE()
		   AND TABLE_NAME = 'v_workitem'`,
	).Scan(&viewCount); err != nil {
		t.Fatalf("query active formula smoke view: %v", err)
	}
	if viewCount != 1 {
		t.Fatalf("active formula smoke view count = %d, want 1", viewCount)
	}
}

func TestApplyOfflineCandidateTiDBTransitionsViewFormulaToMaterializedStorage(t *testing.T) {
	ctx := context.Background()
	root := writeFormulaStorageTransitionPackSourceForRecoveryProof(t, false)
	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	defer cleanup()
	setDatabaseEnvFromConfig(t, cfg)
	conn := openIntegrationConnection(t, cfg)
	if err := InitL0(ctx, conn); err != nil {
		_ = conn.Close()
		t.Fatalf("init-l0: %v", err)
	}
	_ = conn.Close()
	conn = openIntegrationConnection(t, cfg)
	defer conn.Close()
	runner, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new initial formula transition runner: %v", err)
	}
	if err := runner.RefreshCache(ctx); err != nil {
		t.Fatalf("refresh initial formula transition runner: %v", err)
	}
	if err := InstallPackFromSource(ctx, runner, "local_path:"+root); err != nil {
		t.Fatalf("install initial view-only formula pack: %v", err)
	}
	if err := VerifyInstalledPackByName(ctx, conn.DB(), "formula-storage-transition", true); err != nil {
		t.Fatalf("verify initial view-only formula pack: %v", err)
	}

	physicalColumnCount := func(current *database.TiDBConnection) int {
		t.Helper()
		var count int
		if err := current.DB().QueryRowContext(ctx, `
			SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
			 WHERE TABLE_SCHEMA = DATABASE()
			   AND TABLE_NAME = 'Issue'
			   AND COLUMN_NAME = 'SubjectEcho'`).Scan(&count); err != nil {
			t.Fatalf("count Issue.SubjectEcho physical columns: %v", err)
		}
		return count
	}
	if got := physicalColumnCount(conn); got != 0 {
		t.Fatalf("view-only Issue.SubjectEcho physical column count = %d, want 0", got)
	}
	if _, err := conn.DB().ExecContext(ctx, "INSERT INTO `Issue` (`Id`, `Subject`, `CreatedDate`, `LastModifiedDate`, `SystemModstamp`) VALUES ('issue-1', 'Initial subject', NOW(), NOW(), NOW())"); err != nil {
		t.Fatalf("seed view-only Issue row: %v", err)
	}
	var initialEcho string
	if err := conn.DB().QueryRowContext(ctx, "SELECT `SubjectEcho` FROM `v_issue` WHERE `Id` = 'issue-1'").Scan(&initialEcho); err != nil {
		t.Fatalf("read initial formula view: %v", err)
	}
	if initialEcho != "Initial subject" {
		t.Fatalf("initial SubjectEcho = %q, want Initial subject", initialEcho)
	}
	// A readable view is not necessarily authoritative. Simulate a stale
	// derived artifact left by an interrupted/deferred rebuild; the offline
	// transition must regenerate it from live metadata before copying values.
	if _, err := conn.DB().ExecContext(ctx, `CREATE OR REPLACE SQL SECURITY INVOKER VIEW v_issue AS
		SELECT base.*, CONCAT('stale:', base.Subject) AS SubjectEcho FROM Issue AS base`); err != nil {
		t.Fatalf("seed stale but readable formula view: %v", err)
	}
	var staleEcho string
	if err := conn.DB().QueryRowContext(ctx, "SELECT `SubjectEcho` FROM `v_issue` WHERE `Id` = 'issue-1'").Scan(&staleEcho); err != nil {
		t.Fatalf("read stale formula view: %v", err)
	}
	if staleEcho != "stale:Initial subject" {
		t.Fatalf("stale SubjectEcho = %q, want stale:Initial subject", staleEcho)
	}

	writeFormulaStorageTransitionPackSourceAtRoot(t, root, true)
	runner, err = NewRunner(conn)
	if err != nil {
		t.Fatalf("new online transition runner: %v", err)
	}
	if err := runner.RefreshCache(ctx); err != nil {
		t.Fatalf("refresh online transition runner: %v", err)
	}
	err = InstallPackFromSource(ctx, runner, "local_path:"+root)
	if err == nil || !strings.Contains(err.Error(), "stopped-Runtime offline candidate") {
		t.Fatalf("ordinary online transition error = %v, want stopped-Runtime rejection", err)
	}
	if got := physicalColumnCount(conn); got != 0 {
		t.Fatalf("rejected online transition created %d physical columns", got)
	}

	receipt, err := ApplyOfflineCandidate(ctx, conn, OfflineCandidateRequest{
		OperationID: "formula-storage-transition",
		UpgradeMode: UpgradeModeOfflineRequired,
		PackSources: []string{"local_path:" + root},
	}, InstallPackOptions{VerifyAfterApply: true})
	if err != nil {
		t.Fatalf("apply formula-storage offline candidate: %v", err)
	}
	if !receipt.Verified || !receipt.Activated {
		t.Fatalf("formula-storage candidate receipt = %#v, want verified activation", receipt)
	}
	checkConn := openIntegrationConnection(t, cfg)
	defer checkConn.Close()
	if got := physicalColumnType(t, checkConn.DB(), "Issue", "SubjectEcho"); got == "" {
		t.Fatal("materialized Issue.SubjectEcho physical column is absent")
	}
	var physicalEcho, viewEcho string
	if err := checkConn.DB().QueryRowContext(ctx, "SELECT `SubjectEcho` FROM `Issue` WHERE `Id` = 'issue-1'").Scan(&physicalEcho); err != nil {
		t.Fatalf("read materialized formula value: %v", err)
	}
	if err := checkConn.DB().QueryRowContext(ctx, "SELECT `SubjectEcho` FROM `v_issue` WHERE `Id` = 'issue-1'").Scan(&viewEcho); err != nil {
		t.Fatalf("read switched formula view: %v", err)
	}
	if physicalEcho != "Initial subject" || viewEcho != physicalEcho {
		t.Fatalf("materialized values physical=%q view=%q, want Initial subject", physicalEcho, viewEcho)
	}

	var indexCount int
	if err := checkConn.DB().QueryRowContext(ctx, `
		SELECT COUNT(*)
		  FROM INFORMATION_SCHEMA.STATISTICS
		 WHERE TABLE_SCHEMA = DATABASE()
		   AND TABLE_NAME = 'Issue'
		   AND INDEX_NAME = 'idx_issue_subject_echo'
		   AND COLUMN_NAME = 'SubjectEcho'`).Scan(&indexCount); err != nil {
		t.Fatalf("read materialized formula index: %v", err)
	}
	if indexCount != 1 {
		t.Fatalf("materialized formula index column count = %d, want 1", indexCount)
	}

	// The activated candidate is exact and therefore an idempotent retry must
	// converge without another storage transition.
	retry, err := ApplyOfflineCandidate(ctx, checkConn, OfflineCandidateRequest{
		OperationID: "formula-storage-transition-retry",
		UpgradeMode: UpgradeModeOfflineRequired,
		PackSources: []string{"local_path:" + root},
	}, InstallPackOptions{VerifyAfterApply: true})
	if err != nil {
		t.Fatalf("retry formula-storage offline candidate: %v", err)
	}
	if !retry.Verified || !retry.Activated {
		t.Fatalf("formula-storage retry receipt = %#v, want verified activation", retry)
	}
}

func TestManifestV1LegacyActiveScalarPackFailsFreshInstall(t *testing.T) {
	ctx := context.Background()
	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	defer cleanup()
	setDatabaseEnvFromConfig(t, cfg)

	conn := openIntegrationConnection(t, cfg)
	if err := InitL0(ctx, conn); err != nil {
		_ = conn.Close()
		t.Fatalf("init-l0: %v", err)
	}
	_ = conn.Close()

	conn = openIntegrationConnection(t, cfg)
	defer conn.Close()

	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("initial cache refresh: %v", err)
	}

	err = InstallPackFromSource(ctx, r, "local_path:"+writeLegacyActiveScalarFormulaPackSourceForRecoveryProof(t))
	if err == nil {
		t.Fatal("InstallPackFromSource accepted manifest v1 legacy active-scalar formula pack; want fail-closed error")
	}
	if !strings.Contains(err.Error(), "field.formula.expression_on_non_formula_type") {
		t.Fatalf("InstallPackFromSource error = %v, want formula/type coherence diagnostic", err)
	}
}

func installJiraLikeSourcePackForRecoveryProof(t *testing.T, ctx context.Context) *database.TiDBConnection {
	t.Helper()
	return installSourcePackForRecoveryProof(t, ctx, "local_path:"+jiraLikePackSourceDir(t))
}

func installFormulaSourcePackForRecoveryProof(t *testing.T, ctx context.Context) *database.TiDBConnection {
	t.Helper()
	return installSourcePackForRecoveryProof(t, ctx, "local_path:"+writeFormulaPackSourceForRecoveryProof(t))
}

func installSourcePackForRecoveryProof(t *testing.T, ctx context.Context, source string) *database.TiDBConnection {
	t.Helper()
	return installSourcePackForRecoveryProofWithPackName(t, ctx, source, "jira-like-sample")
}

func installSourcePackForRecoveryProofWithPackName(t *testing.T, ctx context.Context, source string, packName string) *database.TiDBConnection {
	t.Helper()

	if source == "official_repo:salesforce-crm-standard" {
		t.Setenv(packs.OfficialAppPacksRootEnvVar, officialPackRootFromControlplaneTest(t))
	}

	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	t.Cleanup(cleanup)
	setDatabaseEnvFromConfig(t, cfg)

	conn := openIntegrationConnection(t, cfg)
	if err := InitL0(ctx, conn); err != nil {
		_ = conn.Close()
		t.Fatalf("init-l0: %v", err)
	}
	_ = conn.Close()

	conn = openIntegrationConnection(t, cfg)
	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("initial cache refresh: %v", err)
	}
	if err := InstallPackFromSource(ctx, r, source); err != nil {
		t.Fatalf("install source-backed pack: %v", err)
	}
	if err := VerifyInstalledPackByName(ctx, conn.DB(), packName, true); err != nil {
		t.Fatalf("verify installed source-backed pack: %v", err)
	}
	return conn
}

func jiraLikePackSourceDir(t *testing.T) string {
	t.Helper()

	return controlplaneTestPackFixtureDir(t, "jira-like")
}

func activeFormulaSmokePackSourceDir(t *testing.T) string {
	t.Helper()

	return controlplaneTestPackFixtureDir(t, "active-formula-smoke")
}

func writeFormulaPackSourceForRecoveryProof(t *testing.T) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "jira-like-sample",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}

	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Issue work item",
    "fields": [
      {"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true},
      {"apiName": "SubjectEcho", "label": "Subject Echo", "type": "formula", "formulaMode": "active", "formula": "Subject", "returnType": "string"}
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}

	return root
}

func writeFormulaStorageTransitionPackSourceForRecoveryProof(t *testing.T, materialized bool) string {
	t.Helper()
	root := t.TempDir()
	writeFormulaStorageTransitionPackSourceAtRoot(t, root, materialized)
	return root
}

func writeFormulaStorageTransitionPackSourceAtRoot(t *testing.T, root string, materialized bool) {
	t.Helper()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir formula storage transition objects dir: %v", err)
	}
	manifest := `{
  "manifestFormatVersion": "1",
  "name": "formula-storage-transition",
  "displayName": "Formula Storage Transition",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {"min": "0.2.0", "maxExclusive": "0.3.0"},
  "dependencies": [],
  "components": [
    {"kind": "object_spec", "businessKey": "issue", "manageability": "locked", "path": "objects/issue.json"}
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write formula storage transition manifest: %v", err)
	}
	storage := ""
	indices := ""
	if materialized {
		storage = `, "formulaStorageMode": "write_materialized"`
		indices = `,
    "indices": [
      {"name": "idx_issue_subject_echo", "columns": ["SubjectEcho"]}
    ]`
	}
	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Formula storage transition fixture",
    "fields": [
      {"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true},
      {"apiName": "SubjectEcho", "label": "Subject Echo", "type": "formula", "formulaMode": "active", "formula": "Subject", "returnType": "string"` + storage + `}
    ]` + indices + `
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write formula storage transition object spec: %v", err)
	}
}

func writeLegacyActiveScalarFormulaPackSourceForRecoveryProof(t *testing.T) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "jira-like-sample",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}

	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Issue work item",
    "fields": [
      {"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true},
      {"apiName": "SubjectEcho", "label": "Subject Echo", "type": "string", "formulaMode": "active", "formula": "Subject", "returnType": "string"}
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}

	return root
}

// TestApplyAllRepairsMissingPhysicalColumnForInstalledSourceBackedPack verifies
// that ApplyAll's step-6 reconcile path (reconcileActiveInstalledPackSourceState)
// repairs a source-backed installed pack that has a missing physical column and
// deleted _System_Field row, while background Verify (pod-readiness) correctly
// reports the error without repairing it.
func TestApplyAllRepairsMissingPhysicalColumnForInstalledSourceBackedPack(t *testing.T) {
	ctx := context.Background()
	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	defer cleanup()
	setDatabaseEnvFromConfig(t, cfg)

	conn := openIntegrationConnection(t, cfg)
	if err := InitL0(ctx, conn); err != nil {
		_ = conn.Close()
		t.Fatalf("init-l0: %v", err)
	}
	_ = conn.Close()

	conn = openIntegrationConnection(t, cfg)
	defer conn.Close()

	r, err := NewRunner(conn)
	if err != nil {
		t.Fatalf("new runner: %v", err)
	}
	if err := r.RefreshCache(ctx); err != nil {
		t.Fatalf("initial cache refresh: %v", err)
	}

	source := "local_path:" + jiraLikePackSourceDir(t)
	if err := InstallPackFromSource(ctx, r, source); err != nil {
		t.Fatalf("install jira-like source-backed pack: %v", err)
	}

	// Simulate drift: delete the _System_Field row and drop the physical column
	// for Issue.Status to reproduce the "missing column" repair scenario.
	if _, err := conn.DB().ExecContext(ctx, `
		DELETE f
		  FROM _System_Field f
		  JOIN _System_Object o ON o.Id = f.ObjectId
		 WHERE o.ApiName = 'Issue'
		   AND f.ApiName = 'Status'
	`); err != nil {
		t.Fatalf("delete Issue.Status field row: %v", err)
	}
	if _, err := conn.DB().ExecContext(ctx, "ALTER TABLE `Issue` DROP COLUMN `Status`"); err != nil {
		t.Fatalf("drop Issue.Status physical column: %v", err)
	}

	// Background verify (pod-readiness) must fail closed — it detects the drift
	// but does not repair it.
	verifyErr := Verify(ctx, conn)
	if verifyErr == nil {
		t.Fatal("Verify after column drift = nil, want installed-pack drift error")
	}
	// The error must be classified as repairable by the ApplyAll predicate so
	// that ApplyAll's reconcile path (step 6) triggers the replay.
	if !isRepairableInstalledPackApplyAllError(verifyErr) {
		t.Fatalf("Verify error = %v, want repairable classify by ApplyAll predicate", verifyErr)
	}

	// ApplyAll's reconcile path must repair the drift and exit cleanly.
	if err := ApplyAll(ctx, conn); err != nil {
		t.Fatalf("ApplyAll after missing physical column: %v", err)
	}

	// Open a fresh connection to confirm the repaired state survives the
	// connection refresh that ApplyMetadata performs internally.
	checkConn := openIntegrationConnection(t, cfg)
	defer checkConn.Close()

	if err := Verify(ctx, checkConn); err != nil {
		t.Fatalf("Verify after ApplyAll repair = %v", err)
	}
	if physicalColumnType(t, checkConn.DB(), "Issue", "Status") == "" {
		t.Fatal("Issue.Status physical column missing after ApplyAll repair")
	}
}

func resetInstalledRuntimeStateForTest() {
	metadata.ClearPackRelatedListRules()
	metadata.ClearTenantRelatedListRules()
	fieldprovenance.SetDefault(nil)
}

func assertInstalledSourceBackedIssueFieldProvenance(t *testing.T) {
	t.Helper()

	if got := fieldprovenance.ClassifySource("Issue", "Subject"); got != constants.FieldSourceStandardSpec {
		t.Fatalf("field provenance Issue.Subject = %q, want %q", got, constants.FieldSourceStandardSpec)
	}
	if got := fieldprovenance.ClassifySource("Issue", "Name"); got != constants.FieldSourceManaged {
		t.Fatalf("field provenance Issue.Name = %q, want %q", got, constants.FieldSourceManaged)
	}
}
