package schemacatalog

import (
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/schema"
	"github.com/nexuscrm/shared/pkg/constants"
)

func TestRecordTypeCatalogProjectsDirectProtectedObjectCascade(t *testing.T) {
	relations, err := SystemTableRowMutationDirectProtectedCascadesFrom("_system_object")
	if err != nil {
		t.Fatalf("SystemTableRowMutationDirectProtectedCascadesFrom(): %v", err)
	}
	if len(relations) != 1 {
		t.Fatalf("relations = %+v", relations)
	}
	relation := relations[0]
	if relation.ParentTableName != constants.TableObject ||
		relation.ParentFieldName != constants.FieldID ||
		relation.ParentPhysicalName != constants.FieldID ||
		relation.ChildTableName != constants.TableRecordType ||
		relation.ChildFieldName != constants.FieldObjectID ||
		relation.ChildPhysicalName != constants.FieldObjectID {
		t.Fatalf("relation = %+v", relation)
	}
	if len(relation.Protection.Fields) != 2 {
		t.Fatalf("protection = %+v", relation.Protection)
	}
}

func TestSystemTableRowMutationDirectProtectedCascadesReturnIndependentFacts(t *testing.T) {
	first, err := SystemTableRowMutationDirectProtectedCascadesFrom(constants.TableObject)
	if err != nil {
		t.Fatal(err)
	}
	first[0].Protection.Fields[0].FieldName = "MutatedField"
	first[0].Protection.RequiredUniqueIndex.Columns[0] = "MutatedColumn"

	second, err := SystemTableRowMutationDirectProtectedCascadesFrom(constants.TableObject)
	if err != nil {
		t.Fatal(err)
	}
	if second[0].Protection.Fields[0].FieldName != "CatalogNamespaceId" {
		t.Fatalf("field name = %q after caller mutation", second[0].Protection.Fields[0].FieldName)
	}
	if second[0].Protection.RequiredUniqueIndex.Columns[0] != constants.FieldObjectID {
		t.Fatalf("index column = %q after caller mutation", second[0].Protection.RequiredUniqueIndex.Columns[0])
	}
}

func TestSystemTableRowMutationProtectedCascadeProjectsPhysicalFields(t *testing.T) {
	definitions := []schema.TableDefinition{
		{
			TableName: "ParentControl",
			Columns: []schema.ColumnDefinition{{
				Name:         "Id",
				PhysicalName: "parent_id",
			}},
		},
		{
			TableName: "ChildControl",
			Columns: []schema.ColumnDefinition{
				{
					Name:         "ParentId",
					PhysicalName: "parent_ref",
					Type:         "VARCHAR(255)",
				},
				{
					Name:                  "AuthorityValue",
					PhysicalName:          "authority_value",
					Type:                  "VARBINARY(128)",
					Nullable:              true,
					WriteProtectionPolicy: constants.FieldWriteProtectionAuthorityOnly,
				},
			},
			Indices: []schema.IndexDefinition{{
				Name:    "uq_authority",
				Columns: []string{"ParentId", "AuthorityValue"},
				Unique:  true,
			}},
			ForeignKeys: []schema.ForeignKeyDefinition{{
				Column:     "ParentId",
				References: "ParentControl(Id)",
				OnDelete:   "CASCADE",
			}},
			RowMutationProtection: &schema.RowMutationProtectionDefinition{
				Policy:              RowMutationProtectionAuthorityOwnedWhenAnyNonNull,
				ScopeFields:         []string{"ParentId"},
				Fields:              []string{"AuthorityValue"},
				RequiredUniqueIndex: "uq_authority",
			},
		},
	}
	inventory, err := systemTableRowMutationProtectedCascadeInventoryFromDefinitions(definitions)
	if err != nil {
		t.Fatal(err)
	}
	relations := inventory[normalizeWriteProtectionLookupKey("ParentControl")]
	if len(relations) != 1 {
		t.Fatalf("relations = %+v", relations)
	}
	relation := relations[0]
	if relation.ParentPhysicalName != "parent_id" ||
		relation.ChildPhysicalName != "parent_ref" ||
		relation.Protection.Fields[0].PhysicalName != "authority_value" {
		t.Fatalf("relation = %+v", relation)
	}
}

func TestSystemTableRowMutationProtectedCascadeRejectsMultiHopTopology(t *testing.T) {
	definitions := []schema.TableDefinition{
		{
			TableName: "RootControl",
			Columns:   []schema.ColumnDefinition{{Name: "Id"}},
		},
		{
			TableName: "ParentControl",
			Columns: []schema.ColumnDefinition{
				{Name: "Id"},
				{Name: "RootId"},
			},
			ForeignKeys: []schema.ForeignKeyDefinition{{
				Column:     "RootId",
				References: "RootControl(Id)",
				OnDelete:   "CASCADE",
			}},
		},
		{
			TableName: "ChildControl",
			Columns: []schema.ColumnDefinition{
				{Name: "ParentId", Type: "VARCHAR(255)"},
				{
					Name:                  "AuthorityValue",
					Type:                  "VARBINARY(128)",
					Nullable:              true,
					WriteProtectionPolicy: constants.FieldWriteProtectionAuthorityOnly,
				},
			},
			Indices: []schema.IndexDefinition{{
				Name:    "uq_authority",
				Columns: []string{"ParentId", "AuthorityValue"},
				Unique:  true,
			}},
			ForeignKeys: []schema.ForeignKeyDefinition{{
				Column:     "ParentId",
				References: "ParentControl(Id)",
				OnDelete:   "CASCADE",
			}},
			RowMutationProtection: &schema.RowMutationProtectionDefinition{
				Policy:              RowMutationProtectionAuthorityOwnedWhenAnyNonNull,
				ScopeFields:         []string{"ParentId"},
				Fields:              []string{"AuthorityValue"},
				RequiredUniqueIndex: "uq_authority",
			},
		},
	}
	_, err := systemTableRowMutationProtectedCascadeInventoryFromDefinitions(definitions)
	if err == nil || !strings.Contains(err.Error(), "multi-hop") {
		t.Fatalf("multi-hop topology error = %v", err)
	}
}
