package packs

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

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

func TestActivePackBackfillsCarryComponentKindRegistrationSet(t *testing.T) {
	root := writePackFixture(t, "jira-like-sample", validPackManifestV2(`[
    {
      "kind": "approval_process",
      "businessKey": "Quote/Quote_Approval",
      "manageability": "locked",
      "path": "approvalProcesses/Quote_Approval.approvalProcess-meta.xml"
    }
  ]`, `"dependencies": [],`))
	writeFile(t, root, "approvalProcesses/Quote_Approval.approvalProcess-meta.xml", "<ApprovalProcess/>")
	registration := testComponentKindRegistration(cp.ComponentKindApprovalProcess, "ProcessDefinition")
	registration.BuildHandler = func(buildContext cp.ComponentBuildContext, _ cp.ComponentBuildRequest) error {
		return buildContext.AddManagedComponent(cp.ComponentBuildManagedComponent{ExpectedProjection: []byte(`{"rows":[]}`)})
	}
	registrations, err := NewComponentKindRegistrationSet([]cp.ComponentKindRegistration{registration})
	if err != nil {
		t.Fatal(err)
	}

	backfills := []struct {
		name       string
		legacy     func(context.Context, *sql.DB) (int, error)
		registered func(context.Context, *sql.DB, ComponentKindRegistrationSet) (int, error)
	}{
		{"child clone", BackfillObjectSpecChildCloneRulePackOwnershipFromActivePacks, BackfillObjectSpecChildCloneRulePackOwnershipFromActivePacksWithComponentKindRegistrationSet},
		{"exclusive sibling", BackfillObjectSpecExclusiveSiblingFieldPackOwnershipFromActivePacks, BackfillObjectSpecExclusiveSiblingFieldPackOwnershipFromActivePacksWithComponentKindRegistrationSet},
		{"page layout", BackfillLegacySourceBackedCuratedDefaultLayoutIDsFromActivePacks, BackfillLegacySourceBackedCuratedDefaultLayoutIDsFromActivePacksWithComponentKindRegistrationSet},
		{"custom setting", BackfillObjectMetadataCustomSettingFromActivePacks, BackfillObjectMetadataCustomSettingFromActivePacksWithComponentKindRegistrationSet},
	}
	for _, backfill := range backfills {
		for _, registered := range []bool{true, false} {
			name := "registered"
			if !registered {
				name = "legacy"
			}
			t.Run(backfill.name+"/"+name, func(t *testing.T) {
				db, mock, err := sqlmock.New()
				if err != nil {
					t.Fatal(err)
				}
				defer db.Close()
				mock.ExpectQuery("SELECT Name, Category, ManifestFormatVersion, SourceKind, SourceURI\\s+FROM _System_InstalledPack").
					WithArgs(string(AppPack)).
					WillReturnRows(sqlmock.NewRows([]string{"Name", "Category", "ManifestFormatVersion", "SourceKind", "SourceURI"}).
						AddRow("jira-like-sample", string(AppPack), ManifestFormatVersion2, string(PackSourceKindLocalPath), root))

				var updated int
				if registered {
					updated, err = backfill.registered(context.Background(), db, registrations)
				} else {
					updated, err = backfill.legacy(context.Background(), db)
				}
				if registered {
					if err != nil || updated != 0 {
						t.Fatalf("registered backfill = (%d, %v), want (0, nil)", updated, err)
					}
				} else if err == nil || !strings.Contains(err.Error(), "approval_process") {
					t.Fatalf("legacy backfill error = %v, want missing approval_process registration", err)
				}
				if err := mock.ExpectationsWereMet(); err != nil {
					t.Fatal(err)
				}
			})
		}
	}
}

func TestActivePackBackfillsDeferCandidateApplyPacksBeforeStoredLocatorResolution(t *testing.T) {
	options := ActivePackBackfillOptions{
		DeferredPackNames: map[string]struct{}{"jira-like-sample": {}},
	}
	backfills := []struct {
		name string
		run  func(context.Context, *sql.DB, ActivePackBackfillOptions) (int, error)
	}{
		{"child clone", BackfillObjectSpecChildCloneRulePackOwnershipFromActivePacksWithOptions},
		{"exclusive sibling", BackfillObjectSpecExclusiveSiblingFieldPackOwnershipFromActivePacksWithOptions},
		{"page layout", BackfillLegacySourceBackedCuratedDefaultLayoutIDsFromActivePacksWithOptions},
	}
	for _, backfill := range backfills {
		t.Run(backfill.name, func(t *testing.T) {
			db, mock, err := sqlmock.New()
			if err != nil {
				t.Fatal(err)
			}
			defer db.Close()
			mock.ExpectQuery("SELECT Name, Category, ManifestFormatVersion, SourceKind, SourceURI\\s+FROM _System_InstalledPack").
				WithArgs(string(AppPack)).
				WillReturnRows(sqlmock.NewRows([]string{"Name", "Category", "ManifestFormatVersion", "SourceKind", "SourceURI"}).
					AddRow("jira-like-sample", string(AppPack), ManifestFormatVersion2, string(PackSourceKindLocalPath), "/removed/staging-release/PingCAP-HubSpot-Companion"))

			updated, err := backfill.run(context.Background(), db, options)
			if err != nil || updated != 0 {
				t.Fatalf("deferred backfill = (%d, %v), want (0, nil)", updated, err)
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatal(err)
			}
		})
	}
}
