package metadata

import (
	"context"
	"testing"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type scopedPicklistMetadataRepo struct {
	ports.MetadataRepository

	schema      *models.ObjectMetadata
	recordTypes []*models.RecordType
	values      []models.ScopedPicklistValue
	valuesErr   error
}

func (r *scopedPicklistMetadataRepo) GetAllSchemas(_ context.Context) ([]*models.ObjectMetadata, error) {
	return []*models.ObjectMetadata{r.schema}, nil
}

func (r *scopedPicklistMetadataRepo) GetSchemaByAPIName(_ context.Context, apiName string) (*models.ObjectMetadata, error) {
	if r.schema != nil && r.schema.APIName == apiName {
		return r.schema, nil
	}
	return nil, nil
}

func (r *scopedPicklistMetadataRepo) GetAllRelationships(_ context.Context) ([]*models.Relationship, error) {
	return nil, nil
}

func (r *scopedPicklistMetadataRepo) GetRecordTypes(_ context.Context, _ string) ([]*models.RecordType, error) {
	return r.recordTypes, nil
}

func (r *scopedPicklistMetadataRepo) GetScopedPicklistValues(_ context.Context, _, _ string) ([]models.ScopedPicklistValue, error) {
	return r.values, r.valuesErr
}

func TestResolvePicklistValuesForRecordType_MasterReturnsFieldOptions(t *testing.T) {
	fieldDefault := "New"
	repo := &scopedPicklistMetadataRepo{schema: scopedPicklistSchema(fieldDefault)}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", constants.SyntheticMasterRecordTypeID, "Status")
	if err != nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = %v", err)
	}
	if !got.IsMaster || got.RecordTypeID != constants.SyntheticMasterRecordTypeID {
		t.Fatalf("master flags = IsMaster:%v RecordTypeID:%q", got.IsMaster, got.RecordTypeID)
	}
	if got.DefaultValue == nil || *got.DefaultValue != "New" {
		t.Fatalf("DefaultValue = %v, want New", got.DefaultValue)
	}
	if len(got.Values) != 3 {
		t.Fatalf("values = %d, want 3", len(got.Values))
	}
	if got.Values[0].Value != "New" || got.Values[0].Label != "New Label" || !got.Values[0].IsDefault {
		t.Fatalf("first value = %+v, want New default with label", got.Values[0])
	}
}

func TestResolvePicklistValuesForRecordType_NonMasterReturnsScopedSubset(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{{
			ID:                "rt-special",
			ObjectAPIName:     "Widget",
			Name:              "Special",
			IsActive:          true,
			BusinessProcessID: ptrString("bp-1"),
		}},
		values: []models.ScopedPicklistValue{
			{Value: "Working", IsActive: true, SortOrder: 20},
			{Value: "Closed", IsActive: true, IsDefault: true, SortOrder: 30, OwnerPack: "pack"},
		},
	}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err != nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = %v", err)
	}
	if got.IsMaster {
		t.Fatal("IsMaster = true, want false")
	}
	if got.BusinessProcessID == nil || *got.BusinessProcessID != "bp-1" {
		t.Fatalf("BusinessProcessID = %v, want bp-1", got.BusinessProcessID)
	}
	if len(got.Values) != 2 {
		t.Fatalf("values = %d, want 2", len(got.Values))
	}
	if got.Values[0].Value != "Working" || got.Values[0].Label != "Working Label" {
		t.Fatalf("first value = %+v, want Working with field label", got.Values[0])
	}
	if got.DefaultValue == nil || *got.DefaultValue != "Closed" || !got.Values[1].IsDefault {
		t.Fatalf("default = %v values=%+v, want Closed", got.DefaultValue, got.Values)
	}
}

func TestResolvePicklistValuesForRecordType_NonMasterMissingFailsClosed(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{{
			ID:            "rt-special",
			ObjectAPIName: "Widget",
			Name:          "Special",
			IsActive:      true,
		}},
	}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err == nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = nil, got %+v", got)
	}
	if !apperrors.IsNotFound(err) {
		t.Fatalf("err = %T %v, want not found", err, err)
	}
}

func TestResolvePicklistValuesForRecordType_RecordTypeFailuresAreNotFound(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{
			{ID: "rt-inactive", ObjectAPIName: "Widget", Name: "Inactive", IsActive: false},
			{ID: "rt-other", ObjectAPIName: "Other", Name: "Other", IsActive: true},
		},
	}
	service := NewMetadataService(repo, nil, nil)

	for _, recordTypeID := range []string{"rt-missing", "rt-inactive", "rt-other"} {
		got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", recordTypeID, "Status")
		if err == nil {
			t.Fatalf("%s error = nil, got %+v", recordTypeID, got)
		}
		if !apperrors.IsNotFound(err) {
			t.Fatalf("%s err = %T %v, want not found", recordTypeID, err, err)
		}
	}
}

func TestResolvePicklistValuesForRecordType_CatalogueAndDefaultTiebreaker(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{{
			ID:            "rt-special",
			ObjectAPIName: "Widget",
			Name:          "Special",
			IsActive:      true,
		}},
		values: []models.ScopedPicklistValue{
			{Value: "New", IsActive: true, IsDefault: true, SortOrder: 10},
			{Value: "Working", IsActive: true, IsDefault: true, SortOrder: 20, OwnerPack: "pack"},
			{Value: "Closed", IsActive: true, IsDefault: true, SortOrder: 30, OwnerPack: "pack", IsCustomized: true},
		},
	}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err != nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = %v", err)
	}
	if got.DefaultValue == nil || *got.DefaultValue != "Closed" {
		t.Fatalf("DefaultValue = %v, want customized Closed", got.DefaultValue)
	}
	defaults := 0
	for _, value := range got.Values {
		if value.IsDefault {
			defaults++
			if value.Value != "Closed" {
				t.Fatalf("default value = %+v, want Closed", value)
			}
		}
	}
	if defaults != 1 {
		t.Fatalf("defaults = %d, want 1", defaults)
	}

	repo.values = []models.ScopedPicklistValue{{Value: "Stale", IsActive: true}}
	got, err = service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err == nil {
		t.Fatalf("stale value error = nil, got %+v", got)
	}
	if !apperrors.IsInternal(err) {
		t.Fatalf("stale value err = %T %v, want internal", err, err)
	}
}

// TestResolvePicklistValuesForRecordType_InactiveDefaultIsSuppressed is the
// Codex round-4 HIGH regression guard (PR #919). Even if live
// `_System_RecordTypePicklistValue` somehow carries `IsDefault=1` on an
// `IsActive=0` row (drift, manual SQL, pre-fix install), the read path must
// NOT publish that row as `DefaultValue` — it would prepopulate an unusable
// default for record creation. The defense-in-depth normalization picks the
// next active default if one exists, else returns no default.
func TestResolvePicklistValuesForRecordType_InactiveDefaultIsSuppressed(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{{
			ID:            "rt-special",
			ObjectAPIName: "Widget",
			Name:          "Special",
			IsActive:      true,
		}},
		values: []models.ScopedPicklistValue{
			// The contradictory row: default flagged but inactive. Must be
			// excluded from default selection.
			{Value: "New", IsActive: false, IsDefault: true, SortOrder: 10, OwnerPack: "pack"},
			// The healthy fallback: active + default-flagged.
			{Value: "Working", IsActive: true, IsDefault: true, SortOrder: 20, OwnerPack: "pack"},
		},
	}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err != nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = %v", err)
	}
	if got.DefaultValue == nil || *got.DefaultValue != "Working" {
		t.Fatalf("DefaultValue = %v, want Working (inactive default suppressed)", got.DefaultValue)
	}
	for _, v := range got.Values {
		if v.Value == "New" && v.IsDefault {
			t.Fatalf("inactive row %+v retained IsDefault=true, want false", v)
		}
		if v.Value == "Working" && !v.IsDefault {
			t.Fatalf("active fallback %+v lost IsDefault, want true", v)
		}
	}
}

// TestResolvePicklistValuesForRecordType_AllDefaultsInactiveReturnsNoDefault
// covers the conservative branch of the round-4 HIGH guard: when EVERY
// default-flagged row is inactive, the read path returns no default rather
// than publishing an unusable one. (Codex preferred raising an internal
// error; we chose to log + return nil because this is the read path and a
// hard error would break user-facing flows — Layer 1 / install-time
// validation is where we fail closed.)
func TestResolvePicklistValuesForRecordType_AllDefaultsInactiveReturnsNoDefault(t *testing.T) {
	repo := &scopedPicklistMetadataRepo{
		schema: scopedPicklistSchema("New"),
		recordTypes: []*models.RecordType{{
			ID:            "rt-special",
			ObjectAPIName: "Widget",
			Name:          "Special",
			IsActive:      true,
		}},
		values: []models.ScopedPicklistValue{
			{Value: "New", IsActive: false, IsDefault: true, SortOrder: 10, OwnerPack: "pack"},
			{Value: "Working", IsActive: true, IsDefault: false, SortOrder: 20, OwnerPack: "pack"},
		},
	}
	service := NewMetadataService(repo, nil, nil)

	got, err := service.ResolvePicklistValuesForRecordType(context.Background(), "Widget", "rt-special", "Status")
	if err != nil {
		t.Fatalf("ResolvePicklistValuesForRecordType() error = %v", err)
	}
	if got.DefaultValue != nil {
		t.Fatalf("DefaultValue = %v, want nil (all defaults inactive)", *got.DefaultValue)
	}
	for _, v := range got.Values {
		if v.IsDefault {
			t.Fatalf("value %+v IsDefault=true, want false after suppression", v)
		}
	}
}

func scopedPicklistSchema(defaultValue string) *models.ObjectMetadata {
	return &models.ObjectMetadata{
		ID:      "obj-widget",
		APIName: "Widget",
		Fields: []models.FieldMetadata{{
			ID:      "fld-status",
			APIName: "Status",
			Label:   "Status",
			Type:    constants.FieldTypePicklist,
			Options: models.PicklistOptions{
				{Value: "New", Label: "New Label"},
				{Value: "Working", Label: "Working Label"},
				{Value: "Closed", Label: "Closed Label"},
			},
			DefaultValue: ptrString(defaultValue),
		}},
	}
}

func ptrString(value string) *string {
	return &value
}
