package controlplane

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

	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
	"github.com/pingcap-inc/nexuscrm-connectors/features/connector/capabilities"
)

type connectorCapabilityGuardVerifier struct{}

func (connectorCapabilityGuardVerifier) Verify(context.Context, *connectorcapabilities.VerifierInput) error {
	return nil
}

func mustConnectorPackWithRawComponents(t *testing.T, name string, components []string) *packs.SourceBackedPack {
	t.Helper()
	root := filepath.Join(t.TempDir(), name)
	if err := os.MkdirAll(root, 0o755); err != nil {
		t.Fatalf("mkdir: %v", err)
	}
	compJSON := "[]"
	if len(components) > 0 {
		compJSON = "[" + strings.Join(components, ",") + "]"
	}
	manifest := `{
  "manifestFormatVersion": "1",
  "name": "` + name + `",
  "displayName": "Test Connector",
  "category": "connector-pack",
  "version": "0.1.0",
  "platformCompatibility": {"min": "0.2.0", "maxExclusive": "0.3.0"},
  "dependencies": [],
  "components": ` + compJSON + `
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack.json: %v", err)
	}
	report := packs.ValidatePackSource("local_path:" + root)
	if report.Source == nil || report.HasErrors() {
		var diags []string
		for _, d := range report.Diagnostics {
			diags = append(diags, d.Message)
		}
		t.Fatalf("ValidatePackSource: source is nil or invalid; diagnostics: %v", diags)
	}
	pack, err := packs.NewSourceBackedPack(report.Source)
	if err != nil {
		t.Fatalf("NewSourceBackedPack: %v", err)
	}
	return pack
}

func connectorCapabilityComponentForTest(namespace, kind string) string {
	return `{"kind":"connector_capability","businessKey":"` + namespace + `:` + kind + `","manageability":"locked","payload":{"namespace":"` + namespace + `","kind":"` + kind + `"}}`
}

func connectorHandlerConfigComponentForTest(namespace, kind, config string) string {
	return `{"kind":"connector_inbound_handler_config","businessKey":"` + namespace + `:` + kind + `","manageability":"locked","payload":{"namespace":"` + namespace + `","capabilityKind":"` + kind + `","config":` + config + `}}`
}

func signatureVerifierConfigForGuardTest() string {
	return `{
  "scheme":"hmac-sha256-template",
  "signingTemplate":"{Timestamp}.{RawBody}",
  "timestampHeader":"X-Test-Timestamp",
  "signatureHeader":"X-Test-Signature",
  "signatureEncoding":"hex",
  "skewWindow":"5m",
  "secretName":"default"
}`
}

func envelopeParserConfigForGuardTest() string {
	return `{
  "eventArrayPath":".events",
  "rules":[{
    "eventTypeLiteral":"thing.updated",
    "namespaceFromCtx":true,
    "objectKind":"thing",
    "sourceIdPath":".objectId",
    "idempotencyKeyPath":".eventId",
    "payloadProjection":[{"name":"objectId","path":".objectId"}]
  }]
}`
}

func TestAssertConnectorCapabilitiesHaveHandlers_ConfigPresentNoCompiledHandlerRequired(t *testing.T) {
	const namespace = "acme-connector"
	pack := mustConnectorPackWithRawComponents(t, namespace, []string{
		connectorCapabilityComponentForTest(namespace, "signature_verifier"),
		connectorHandlerConfigComponentForTest(namespace, "signature_verifier", signatureVerifierConfigForGuardTest()),
		connectorCapabilityComponentForTest(namespace, "envelope_parser"),
		connectorHandlerConfigComponentForTest(namespace, "envelope_parser", envelopeParserConfigForGuardTest()),
	})

	if err := assertConnectorCapabilitiesHaveHandlers(pack); err != nil {
		t.Fatalf("assertConnectorCapabilitiesHaveHandlers = %v, want nil", err)
	}
}

func TestAssertConnectorCapabilitiesHaveHandlers_MissingHandlerConfigFailsClosed(t *testing.T) {
	const namespace = "acme-connector"
	pack := mustConnectorPackWithRawComponents(t, namespace, []string{
		connectorCapabilityComponentForTest(namespace, "signature_verifier"),
	})

	err := assertConnectorCapabilitiesHaveHandlers(pack)
	if err == nil {
		t.Fatal("assertConnectorCapabilitiesHaveHandlers = nil, want missing config error")
	}
	if !strings.Contains(err.Error(), "connector_inbound_handler_config") {
		t.Fatalf("error %q does not mention handler config component", err)
	}
	if !strings.Contains(err.Error(), "signature_verifier") {
		t.Fatalf("error %q does not mention missing capability kind", err)
	}
}

func TestAssertConnectorCapabilitiesHaveHandlers_InstalledCompatRequiresLegacyHandlerRegistration(t *testing.T) {
	const namespace = "legacy-compiled-connector"
	t.Cleanup(func() {
		connectorcapabilities.UnregisterCapability(namespace, connectorcapabilities.SignatureVerifier)
	})
	pack := mustConnectorPackWithRawComponents(t, namespace, []string{
		connectorCapabilityComponentForTest(namespace, "signature_verifier"),
	})

	if err := assertConnectorCapabilitiesHaveHandlers(pack); err == nil {
		t.Fatal("strict guard = nil, want missing config error")
	}
	if err := assertConnectorCapabilitiesHaveHandlersWithInstalledCompat(pack, true); err == nil {
		t.Fatal("installed compat guard = nil without legacy compiled handler, want missing config error")
	}

	if err := connectorcapabilities.RegisterCapability(namespace, connectorcapabilities.SignatureVerifier, connectorCapabilityGuardVerifier{}); err != nil {
		t.Fatalf("register legacy compiled handler: %v", err)
	}
	if err := assertConnectorCapabilitiesHaveHandlersWithInstalledCompat(pack, true); err != nil {
		t.Fatalf("installed compat guard with legacy compiled handler = %v, want nil", err)
	}
}

func TestAssertConnectorCapabilitiesHaveHandlers_InstalledCompatRejectsPackBoundOnlyProjection(t *testing.T) {
	const namespace = "pack-bound-connector"
	t.Cleanup(func() {
		connectorcapabilities.UnregisterCapability(namespace, connectorcapabilities.SignatureVerifier)
	})
	pack := mustConnectorPackWithRawComponents(t, namespace, []string{
		connectorCapabilityComponentForTest(namespace, "signature_verifier"),
	})

	if err := connectorcapabilities.UpsertCapabilityForPackInstall(namespace, connectorcapabilities.SignatureVerifier, "pack-install-1", connectorCapabilityGuardVerifier{}); err != nil {
		t.Fatalf("upsert pack-bound handler: %v", err)
	}
	if err := assertConnectorCapabilitiesHaveHandlersWithInstalledCompat(pack, true); err == nil {
		t.Fatal("installed compat guard = nil for pack-bound projection, want missing config error")
	}
}

func TestAssertConnectorCapabilitiesHaveHandlers_OrphanHandlerConfigFailsClosed(t *testing.T) {
	const namespace = "acme-connector"
	pack := mustConnectorPackWithRawComponents(t, namespace, []string{
		connectorHandlerConfigComponentForTest(namespace, "signature_verifier", signatureVerifierConfigForGuardTest()),
	})

	err := assertConnectorCapabilitiesHaveHandlers(pack)
	if err == nil {
		t.Fatal("assertConnectorCapabilitiesHaveHandlers = nil, want orphan config error")
	}
	if !strings.Contains(err.Error(), "orphan") {
		t.Fatalf("error %q does not mention orphan config", err)
	}
	if !strings.Contains(err.Error(), "signature_verifier") {
		t.Fatalf("error %q does not mention orphan capability kind", err)
	}
}
