package packs

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestManagedLifecycleInstallPlanPreflightsItemAndSetBeforeAnyMutation(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	base := testComponentKindRegistration(cp.ComponentKindApprovalProcess, "ProcessDefinition")
	base.ManagedProvider = nil
	lifecycle := testComponentManagedLifecycleRegistration(t, cp.ComponentKindApprovalProcess, "ProcessDefinition")
	provider := lifecycle.Lifecycle()
	provider.Plan = func(_ context.Context, scope cp.ComponentManagedScope, request cp.ComponentManagedLifecycleRequest, _ cp.ComponentManagedReadStore) (cp.ComponentManagedPlan, error) {
		if scope == cp.ComponentManagedScopeSet {
			return cp.ComponentManagedPlan{}, fmt.Errorf("set collision")
		}
		return testManagedLifecyclePlan(t, request.Item.ExpectedProjection), nil
	}
	lifecycle, err = cp.NewComponentManagedLifecycleRegistration(cp.ComponentManagedLifecycleRegistrationSpec{
		Kind: lifecycle.Kind(), ReadSet: lifecycle.ReadSet(),
		Capabilities: append(lifecycle.Capabilities(), cp.ComponentManagedCapabilityExactSet), EffectClasses: lifecycle.EffectClasses(),
		TransactionMode: lifecycle.TransactionMode(), Budget: lifecycle.Budget(), Lifecycle: provider,
	})
	if err != nil {
		t.Fatal(err)
	}
	payload := []byte(`{"name":"one"}`)
	item := newSourceBackedExternalManagedComponent("test-pack", ManifestComponent{
		Kind: ComponentKindApprovalProcess, BusinessKey: "One", Manageability: ManageabilityLocked, Payload: payload,
	}, payload, cp.ComponentBuildManagedComponent{ExpectedProjection: []byte(`{"name":"one"}`)}, base, lifecycle)
	set, err := newSourceBackedExternalManagedSetComponent("test-pack", base, []string{"One"}, lifecycle)
	if err != nil {
		t.Fatal(err)
	}
	var applied []string
	plan, err := NewInstallPlan([]PackComponent{
		&fakePlanComponent{kind: ComponentKindObjectSpec, key: "Earlier", manageability: ManageabilityLocked, applied: &applied},
		item,
		set,
	})
	if err != nil {
		t.Fatal(err)
	}
	err = plan.Apply(context.Background(), ApplyDeps{DB: db}, InstallPlanHooks{})
	if err == nil || !strings.Contains(err.Error(), "set collision") {
		t.Fatalf("Apply() error = %v", err)
	}
	if len(applied) != 0 {
		t.Fatalf("components mutated before complete preflight: %v", applied)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("preflight executed mutation SQL: %v", err)
	}
}
