package records

import (
	"database/sql/driver"
	"encoding/json"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"testing"

	eventFeature "github.com/nexuscrm/events"
	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	flowsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowv1"
)

type runtimeRecordEventBindingMatcher struct {
	want     catalogsnapshot.RuntimeWorkBinding
	wantFlow *catalogsnapshot.RuntimeWorkBinding
}

func (m runtimeRecordEventBindingMatcher) Match(value driver.Value) bool {
	raw, ok := value.([]byte)
	if !ok {
		text, textOK := value.(string)
		if !textOK {
			return false
		}
		raw = []byte(text)
	}
	var envelope eventFeature.InternalRecordEventEnvelope
	if json.Unmarshal(raw, &envelope) != nil {
		return false
	}
	for _, encoded := range envelope.InternalMetadata {
		bindingSet, err := catalogsnapshot.OpenRuntimeWorkBindingSet(encoded)
		if err != nil || bindingSet.Primary != m.want {
			continue
		}
		if m.wantFlow == nil && bindingSet.FlowWork == nil {
			return true
		}
		if m.wantFlow != nil && bindingSet.FlowWork != nil && *bindingSet.FlowWork == *m.wantFlow {
			return true
		}
	}
	return false
}

func runtimeRecordEventScopeDigest(t *testing.T, binding catalogsnapshot.RuntimeWorkBinding) string {
	t.Helper()
	digest, err := (persistence.RuntimeWorkScope{
		OrgID: binding.OrgID, AppInstallationID: binding.AppInstallationID,
		RuntimeCapabilityID: binding.RuntimeCapabilityID,
	}).Digest()
	if err != nil {
		t.Fatalf("runtime record-event scope digest: %v", err)
	}
	return digest
}

func runtimeRecordEventTestView(t *testing.T) *catalogsnapshot.RuntimeMetadataView {
	t.Helper()
	view, _ := runtimeRecordEventTestViews(t)
	return view
}

func runtimeRecordEventTestViews(t *testing.T) (*catalogsnapshot.RuntimeMetadataView, *catalogsnapshot.RuntimeMetadataView) {
	t.Helper()
	const revision catalogsnapshot.CatalogRevision = 71
	const installation = "01900000-0000-7000-8000-000000000071"
	const capability = "runtime.records.mutate.v1"
	input := catalogsnapshot.CompileInput{
		OrgID:   "org-runtime-record-event",
		Lineage: catalogsnapshot.Lineage{CatalogRevision: revision, SchemaGeneration: 4, EngineGeneration: 2},
		Objects: []catalogsnapshot.ObjectInput{{CatalogRevision: revision, Metadata: &models.ObjectMetadata{
			APIName: "Issue", Fields: []models.FieldMetadata{{APIName: constants.FieldID, PhysicalName: constants.FieldID, Type: constants.FieldTypeString}},
		}}},
		Projections: []catalogsnapshot.RuntimeProjectionInput{
			{
				CatalogRevision: revision, AppInstallationID: installation, CapabilityID: capability,
				Profile: catalogsnapshot.ProjectionRecordMutationV1, ObjectAPINames: []string{"Issue"},
			},
			{
				CatalogRevision: revision, AppInstallationID: installation, CapabilityID: flowsemantic.CapabilityName,
				Profile: catalogsnapshot.ProjectionRecordMutationV1, ObjectAPINames: []string{"Issue"},
			},
		},
	}
	snapshotArtifact, err := catalogsnapshot.CompileCatalogSnapshot(input)
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(snapshotArtifact)
	if err != nil {
		t.Fatal(err)
	}
	viewArtifact, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{
		AppInstallationID: installation, CapabilityID: capability,
	})
	if err != nil {
		t.Fatal(err)
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(viewArtifact)
	if err != nil {
		t.Fatal(err)
	}
	flowArtifact, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{
		AppInstallationID: installation, CapabilityID: flowsemantic.CapabilityName,
	})
	if err != nil {
		t.Fatal(err)
	}
	flowView, err := catalogsnapshot.OpenRuntimeMetadataView(flowArtifact)
	if err != nil {
		t.Fatal(err)
	}
	return view, flowView
}

func TestAllRecordsDurableEventWritersUseOneBindingEntryPoint(t *testing.T) {
	var direct []string
	err := filepath.WalkDir(".", func(path string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if entry.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") || filepath.Base(path) == "runtime_record_event.go" {
			return nil
		}
		raw, err := os.ReadFile(path)
		if err != nil {
			return err
		}
		if strings.Contains(string(raw), ".outbox.EnqueueEvent(") {
			direct = append(direct, path)
		}
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(direct) != 0 {
		t.Fatalf("record-event writers bypass the binding entry point: %v", direct)
	}
	for _, path := range []string{"persistence_insert.go", "persistence_update_helpers.go", "persistence_delete.go", "persistence_bulk_insert.go", "record_lifecycle_hooks.go"} {
		raw, err := os.ReadFile(path)
		if err != nil {
			t.Fatal(err)
		}
		if !strings.Contains(string(raw), "ps.enqueueRecordEvent(") {
			t.Fatalf("%s does not use the binding entry point", path)
		}
	}
}
