package analytics

import (
	"context"
	"errors"
	"strings"
	"testing"

	pkgerrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestParseVirtualReportSourceBinding(t *testing.T) {
	tests := []struct {
		name      string
		crt       *models.SystemReportType
		want      bool
		wantRef   string
		wantError string
	}{
		{name: "nil", crt: nil},
		{name: "absent", crt: &models.SystemReportType{FullName: "Physical", Metadata: `{"other":true}`}},
		{name: "valid", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":" OpportunityHistory ","configuration":{"columns":[]}}}`}, want: true, wantRef: "OpportunityHistory"},
		{name: "default configuration", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory"}}`}, want: true, wantRef: "OpportunityHistory"},
		{name: "malformed non-binding metadata", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{`}},
		{name: "malformed binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":`}, wantError: "invalid metadata JSON"},
		{name: "malformed sibling before binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":,"virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "malformed sibling after binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory"},"broken":}`}, wantError: "invalid metadata JSON"},
		{name: "malformed sibling before escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":,"\u0076irtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "malformed sibling after escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"\u0076irtualSource":{"sourceRef":"OpportunityHistory"},"broken":}`}, wantError: "invalid metadata JSON"},
		{name: "mismatched root closer before binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":],"virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "mismatched root closer after escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"\u0076irtualSource":{"sourceRef":"OpportunityHistory"},"broken":]}`}, wantError: "invalid metadata JSON"},
		{name: "mismatched nested closer before escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":[},"value"],"\u0076irtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "mismatched nested closer after binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory"},"broken":[}]}`}, wantError: "invalid metadata JSON"},
		{name: "missing separator before binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":true "virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "missing separator before escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":true "\u0076irtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "invalid separator before binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":true @ "virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "invalid separator before escaped binding", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"broken":true @ "\u0076irtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "invalid metadata JSON"},
		{name: "malformed string containing carrier text", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"note":"literal \"virtualSource\": marker`}},
		{name: "quoted carrier text", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"note":"virtualSource: marker"}`}},
		{name: "escaped carrier key", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"\u0076irtualSource":{"sourceRef":"OpportunityHistory","configuration":{}}}`}, want: true, wantRef: "OpportunityHistory"},
		{name: "nested carrier key", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"note":{"virtualSource":{"sourceRef":"OpportunityHistory"}}}`}},
		{name: "malformed nested literal carrier remains absent", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"outer":[},"virtualSource":{"sourceRef":"Nested"}}]}`}},
		{name: "malformed nested escaped carrier remains absent", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"outer":[},"\u0076irtualSource":{"sourceRef":"Nested"}}]}`}},
		{name: "blank source", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":" "}}`}, wantError: "sourceRef is required"},
		{name: "non object configuration", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory","configuration":[]}}`}, wantError: "configuration must be an object"},
		{name: "unknown binding field", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory","columns":[]}}`}, wantError: "unknown field"},
		{name: "dual metadata owners", crt: &models.SystemReportType{FullName: "OpportunityHistory", Metadata: `{"joinTemplate":{},"virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "both joinTemplate and virtualSource"},
		{name: "dual join owners", crt: &models.SystemReportType{FullName: "OpportunityHistory", Join: `{}`, Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory"}}`}, wantError: "both Join and virtualSource"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, ok, err := ParseVirtualReportSourceBinding(tt.crt)
			if tt.wantError != "" {
				if err == nil || !strings.Contains(err.Error(), tt.wantError) {
					t.Fatalf("error = %v, want containing %q", err, tt.wantError)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseVirtualReportSourceBinding: %v", err)
			}
			if ok != tt.want || got.SourceRef != tt.wantRef {
				t.Fatalf("binding = %#v, ok = %v", got, ok)
			}
			if ok && string(got.Configuration) == "" {
				t.Fatal("valid binding has no configuration object")
			}
		})
	}
}

func TestCRTResolverResolvesVirtualReportSourceBinding(t *testing.T) {
	repository := &bindingCRTRepository{byName: map[string]*models.SystemReportType{
		"OpportunityHistory": {
			FullName: "OpportunityHistory",
			Metadata: `{"virtualSource":{"sourceRef":"OpportunityHistory","configuration":{}}}`,
		},
		"Mismatch": {
			FullName: "Mismatch",
			Metadata: `{"virtualSource":{"sourceRef":"Other","configuration":{}}}`,
		},
	}}
	resolver := newCRTResolverForTest(repository, nil, nil, nil)
	if binding, ok, exists, err := resolver.ResolveVirtualReportSourceBinding(context.Background(), "OpportunityHistory", nil); err != nil || !ok || !exists || binding.SourceRef != "OpportunityHistory" {
		t.Fatalf("valid binding = %#v, ok = %v, exists = %v, err = %v", binding, ok, exists, err)
	}
	if _, ok, exists, err := resolver.ResolveVirtualReportSourceBinding(context.Background(), "Missing", nil); err != nil || ok || exists {
		t.Fatalf("missing binding ok = %v, exists = %v, err = %v", ok, exists, err)
	}
	if _, _, _, err := resolver.ResolveVirtualReportSourceBinding(context.Background(), "Mismatch", nil); err == nil || !strings.Contains(err.Error(), "binds virtual source") {
		t.Fatalf("mismatch error = %v", err)
	}
	repository.err = errors.New("database unavailable")
	if _, _, _, err := resolver.ResolveVirtualReportSourceBinding(context.Background(), "OpportunityHistory", nil); err == nil || !strings.Contains(err.Error(), "database unavailable") {
		t.Fatalf("repository error = %v", err)
	}
}

func TestCRTResolverRoutingPreservesSingleLookupSemantics(t *testing.T) {
	t.Run("standard object short circuits repository", func(t *testing.T) {
		repository := &bindingCRTRepository{}
		standard := &StandardObjectResolver{canonical: map[string]string{"account": "account"}}
		resolver := newCRTResolverForTest(repository, nil, standard, nil)
		if _, _, err := resolver.Resolve(context.Background(), "Account", nil); err != nil {
			t.Fatalf("Resolve standard object: %v", err)
		}
		if repository.loadByNameCalls != 0 {
			t.Fatalf("repository calls = %d, want 0", repository.loadByNameCalls)
		}
	})

	t.Run("physical CRT with malformed non-binding metadata keeps legacy degradation", func(t *testing.T) {
		repository := &bindingCRTRepository{byName: map[string]*models.SystemReportType{
			"PhysicalReport": {FullName: "PhysicalReport", BaseObject: "Account", Metadata: `{`},
		}}
		resolver := newCRTResolverForTest(repository, nil, nil, nil)
		if _, _, err := resolver.Resolve(context.Background(), "PhysicalReport", nil); err != nil {
			t.Fatalf("Resolve physical CRT: %v", err)
		}
		if repository.loadByNameCalls != 1 {
			t.Fatalf("repository calls = %d, want 1", repository.loadByNameCalls)
		}
	})

	t.Run("identifiable malformed binding fails closed", func(t *testing.T) {
		repository := &bindingCRTRepository{byName: map[string]*models.SystemReportType{
			"BoundReport": {FullName: "BoundReport", BaseObject: "Account", Metadata: `{"virtualSource":`},
		}}
		resolver := newCRTResolverForTest(repository, nil, nil, nil)
		if _, _, err := resolver.Resolve(context.Background(), "BoundReport", nil); err == nil || !strings.Contains(err.Error(), "invalid metadata JSON") {
			t.Fatalf("malformed binding error = %v", err)
		}
		if repository.loadByNameCalls != 1 {
			t.Fatalf("repository calls = %d, want 1", repository.loadByNameCalls)
		}
	})

	t.Run("synthetic CRT retains one repository probe", func(t *testing.T) {
		repository := &bindingCRTRepository{}
		resolver := newCRTResolverForTest(repository, nil, nil, nil)
		if _, _, err := resolver.Resolve(context.Background(), "CustomEntity$Ghost", nil); err != nil {
			t.Fatalf("Resolve synthetic CRT: %v", err)
		}
		if repository.loadByNameCalls != 1 {
			t.Fatalf("repository calls = %d, want 1", repository.loadByNameCalls)
		}
	})
}

type bindingCRTRepository struct {
	byName          map[string]*models.SystemReportType
	err             error
	loadByNameCalls int
}

func (r *bindingCRTRepository) LoadByFullName(_ context.Context, fullName string, _ *models.UserSession) (*models.SystemReportType, error) {
	r.loadByNameCalls++
	if r.err != nil {
		return nil, r.err
	}
	if crt := r.byName[fullName]; crt != nil {
		return crt, nil
	}
	return nil, pkgerrors.NewNotFoundError("CRT", fullName)
}

func (r *bindingCRTRepository) LoadByID(context.Context, string, *models.UserSession) (*models.SystemReportType, error) {
	return nil, pkgerrors.NewNotFoundError("CRT", "")
}
