package controlplane

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

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/db"
	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

func TestResolveApplyAllSourceRevisionAdvanceReplayTupleUsesOriginalFromRevisionForRetry(t *testing.T) {
	advance := testApplyAllSourceRevisionAdvance("retry-replay", 4)
	currentTarget := testApplyAllSourceRevisionTarget(
		t, advance.Name, "2.0.0", advance.Kind, advance.URI, advance.ToRevision,
	)
	replayBase := testApplyAllSourceRevisionTarget(
		t, advance.Name, "1.0.0", advance.Kind, advance.URI, advance.FromRevision,
	)
	precondition := testApplyAllSourceRevisionAdvanceFrozenCurrentTargetProof(
		t, advance, currentTarget, installedPackStatusFailed, advance.Kind, nil, testCurrentTargetV1Hash,
	)

	rawDB, _, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	t.Cleanup(func() { _ = rawDB.Close() })
	runner := &Runner{DB: database.NewConnectionFromDB(rawDB)}
	activeRuntime := testApplyAllSourceRevisionAdvanceActiveRuntimeObservationForDatabase(rawDB)

	originalValidator := validateApplyAllSourceRevisionSource
	originalResolver := resolveApplyAllSourceRevisionInstalledTarget
	originalFormulaResolver := resolveApplyAllSourceRevisionFormulaContext
	t.Cleanup(func() {
		validateApplyAllSourceRevisionSource = originalValidator
		resolveApplyAllSourceRevisionInstalledTarget = originalResolver
		resolveApplyAllSourceRevisionFormulaContext = originalFormulaResolver
	})
	validateApplyAllSourceRevisionSource = func(
		_ string,
		opts packs.PackSourceOptions,
		_ packs.ComponentKindRegistrationSet,
	) packs.PackValidationReport {
		if got := opts.OfficialRepoSourceRevisions[advance.URI]; got != advance.FromRevision {
			t.Fatalf("replay source revision = %q, want original FromRevision %q", got, advance.FromRevision)
		}
		return packs.PackValidationReport{
			Source: testApplyAllSourceRevisionSource(advance.Name, "1.0.0", advance.FromRevision),
		}
	}
	resolveApplyAllSourceRevisionFormulaContext = func(
		context.Context,
		*packs.ActiveInstalledRuntimeSnapshot,
		[]packs.Pack,
		packs.ComponentKindRegistrationSet,
	) ([]formula.FunctionBundle, error) {
		return nil, nil
	}
	resolveApplyAllSourceRevisionInstalledTarget = func(
		_ context.Context,
		gotDB *sql.DB,
		row installedPackRow,
		revision string,
		_ packs.PackSourceOptions,
		_ []formula.FunctionBundle,
		_ bool,
		_ packs.ComponentKindRegistrationSet,
		_ ...packs.SourceBackedPackOption,
	) (*packs.SourceBackedPack, error) {
		if gotDB != rawDB || row.SourceRevision.String != advance.ToRevision || revision != advance.FromRevision {
			t.Fatalf(
				"replay resolver DB/installed/replay revision = %p/%q/%q, want %p/%q/%q",
				gotDB, row.SourceRevision.String, revision, rawDB, advance.ToRevision, advance.FromRevision,
			)
		}
		return replayBase, nil
	}

	replay, err := resolveApplyAllSourceRevisionAdvanceReplayTuple(
		context.Background(), runner, precondition.snapshot, activeRuntime, precondition.current,
	)
	if err != nil {
		t.Fatalf("resolve retry replay tuple: %v", err)
	}
	if len(replay.plan) != 1 || replay.targets[advance.Name] != replayBase {
		t.Fatalf("retry replay tuple = %#v, want original FromRevision target", replay)
	}
}

func TestValidateApplyAllSourceRevisionAdvanceReplayTupleRejectsRetryAtTarget(t *testing.T) {
	advance := testApplyAllSourceRevisionAdvance("retry-target-noop", 7)
	target := testApplyAllSourceRevisionTarget(
		t, advance.Name, "2.0.0", advance.Kind, advance.URI, advance.ToRevision,
	)
	precondition := testApplyAllSourceRevisionAdvanceFrozenCurrentTargetProof(
		t, advance, target, installedPackStatusFailed, advance.Kind, nil, testCurrentTargetV1Hash,
	)
	precondition.replay = cloneApplyAllSourceRevisionAdvanceTuple(precondition.current)

	err := validateApplyAllSourceRevisionAdvanceReplayTuple(precondition)
	assertApplyAllSourceRevisionAdvancePreconditionError(
		t, err, ApplyAllSourceRevisionAdvancePlanState, advance.index, advance.Name,
	)
}
