package controlplane

import (
	"context"
	"database/sql"
	"os"
	"path/filepath"
	"strings"
	"testing"

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

func TestReapplyUsesRunnerRegistrationsWithoutActivatingFreshInstall(t *testing.T) {
	rawSource, runnerOptions := runnerManagedVerifyFixture(t)
	root := strings.TrimPrefix(rawSource, "local_path:")
	registrations := runnerOptions.ComponentKindRegistrationSet
	rawDB, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer rawDB.Close()

	row := installedPackRow{
		Name: "runner-managed-test", Category: string(packs.AppPack), Status: installedPackStatusActive,
		ManifestFormatVersion: sql.NullString{String: packs.SupportedManifestFormatVersion, Valid: true},
		SourceKind:            sql.NullString{String: string(packs.PackSourceKindLocalPath), Valid: true},
		SourceURI:             sql.NullString{String: root, Valid: true},
	}
	pack, err := sourceBackedPackFromInstalledRowWithComponentKindRegistrationSet(context.Background(), nil, row, registrations)
	if err != nil {
		t.Fatal(err)
	}
	hash, err := packs.PackPayloadHash(pack)
	if err != nil {
		t.Fatal(err)
	}
	meta := packs.PackHashMetadataForPack(pack)
	row.AppliedPayloadHash = sql.NullString{String: hash, Valid: true}
	row.DesiredPayloadHash = row.AppliedPayloadHash
	row.PayloadHashAlgorithm = sql.NullString{String: meta.PayloadHashAlgorithm, Valid: true}
	row.ManifestFormatVersion = sql.NullString{String: meta.ManifestFormatVersion, Valid: true}
	row.PackVersion = sql.NullString{String: meta.PackVersion, Valid: true}
	row.PlatformCompatibility = sql.NullString{String: meta.PlatformCompatibility, Valid: true}
	expectReapplyInstalledPackSourceRow(mock, row)

	originalEnsure := ensureInstallPackSchemaReadinessForInstall
	originalDependencies := validateLivePackDependenciesForInstall
	originalOwnerless := samePackAllowedOwnerlessObjectsForReapply
	originalApply := applyResolvedSourceBackedPackForInstall
	originalList := listInstalledAppPacksForReconcile
	t.Cleanup(func() {
		ensureInstallPackSchemaReadinessForInstall = originalEnsure
		validateLivePackDependenciesForInstall = originalDependencies
		samePackAllowedOwnerlessObjectsForReapply = originalOwnerless
		applyResolvedSourceBackedPackForInstall = originalApply
		listInstalledAppPacksForReconcile = originalList
	})
	ensureInstallPackSchemaReadinessForInstall = func(context.Context, *Runner) error { return nil }
	validateLivePackDependenciesForInstall = func(context.Context, *sql.DB, *packs.SourceBackedPack) error { return nil }
	listInstalledAppPacksForReconcile = func(context.Context, *sql.DB) ([]installedPackRow, error) {
		return []installedPackRow{row}, nil
	}
	samePackAllowedOwnerlessObjectsForReapply = func(_ context.Context, _ *sql.DB, _ *packs.SourceBackedPack, _ string, _ InstallPackOptions, got packs.ComponentKindRegistrationSet) ([]string, error) {
		if len(got.Registrations()) != 1 {
			t.Fatalf("reapply registrations = %#v", got.Registrations())
		}
		return []string{"Quote"}, nil
	}
	applyCalls := 0
	applyResolvedSourceBackedPackForInstall = func(_ context.Context, _ *Runner, got *packs.SourceBackedPack, _ *installPackPreflightOverrides, _ InstallPackOptions) error {
		if got == nil || got.Name() != row.Name {
			t.Fatalf("applied pack = %#v", got)
		}
		applyCalls++
		return nil
	}

	runner := &Runner{
		DB: database.NewConnectionFromDB(rawDB),
		SvcMgr: &runnerServiceGraph{
			ServiceGraph: newTestServiceGraph(testServiceGraphConfig{}),
			options:      runnerOptions,
		},
	}
	if err := reapplyPackFromSourceUnlocked(context.Background(), runner, rawSource, InstallPackOptions{}); err != nil {
		t.Fatal(err)
	}
	if applyCalls != 1 {
		t.Fatalf("reapply calls = %d, want 1", applyCalls)
	}
	if err := installPackFromSourceUnlockedWithOptions(context.Background(), runner, rawSource, InstallPackOptions{}); err == nil || !strings.Contains(err.Error(), "runner_managed_test") {
		t.Fatalf("fresh install error = %v, want unsupported runner_managed_test", err)
	}
	if applyCalls != 1 {
		t.Fatalf("fresh install reached apply; calls = %d, want 1", applyCalls)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func TestReapplyRegistrationSetPreservesLegacyFormulaProviderBridge(t *testing.T) {
	rawSource, runnerOptions := runnerManagedFormulaProviderFixture(t)
	root := strings.TrimPrefix(rawSource, "local_path:")
	registrations := runnerOptions.ComponentKindRegistrationSet
	providerReport := packs.ValidateInstallablePackSourceWithComponentKindRegistrationSet(
		rawSource, packs.DefaultPackSourceOptions(), registrations,
	)
	if providerReport.HasErrors() || providerReport.Pack == nil {
		t.Fatalf("provider source diagnostics: %s", providerReport.Error())
	}
	provider := providerReport.Pack
	providerHash, err := packs.PackPayloadHash(provider)
	if err != nil {
		t.Fatal(err)
	}
	providerMeta := packs.PackHashMetadataForPack(provider)

	rawDB, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer rawDB.Close()
	mock.ExpectQuery("SELECT COLUMN_NAME\\s+FROM INFORMATION_SCHEMA\\.COLUMNS").
		WithArgs("_System_InstalledPack", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields").
		WillReturnRows(sqlmock.NewRows([]string{"COLUMN_NAME"}))
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).AddRow(
			provider.Name(), string(provider.Category()), providerHash, providerMeta.ManifestFormatVersion,
			string(packs.PackSourceKindLocalPath), root, nil,
		))

	consumerRoot := writeFormulaFunctionConsumerPackSourceWithDependencyFunction(
		t, "runner-formula-consumer", provider.Name(), "RUNNER_PREFIX",
	)
	report, _, err := validateInstallablePackSourceForInstallWithPreflightOverridesAndOptionsAndComponentKindRegistrationSet(
		context.Background(), rawDB, "local_path:"+consumerRoot, nil, packs.DefaultPackSourceOptions(), registrations,
	)
	if err != nil {
		t.Fatal(err)
	}
	if report.HasErrors() || report.Pack == nil {
		t.Fatalf("consumer source diagnostics: %s", report.Error())
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func runnerManagedFormulaProviderFixture(t *testing.T) (string, RunnerOptions) {
	t.Helper()
	rawSource, options := runnerManagedVerifyFixture(t)
	manifestPath := filepath.Join(strings.TrimPrefix(rawSource, "local_path:"), "pack.json")
	rawManifest, err := os.ReadFile(manifestPath)
	if err != nil {
		t.Fatal(err)
	}
	formulaComponent := `,{"kind":"formula_function_bundle","businessKey":"runner-functions","manageability":"locked","payload":{"functions":[{"name":"RUNNER_PREFIX","minArgs":1,"maxArgs":1,"return":{"kind":"fixed","type":"string"},"args":[{"type":"string","nullable":true}],"evaluator":{"kind":"formula_expr_v1","params":["value"],"body":"value + \"-runner\""}}]}}]}`
	manifest := strings.TrimSuffix(string(rawManifest), `]}`) + formulaComponent
	if err := os.WriteFile(manifestPath, []byte(manifest), 0o644); err != nil {
		t.Fatal(err)
	}
	return rawSource, options
}
