package control

import (
	"context"
	"path/filepath"
	"testing"
	"time"
)

type immediateRunner struct{}

func (immediateRunner) Run(_ context.Context, closeRun *CloseRun, definition StepDefinition, attempt *Attempt) (RunnerResult, error) {
	return RunnerResult{Verification: VerificationPassed, ExternalRunID: "test-run", Receipt: map[string]any{
		"bill_month": closeRun.BillMonth,
		"scope":      definition.Scope,
		"attempt":    attempt.AttemptNo,
	}}, nil
}

func (immediateRunner) Reconcile(ctx context.Context, closeRun *CloseRun, definition StepDefinition, attempt *Attempt) (RunnerResult, error) {
	return immediateRunner{}.Run(ctx, closeRun, definition, attempt)
}

func TestMonthlyClosePolicyAndDurableAttempt(t *testing.T) {
	path := filepath.Join(t.TempDir(), "control.json")
	store, err := NewStoreForMode(path, "demo")
	if err != nil {
		t.Fatal(err)
	}
	closes := store.ListCloses()
	if len(closes) != 1 {
		t.Fatalf("expected one seeded close, got %d", len(closes))
	}
	closeRun := closes[0]
	monthly := closeRun.Steps["run_monthly_cloud_billing"]
	if monthly.Action.Type != "run" || !monthly.Action.Enabled {
		t.Fatalf("expected monthly billing to be runnable, got %+v", monthly.Action)
	}
	if closeRun.Steps["run_cloud_breakdown"].Action.Enabled {
		t.Fatal("cloud breakdown must wait for monthly billing verification")
	}

	engine := NewEngine(store, immediateRunner{})
	attempt, err := engine.Execute(closeRun.ID, monthly.Key, ActionRequest{Action: "run", Reason: "test first migrated step"})
	if err != nil {
		t.Fatal(err)
	}
	deadline := time.Now().Add(2 * time.Second)
	for time.Now().Before(deadline) {
		current, _ := store.GetClose(closeRun.ID)
		if current.Steps[monthly.Key].Verification == VerificationPassed {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}
	current, _ := store.GetClose(closeRun.ID)
	if current.Steps[monthly.Key].Verification != VerificationPassed {
		t.Fatalf("attempt %s did not pass", attempt.ID)
	}
	if !current.Steps["run_cloud_breakdown"].Action.Enabled {
		t.Fatal("cloud breakdown should become runnable after monthly billing passes")
	}

	reloaded, err := NewStoreForMode(path, "demo")
	if err != nil {
		t.Fatal(err)
	}
	if len(reloaded.Attempts(closeRun.ID)) != 1 {
		t.Fatal("attempt did not survive store reload")
	}
}

func TestCRMOrderBundleIsExact(t *testing.T) {
	definition, ok := definitionByKey("refresh_crm_order_bundle")
	if !ok {
		t.Fatal("CRM order bundle step is missing")
	}
	if len(definition.Scope) != 2 || definition.Scope[0] != "nexuscrm_order" || definition.Scope[1] != "nexuscrm_orderitem" {
		t.Fatalf("unexpected CRM scope: %v", definition.Scope)
	}
	if definition.MigratedRuntime {
		t.Fatal("data-pipeline step must not pretend to be a migrated netforce runtime")
	}
}

func TestRejectsActionThatDoesNotMatchState(t *testing.T) {
	store, err := NewStoreForMode(filepath.Join(t.TempDir(), "control.json"), "demo")
	if err != nil {
		t.Fatal(err)
	}
	closeRun := store.ListCloses()[0]
	engine := NewEngine(store, immediateRunner{})
	if _, err := engine.Execute(closeRun.ID, "run_monthly_cloud_billing", ActionRequest{Action: "retry", Reason: "wrong action test"}); err == nil {
		t.Fatal("expected mismatched action to be rejected")
	}
}
