package persistence

import (
	"strings"
	"testing"

	"github.com/nexuscrm/backend/internal/domain/schema"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestMapFieldToColumnDef(t *testing.T) {
	r := &SchemaRepository{db: nil}

	t.Run("simple string field", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName: "Name",
			Type:    constants.FieldTypeString,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Name != "Name" {
			t.Errorf("col.Name = %q, want %q", col.Name, "Name")
		}
		if col.Type != SQLTypeVarchar255 {
			t.Errorf("col.Type = %q, want %q", col.Type, SQLTypeVarchar255)
		}
		if col.LogicalType != string(constants.FieldTypeString) {
			t.Errorf("col.LogicalType = %q, want %q", col.LogicalType, constants.FieldTypeString)
		}
		if !col.Nullable {
			t.Error("non-required field should be nullable")
		}
	})

	t.Run("required field is not nullable", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName:  "Email",
			Type:     constants.FieldTypeEmail,
			Required: true,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Nullable {
			t.Error("required field should not be nullable")
		}
	})

	t.Run("unique field", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName:  "Code",
			Type:     constants.FieldTypeString,
			IsUnique: true,
		}
		col := r.mapFieldToColumnDef(field)

		if !col.Unique {
			t.Error("unique field should have Unique = true")
		}
	})

	t.Run("reference field with single target", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName:     "AccountId",
			Type:        constants.FieldTypeReference,
			ReferenceTo: []string{"Account"},
		}
		col := r.mapFieldToColumnDef(field)

		if col.Type != SQLTypeVarchar255 {
			t.Errorf("reference col.Type = %q, want %q", col.Type, SQLTypeVarchar255)
		}
		if len(col.ReferenceTo) != 1 || col.ReferenceTo[0] != "Account" {
			t.Errorf("col.ReferenceTo = %v, want [Account]", col.ReferenceTo)
		}
	})

	t.Run("master-detail field", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName:        "ParentId",
			Type:           constants.FieldTypeMasterDetail,
			IsMasterDetail: true,
			ReferenceTo:    []string{"Parent"},
		}
		col := r.mapFieldToColumnDef(field)

		if !col.IsMasterDetail {
			t.Error("master-detail field should have IsMasterDetail = true")
		}
	})

	t.Run("field with default value", func(t *testing.T) {
		defaultVal := "active"
		field := models.FieldMetadata{
			APIName:      "Status",
			Type:         constants.FieldTypeString,
			DefaultValue: &defaultVal,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Default != "'active'" {
			t.Errorf("col.Default = %q, want %q", col.Default, "'active'")
		}
	})

	t.Run("field with default value containing single quote is escaped", func(t *testing.T) {
		defaultVal := "O'Reilly"
		field := models.FieldMetadata{
			APIName:      "Publisher",
			Type:         constants.FieldTypeString,
			DefaultValue: &defaultVal,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Default != "'O''Reilly'" {
			t.Errorf("col.Default = %q, want %q", col.Default, "'O''Reilly'")
		}
	})

	t.Run("formula field", func(t *testing.T) {
		formula := "price * quantity"
		rt := models.FieldType("double")
		field := models.FieldMetadata{
			APIName:    "Total",
			Type:       constants.FieldTypeFormula,
			Formula:    &formula,
			ReturnType: &rt,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Formula != "price * quantity" {
			t.Errorf("col.Formula = %q, want %q", col.Formula, "price * quantity")
		}
		if col.ReturnType != "double" {
			t.Errorf("col.ReturnType = %q, want %q", col.ReturnType, "double")
		}
	})

	t.Run("field with delete rule CASCADE", func(t *testing.T) {
		dr := models.DeleteRule(constants.DeleteRuleCascade)
		field := models.FieldMetadata{
			APIName:    "AccountId",
			Type:       constants.FieldTypeReference,
			DeleteRule: &dr,
		}
		col := r.mapFieldToColumnDef(field)

		if col.OnDelete != "CASCADE" {
			t.Errorf("col.OnDelete = %q, want %q", col.OnDelete, "CASCADE")
		}
	})

	t.Run("field with delete rule SetNull", func(t *testing.T) {
		dr := models.DeleteRule(constants.DeleteRuleSetNull)
		field := models.FieldMetadata{
			APIName:    "ContactId",
			Type:       constants.FieldTypeReference,
			DeleteRule: &dr,
		}
		col := r.mapFieldToColumnDef(field)

		if col.OnDelete != "SET NULL" {
			t.Errorf("col.OnDelete = %q, want %q", col.OnDelete, "SET NULL")
		}
	})

	t.Run("field with delete rule Restrict", func(t *testing.T) {
		dr := models.DeleteRule(constants.DeleteRuleRestrict)
		field := models.FieldMetadata{
			APIName:    "OwnerId",
			Type:       constants.FieldTypeReference,
			DeleteRule: &dr,
		}
		col := r.mapFieldToColumnDef(field)

		if col.OnDelete != "RESTRICT" {
			t.Errorf("col.OnDelete = %q, want %q", col.OnDelete, "RESTRICT")
		}
	})

	t.Run("field with relationship name", func(t *testing.T) {
		relName := "Contacts"
		field := models.FieldMetadata{
			APIName:          "AccountId",
			Type:             constants.FieldTypeReference,
			ReferenceTo:      []string{"Account"},
			RelationshipName: &relName,
		}
		col := r.mapFieldToColumnDef(field)

		if col.RelationshipName != "Contacts" {
			t.Errorf("col.RelationshipName = %q, want %q", col.RelationshipName, "Contacts")
		}
	})

	t.Run("field with picklist options", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName: "Stage",
			Type:    constants.FieldTypePicklist,
			Options: models.PicklistOptionsFromStrings([]string{"Open", "Closed"}),
		}
		col := r.mapFieldToColumnDef(field)

		if len(col.Options) != 2 {
			t.Errorf("col.Options length = %d, want 2", len(col.Options))
		}
	})

	t.Run("boolean field maps to BOOLEAN SQL type", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName: "IsActive",
			Type:    constants.FieldTypeBoolean,
		}
		col := r.mapFieldToColumnDef(field)

		if !strings.EqualFold(col.Type, "BOOLEAN") {
			t.Errorf("boolean col.Type = %q, want BOOLEAN", col.Type)
		}
	})

	t.Run("field with nil optional pointers", func(t *testing.T) {
		field := models.FieldMetadata{
			APIName:          "SimpleField",
			Type:             constants.FieldTypeString,
			DefaultValue:     nil,
			Formula:          nil,
			ReturnType:       nil,
			DeleteRule:       nil,
			RelationshipName: nil,
		}
		col := r.mapFieldToColumnDef(field)

		if col.Default != "" {
			t.Errorf("col.Default = %q, want empty", col.Default)
		}
		if col.Formula != "" {
			t.Errorf("col.Formula = %q, want empty", col.Formula)
		}
		if col.ReturnType != "" {
			t.Errorf("col.ReturnType = %q, want empty", col.ReturnType)
		}
		if col.OnDelete != "" {
			t.Errorf("col.OnDelete = %q, want empty", col.OnDelete)
		}
		if col.RelationshipName != "" {
			t.Errorf("col.RelationshipName = %q, want empty", col.RelationshipName)
		}
	})
}

func TestPrepareFieldForBatch(t *testing.T) {
	r := &SchemaRepository{db: nil}

	col := schema.ColumnDefinition{
		Name:     "Amount",
		Type:     "DECIMAL(18,6)",
		Nullable: true,
	}

	fc := r.PrepareFieldForBatch("Account", col)

	if fc.TableName != "Account" {
		t.Errorf("fc.TableName = %q, want %q", fc.TableName, "Account")
	}
	if fc.ColumnDefinition.Name != "Amount" {
		t.Errorf("fc.ColumnDefinition.Name = %q, want %q", fc.ColumnDefinition.Name, "Amount")
	}
	if fc.ColumnDefinition.Type != "DECIMAL(18,6)" {
		t.Errorf("fc.ColumnDefinition.Type = %q, want %q", fc.ColumnDefinition.Type, "DECIMAL(18,6)")
	}
}
