//go:build integration

package controlplane

import (
	"context"
	"database/sql"
	"strings"
	"testing"

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

// TestReconcileActiveInstalledPackSourceStateReappliesOnHashDrift is an
// end-to-end integration test for the substrate primitive landed in
// commit 168e656. It seeds an active _System_InstalledPack row with
// synthetic stale hashes, calls reconcileActiveInstalledPackSourceState,
// and asserts:
//
//	(a) AppliedPayloadHash and DesiredPayloadHash are refreshed to the
//	    current pack content hash, and
//	(b) the new ISCHANGED validation rule set lands and the old
//	    vrule_lead_post_convert_read_only rule (replaced in NX#551) is absent.
func TestReconcileActiveInstalledPackSourceStateReappliesOnHashDrift(t *testing.T) {
	ctx := context.Background()
	cfg, cleanup := newTemporaryIntegrationDatabase(t)
	defer cleanup()

	conn := initializeControlPlaneMetadata(t, ctx, 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)
	}
	if err := installSourceBackedCRMPack(t, ctx, r); err != nil {
		t.Fatalf("install salesforce-crm-standard: %v", err)
	}

	// Capture the current (correct) hash from the installed row.
	var currentApplied, currentDesired sql.NullString
	var activePackInstallID string
	if err := conn.DB().QueryRowContext(ctx, `
			SELECT AppliedPayloadHash, DesiredPayloadHash, PackInstallId
			  FROM _System_InstalledPack
		 WHERE Name = 'salesforce-crm-standard'
		   AND COALESCE(IsDeleted, 0) = 0
		 LIMIT 1`,
	).Scan(&currentApplied, &currentDesired, &activePackInstallID); err != nil {
		t.Fatalf("query current installed pack hash: %v", err)
	}
	if !currentApplied.Valid || currentApplied.String == "" {
		t.Fatal("AppliedPayloadHash after fresh install is empty")
	}
	if !currentDesired.Valid || currentDesired.String != currentApplied.String {
		t.Fatalf("DesiredPayloadHash=%q != AppliedPayloadHash=%q after fresh install",
			currentDesired.String, currentApplied.String)
	}
	currentHash := currentApplied.String
	assertOnePackGenerationAuthority(
		t,
		ctx,
		conn.DB(),
		"salesforce-crm-standard",
	)

	// Simulate a warm-upgraded org: overwrite both hash columns with a
	// synthetic stale V1 hash. Source identity (SourceKind/SourceURI) is
	// intentionally preserved so reconcileActiveInstalledPackSourceState
	// can resolve the pack and take the hash-drift reapply branch.
	staleHash := packs.PackPayloadHashV1Prefix + strings.Repeat("0", 64)
	if staleHash == currentHash {
		t.Fatal("test setup: staleHash must differ from currentHash")
	}
	if _, err := conn.DB().ExecContext(ctx, `
		UPDATE _System_InstalledPack
		   SET AppliedPayloadHash = ?,
		       DesiredPayloadHash = ?,
		       LastModifiedDate   = NOW()
		 WHERE Name = 'salesforce-crm-standard'`,
		staleHash, staleHash,
	); err != nil {
		t.Fatalf("seed stale hash: %v", err)
	}

	// Act: ApplyAll repair-mode reconcile must detect the hash drift and
	// reapply the pack.
	if err := reconcileActiveInstalledPackSourceState(ctx, r, InstallPackOptions{
		allowInstalledPackSourceStateRepair: true,
	}); err != nil {
		t.Fatalf("reconcileActiveInstalledPackSourceState: %v", err)
	}

	// Assert (a): hash columns are refreshed.
	var gotApplied, gotDesired sql.NullString
	var repairedPackInstallID string
	if err := conn.DB().QueryRowContext(ctx, `
			SELECT AppliedPayloadHash, DesiredPayloadHash, PackInstallId
		  FROM _System_InstalledPack
		 WHERE Name = 'salesforce-crm-standard'
		   AND COALESCE(IsDeleted, 0) = 0
		 LIMIT 1`,
	).Scan(&gotApplied, &gotDesired, &repairedPackInstallID); err != nil {
		t.Fatalf("query installed pack hash after reconcile: %v", err)
	}
	if !gotApplied.Valid || gotApplied.String != currentHash {
		t.Errorf("AppliedPayloadHash after reconcile = %q, want %q", gotApplied.String, currentHash)
	}
	if !gotDesired.Valid || gotDesired.String != currentHash {
		t.Errorf("DesiredPayloadHash after reconcile = %q, want %q", gotDesired.String, currentHash)
	}
	if repairedPackInstallID != activePackInstallID {
		t.Fatalf(
			"same-generation repair changed PackInstallId = %q, want preserved %q",
			repairedPackInstallID,
			activePackInstallID,
		)
	}
	assertActivePackGenerationBinding(
		t,
		ctx,
		conn.DB(),
		"salesforce-crm-standard",
	)

	// Assert (b): new ISCHANGED validation rules are present and active.
	newRules := []string{
		"Lead_IsConverted_Immutable_Post_Convert",
		"Lead_ConvertedAccountId_Immutable_Post_Convert",
		"Lead_ConvertedContactId_Immutable_Post_Convert",
		"Lead_ConvertedOpportunityId_Immutable_Post_Convert",
		"Lead_ConvertedDate_Immutable_Post_Convert",
	}
	for _, ruleName := range newRules {
		var active sql.NullInt64
		var ownerPack sql.NullString
		err := conn.DB().QueryRowContext(ctx, `
			SELECT Active, OwnerPack
			  FROM _System_ValidationRule
			 WHERE ObjectApiName = 'Lead'
			   AND Name = ?
			 LIMIT 1`, ruleName,
		).Scan(&active, &ownerPack)
		if err == sql.ErrNoRows {
			t.Errorf("validation rule Lead/%s not found after reconcile", ruleName)
			continue
		}
		if err != nil {
			t.Fatalf("query validation rule Lead/%s: %v", ruleName, err)
		}
		if !active.Valid || active.Int64 != 1 {
			t.Errorf("validation rule Lead/%s Active = %v (valid=%v), want 1", ruleName, active.Int64, active.Valid)
		}
		if !ownerPack.Valid || ownerPack.String != "salesforce-crm-standard" {
			t.Errorf("validation rule Lead/%s OwnerPack = %q, want \"salesforce-crm-standard\"", ruleName, ownerPack.String)
		}
	}

	// Assert (b cont.): the old monolithic rule replaced by NX#551 is absent
	// (it was removed from the pack; a post-drift reapply must not leave it).
	var oldRuleCount int
	if err := conn.DB().QueryRowContext(ctx, `
		SELECT COUNT(*)
		  FROM _System_ValidationRule
		 WHERE ObjectApiName = 'Lead'
		   AND Name = 'Lead_Post_Convert_Read_Only'`,
	).Scan(&oldRuleCount); err != nil {
		t.Fatalf("query old validation rule count: %v", err)
	}
	if oldRuleCount != 0 {
		t.Errorf("old validation rule Lead/Lead_Post_Convert_Read_Only still present (%d row(s)) after hash-drift reapply", oldRuleCount)
	}

	// Assert (c): umbrella validation rule lands after hash-drift reapply.
	// Without this assertion the test would silently pass even if the reapply
	// path skipped installing the umbrella rule introduced alongside NX#551.
	{
		const umbrellaRuleName = "Lead_Post_Convert_Requires_AllowViewEditConvertedLeads"
		var active sql.NullInt64
		var ownerPack sql.NullString
		err := conn.DB().QueryRowContext(ctx, `
			SELECT Active, OwnerPack
			  FROM _System_ValidationRule
			 WHERE ObjectApiName = 'Lead'
			   AND Name = ?
			 LIMIT 1`, umbrellaRuleName,
		).Scan(&active, &ownerPack)
		if err == sql.ErrNoRows {
			t.Errorf("umbrella validation rule Lead/%s not found after hash-drift reapply", umbrellaRuleName)
		} else if err != nil {
			t.Fatalf("query umbrella validation rule Lead/%s: %v", umbrellaRuleName, err)
		} else {
			if !active.Valid || active.Int64 != 1 {
				t.Errorf("umbrella validation rule Lead/%s Active = %v (valid=%v), want 1", umbrellaRuleName, active.Int64, active.Valid)
			}
			if !ownerPack.Valid || ownerPack.String != "salesforce-crm-standard" {
				t.Errorf("umbrella validation rule Lead/%s OwnerPack = %q, want \"salesforce-crm-standard\"", umbrellaRuleName, ownerPack.String)
			}
		}
	}

	// Assert (d): AllowViewEditConvertedLeads permission lands after hash-drift reapply.
	// Without this assertion a regression where the reapply path fails to
	// install the permission would not trip this test.
	{
		const permApiName = "AllowViewEditConvertedLeads"
		var isActive sql.NullInt64
		var ownerPack sql.NullString
		err := conn.DB().QueryRowContext(ctx, `
			SELECT IsActive, OwnerPack
			  FROM _System_Permission
			 WHERE ApiName = ?
			 LIMIT 1`, permApiName,
		).Scan(&isActive, &ownerPack)
		if err == sql.ErrNoRows {
			t.Errorf("permission %s not found after hash-drift reapply", permApiName)
		} else if err != nil {
			t.Fatalf("query permission %s: %v", permApiName, err)
		} else {
			if !isActive.Valid || isActive.Int64 != 1 {
				t.Errorf("permission %s IsActive = %v (valid=%v), want 1", permApiName, isActive.Int64, isActive.Valid)
			}
			if !ownerPack.Valid || ownerPack.String != "salesforce-crm-standard" {
				t.Errorf("permission %s OwnerPack = %q, want \"salesforce-crm-standard\"", permApiName, ownerPack.String)
			}
		}
	}
}
