package controlplane

import (
	"context"
	"database/sql"
	"reflect"
	"strings"
	"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 TestResolveApplyAllSourceRevisionAdvanceCurrentInstalledTupleUsesFrozenMixedEndpointContext(t *testing.T) {
	const (
		providerName = "current-provider"
		consumerName = "current-consumer"
	)
	provider := testApplyAllSourceRevisionAdvance(providerName, 1)
	consumer := testApplyAllSourceRevisionAdvance(consumerName, 0)
	consumerRow := installedPackRow{
		Name: consumerName, Category: string(packs.AppPack), Status: installedPackStatusActive,
		SourceKind:     sql.NullString{Valid: true, String: string(packs.PackSourceKindOfficialRepo)},
		SourceURI:      sql.NullString{Valid: true, String: consumerName},
		SourceRevision: sql.NullString{Valid: true, String: consumer.FromRevision},
	}
	providerRow := installedPackRow{
		Name: providerName, Category: string(packs.AppPack), Status: installedPackStatusFailed, IsDeleted: 1,
		SourceKind:     sql.NullString{Valid: true, String: string(packs.PackSourceKindOfficialRepo)},
		SourceURI:      sql.NullString{Valid: true, String: providerName},
		SourceRevision: sql.NullString{Valid: true, String: provider.ToRevision},
	}
	snapshot := applyAllSourceRevisionAdvanceCurrentSnapshot{
		entries: []applyAllSourceRevisionAdvanceCurrentSnapshotEntry{
			{advance: consumer, row: consumerRow},
			{advance: provider, row: providerRow},
		},
	}
	wantRevisions := map[string]string{
		consumerName: consumer.FromRevision,
		providerName: provider.ToRevision,
	}
	sources := map[string]*packs.ResolvedPackSource{
		consumerName: testApplyAllSourceRevisionSource(
			consumerName, "1.0.0", consumer.FromRevision,
			packs.PackDependency{Name: providerName, VersionConstraint: ">=2.0.0"},
		),
		providerName: testApplyAllSourceRevisionSource(providerName, "2.1.0", provider.ToRevision),
	}
	finalTargets := map[string]*packs.SourceBackedPack{
		consumerName: testApplyAllSourceRevisionTarget(
			t, consumerName, "1.0.0", "official_repo", consumerName, consumer.FromRevision,
			packs.PackDependency{Name: providerName, VersionConstraint: ">=2.0.0"},
		),
		providerName: testApplyAllSourceRevisionTarget(
			t, providerName, "2.1.0", "official_repo", providerName, provider.ToRevision,
		),
	}

	originalValidator := validateApplyAllSourceRevisionSource
	originalResolver := resolveApplyAllSourceRevisionInstalledTarget
	originalFormulaResolver := resolveApplyAllSourceRevisionFormulaContext
	t.Cleanup(func() {
		validateApplyAllSourceRevisionSource = originalValidator
		resolveApplyAllSourceRevisionInstalledTarget = originalResolver
		resolveApplyAllSourceRevisionFormulaContext = originalFormulaResolver
	})
	sourceCalls := 0
	validateApplyAllSourceRevisionSource = func(
		raw string,
		opts packs.PackSourceOptions,
		registrations packs.ComponentKindRegistrationSet,
	) packs.PackValidationReport {
		sourceCalls++
		if !reflect.DeepEqual(opts.OfficialRepoSourceRevisions, wantRevisions) {
			t.Fatalf("source call %d revisions = %#v, want complete mixed map", sourceCalls, opts.OfficialRepoSourceRevisions)
		}
		assertApplyAllSourceRevisionRunnerTestRegistration(t, registrations)
		return packs.PackValidationReport{Source: sources[strings.TrimPrefix(raw, "official_repo:")]}
	}
	bundles := []formula.FunctionBundle{{Functions: []formula.FunctionDefinition{{Name: "current-tuple-sentinel"}}}}
	formulaCalls := 0
	resolveApplyAllSourceRevisionFormulaContext = func(
		_ context.Context,
		snapshot *packs.ActiveInstalledRuntimeSnapshot,
		installing []packs.Pack,
		registrations packs.ComponentKindRegistrationSet,
	) ([]formula.FunctionBundle, error) {
		formulaCalls++
		assertApplyAllSourceRevisionRunnerTestRegistration(t, registrations)
		if snapshot == nil {
			t.Fatal("formula context snapshot = nil, want frozen active-runtime snapshot")
		}
		if sourceCalls != 2 {
			t.Fatalf("formula context assembled after %d source calls, want 2", sourceCalls)
		}
		gotNames := make([]string, 0, len(installing))
		for _, pack := range installing {
			gotNames = append(gotNames, pack.Name())
		}
		if want := []string{consumerName, providerName}; !reflect.DeepEqual(gotNames, want) {
			t.Fatalf("formula installing order = %v, want frozen plan order %v", gotNames, want)
		}
		return bundles, nil
	}
	wantRows := map[string]installedPackRow{consumerName: consumerRow, providerName: providerRow}
	resolverCalls := 0
	resolveApplyAllSourceRevisionInstalledTarget = func(
		_ context.Context,
		_ *sql.DB,
		row installedPackRow,
		revision string,
		sourceOptions packs.PackSourceOptions,
		gotBundles []formula.FunctionBundle,
		hasBundles bool,
		registrations packs.ComponentKindRegistrationSet,
		_ ...packs.SourceBackedPackOption,
	) (*packs.SourceBackedPack, error) {
		resolverCalls++
		assertApplyAllSourceRevisionRunnerTestRegistration(t, registrations)
		if row != wantRows[row.Name] || revision != wantRevisions[row.Name] {
			t.Fatalf("resolver %s frozen row/revision = %#v/%q", row.Name, row, revision)
		}
		if !reflect.DeepEqual(sourceOptions.OfficialRepoSourceRevisions, wantRevisions) ||
			!hasBundles || !reflect.DeepEqual(gotBundles, bundles) {
			t.Fatalf("resolver %s options/bundles = %#v/%#v/%t", row.Name, sourceOptions.OfficialRepoSourceRevisions, gotBundles, hasBundles)
		}
		return finalTargets[row.Name], nil
	}

	rawDB, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	t.Cleanup(func() { _ = rawDB.Close() })
	registrations := runnerTestRegistrationSet(t)
	runner := &Runner{
		DB: database.NewConnectionFromDB(rawDB),
		SvcMgr: &runnerServiceGraph{options: RunnerOptions{
			ComponentKindRegistrationSet: registrations,
		}},
	}
	expectApplyAllSourceRevisionAdvanceActiveRuntimeObservation(mock)
	activeRuntime, err := readApplyAllSourceRevisionAdvanceActiveRuntimeObservation(context.Background(), rawDB)
	if err != nil {
		t.Fatalf("read active runtime observation: %v", err)
	}
	tuple, err := resolveApplyAllSourceRevisionAdvanceCurrentInstalledTuple(context.Background(), runner, snapshot, activeRuntime)
	if err != nil {
		t.Fatalf("resolve current installed tuple: %v", err)
	}
	if sourceCalls != 2 || formulaCalls != 1 || resolverCalls != 2 {
		t.Fatalf("source/formula/resolver calls = %d/%d/%d, want 2/1/2", sourceCalls, formulaCalls, resolverCalls)
	}
	if got := []string{tuple.plan[0].Name, tuple.plan[1].Name}; !reflect.DeepEqual(got, []string{consumerName, providerName}) {
		t.Fatalf("mixed installed tuple order = %v, want frozen plan order", got)
	}
	if tuple.targets[consumerName] != finalTargets[consumerName] || tuple.targets[providerName] != finalTargets[providerName] {
		t.Fatalf("mixed installed targets = %#v, want compatibility targets", tuple.targets)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unexpected planned-row query: %v", err)
	}
}
