package packs

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

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestComponentRegistrationSetPropagatesIntoOfficialRepoDependencyValidation(t *testing.T) {
	opts := testPackSourceOptions(t)
	managedKind := cp.ComponentKind("managed_dependency_test")
	dependencyRoot := filepath.Join(opts.OfficialRepoRoot, "managed-dependency")
	dependencyManifest := strings.Replace(validPackManifest(`[
    {
      "kind": "managed_dependency_test",
      "businessKey": "managed",
      "manageability": "locked",
      "payload": {}
    }
  ]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "managed-dependency"`, 1)
	writeManifest(t, dependencyRoot, dependencyManifest)

	root := writePackFixture(t, "dependent-pack", validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`
	]`, `"dependencies": [{"name": "managed-dependency"}],`))
	set, err := NewComponentKindRegistrationSet([]cp.ComponentKindRegistration{
		testComponentKindRegistration(managedKind, "ManagedDependency"),
	})
	if err != nil {
		t.Fatal(err)
	}

	withoutRegistration := ValidateInstallablePackSourceWithOptions("local_path:"+root, opts)
	unsupported := diagnosticWithCode(withoutRegistration.Diagnostics, "component.kind.unsupported")
	if unsupported == nil || !strings.Contains(unsupported.Path, "dependencies[0]") {
		t.Fatalf("unregistered dependency diagnostics = %#v, want dependency-scoped unsupported kind", withoutRegistration.Diagnostics)
	}
	report := ValidateInstallablePackSourceWithComponentKindRegistrationSet("local_path:"+root, opts, set)
	if report.HasErrors() || report.Pack == nil {
		t.Fatalf("registration-aware dependency validation failed: %s", report.Error())
	}
}

func TestComponentRegistrationSetPropagatesAcrossPackConstructionCarriers(t *testing.T) {
	root := writePackFixture(t, "approval-process-pack", validPackManifest(`[
    {
      "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")
	buildCount := 0
	registration.BuildHandler = func(buildContext cp.ComponentBuildContext, _ cp.ComponentBuildRequest) error {
		buildCount++
		return buildContext.AddManagedComponent(cp.ComponentBuildManagedComponent{ExpectedProjection: []byte(`{"rows":[]}`)})
	}
	set, err := NewComponentKindRegistrationSet([]cp.ComponentKindRegistration{registration})
	if err != nil {
		t.Fatal(err)
	}
	opts := testPackSourceOptions(t)

	fresh := ValidateInstallablePackSourceWithComponentKindRegistrationSet("local_path:"+root, opts, set)
	if fresh.HasErrors() || fresh.Pack == nil {
		t.Fatalf("fresh build failed: %s", fresh.Error())
	}
	freshFormula := ValidateInstallablePackSourceWithRuntimeFormulaFunctionBundlesAndComponentKindRegistrationSet("local_path:"+root, opts, nil, set)
	if freshFormula.HasErrors() || freshFormula.Pack == nil {
		t.Fatalf("fresh formula build failed: %s", freshFormula.Error())
	}
	installed, err := ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevisionWithPackSourceOptionsAndComponentKindRegistrationSet(
		"jira-like-sample", AppPack, SupportedManifestFormatVersion,
		PackSourceKindLocalPath, root, "", opts, set,
	)
	if err != nil {
		t.Fatal(err)
	}
	installedFormula, err := ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevisionWithPackSourceOptionsAndFormulaBundlesAndComponentKindRegistrationSet(
		"jira-like-sample", AppPack, SupportedManifestFormatVersion, PackSourceKindLocalPath, root, "", opts, nil, true, set)
	if err != nil {
		t.Fatal(err)
	}
	active, err := resolveActiveInstalledRuntimePack(context.Background(), nil, activeInstalledPackRow{
		name:                  "jira-like-sample",
		category:              string(AppPack),
		manifestFormatVersion: sql.NullString{String: SupportedManifestFormatVersion, Valid: true},
		sourceKind:            sql.NullString{String: string(PackSourceKindLocalPath), Valid: true},
		sourceURI:             sql.NullString{String: root, Valid: true},
	}, nil, false, &set)
	if err != nil {
		t.Fatal(err)
	}
	activeSource, ok := active.(*SourceBackedPack)
	if !ok {
		t.Fatalf("active runtime pack type = %T", active)
	}
	rebuilt, err := NewSourceBackedPack(fresh.Source,
		WithComponentKindRegistrationsFromSourcePack(fresh.Pack),
		WithAllowedOwnerlessObjectAPIs([]string{"Quote"}),
	)
	if err != nil {
		t.Fatal(err)
	}
	if buildCount != 9 {
		t.Fatalf("build handler calls = %d, want two fresh + six installed + rebuild", buildCount)
	}
	for name, pack := range map[string]*SourceBackedPack{
		"fresh": fresh.Pack, "fresh formula": freshFormula.Pack,
		"installed": installed, "installed formula": installedFormula,
		"active runtime": activeSource, "ownerless": rebuilt,
	} {
		got := pack.componentRegistrationSet.Registrations()
		if !pack.componentRegistrationSetConfigured || len(got) != 1 || got[0].Contract.Kind != cp.ComponentKindApprovalProcess {
			t.Fatalf("%s registration set = %#v (configured %v)", name, got, pack.componentRegistrationSetConfigured)
		}
	}
	if !rebuilt.allowOwnerlessObjectAPI["quote"] {
		t.Fatal("ownerless rebuild lost its policy option")
	}
}
