package control

import (
	"testing"
	"time"
)

func TestLivePredictedCloseStopsAtUnsyncedOrder(t *testing.T) {
	now := time.Date(2026, 9, 2, 4, 0, 0, 0, time.UTC)
	facts := completePredictedFacts(now)
	observation := SynthesizeCloseObservation("2026-08", "predicted", facts)

	for _, key := range []string{"ar_table_ready", "sync_ar_to_bigquery", "verify_billing_inputs", "run_monthly_cloud_billing", "run_cloud_breakdown"} {
		if observation.Close.Steps[key].Verification == VerificationPassed {
			t.Fatalf("%s must not turn unbound counts into acceptance", key)
		}
	}
	order := observation.Close.Steps["sync_order_to_crm"]
	if order.Verification == VerificationPassed {
		t.Fatal("unsynced orders must not be reported as complete")
	}
	if observation.NextGate != "ar_table_ready" {
		t.Fatalf("next gate = %s, want first unbound input", observation.NextGate)
	}
	if order.Action.Enabled || observation.WriteActionsEnabled {
		t.Fatal("observe-only snapshots must never enable mutations")
	}
	if observation.Close.Steps["refresh_crm_order_bundle"].Verification == VerificationPassed {
		t.Fatal("a healthy generic CRM run is not a current-close receipt before Order sync")
	}
}

func TestLiveActualCloseBlocksWhenARHasNoActualRows(t *testing.T) {
	now := time.Date(2026, 9, 2, 4, 0, 0, 0, time.UTC)
	facts := completePredictedFacts(now)
	facts.MonthlyOutput = MonthlyOutputFact{}
	observation := SynthesizeCloseObservation("2026-08", "actual", facts)

	input := observation.Close.Steps["verify_billing_inputs"]
	if input.Verification != VerificationFailed {
		t.Fatalf("actual input verification = %s, want failed", input.Verification)
	}
	if observation.Close.Status != "blocked" {
		t.Fatalf("close status = %s, want blocked", observation.Close.Status)
	}
	if observation.NextGate != "ar_table_ready" {
		t.Fatalf("next gate = %s, want first unbound input", observation.NextGate)
	}
}

func TestFinalizeUpdatesDoNotRequireNewCRMIdentity(t *testing.T) {
	now := time.Date(2026, 9, 2, 4, 0, 0, 0, time.UTC)
	facts := completePredictedFacts(now)
	facts.Finance.Order = FinanceTableFact{Total: 2, Synced: 2, RequiredIdentityRows: 2, IdentityRows: 2, LastSynced: &now}
	facts.Finance.RevRec = FinanceTableFact{Total: 3, Synced: 3, IdentityRows: 0, EstimatedRows: 3, LastSynced: &now}
	facts.Finance.Collection = FinanceTableFact{Total: 4, Synced: 4, IdentityRows: 0, LastSynced: &now}
	observation := SynthesizeCloseObservation("2026-08", "predicted", facts)

	if observation.Close.Steps["finalize_revrec"].Execution != ExecutionSucceeded || observation.Close.Steps["finalize_revrec"].Verification == VerificationPassed {
		t.Fatal("RevRec local completion is observed but is not independent close acceptance")
	}
	if observation.Close.Steps["finalize_collection"].Execution != ExecutionSucceeded || observation.Close.Steps["finalize_collection"].Verification == VerificationPassed {
		t.Fatal("Collection local completion is observed but is not independent close acceptance")
	}
	if observation.Close.Steps["verify_close_complete"].Verification == VerificationPassed {
		t.Fatal("Finance table completion must not impersonate missing NetSuite acceptance")
	}
	if observation.Close.Status != "blocked" || observation.NextGate != "ar_table_ready" {
		t.Fatalf("close should wait at NetSuite verification, status=%s next=%s", observation.Close.Status, observation.NextGate)
	}
}

func completePredictedFacts(now time.Time) LiveFacts {
	return LiveFacts{
		ObservedAt:   now,
		Fivetran:     FivetranFact{ConnectorID: "connector", SyncState: "scheduled", SucceededAt: &now},
		BillingInput: BillingInputFact{TotalRows: 512, PredictedRows: 512, ActualRows: 0, LastSyncedAt: &now},
		MonthlyOutput: MonthlyOutputFact{
			Orders:      TableFact{Name: "project.dataset.orders", Rows: 513, LastModified: &now},
			Collections: TableFact{Name: "project.dataset.collections", Rows: 509, LastModified: &now},
		},
		Finance: FinanceCloseFact{
			BreakdownSucceededAt: &now,
			BreakdownReceiptID:   22560002,
			Order:                FinanceTableFact{Total: 482, Synced: 270, Unsynced: 212, LastUpdated: &now},
			RevRec:               FinanceTableFact{Total: 513, Synced: 270, Unsynced: 243, EstimatedRows: 513, LastUpdated: &now},
			Collection:           FinanceTableFact{Total: 53, Synced: 3, Unsynced: 50, LastUpdated: &now},
		},
		CRM: CRMPipelineFact{
			Order:     CRMTableFact{RunID: "crm-run", Status: "loaded", Rows: 5723, ObservedAt: &now},
			OrderItem: CRMTableFact{RunID: "crm-run", Status: "loaded", Rows: 5797, ObservedAt: &now},
			Product2:  CRMTableFact{RunID: "crm-run", Status: "loaded", Rows: 72, ObservedAt: &now},
		},
		Salesforce: SalesforceOrderFact{RunID: "finance-run", Status: "success", SourceRows: 5571, TargetRows: 5571, FinishedAt: &now},
	}
}
