package persistence

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/backend/internal/domain/schema"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestBatchSaveObjectMetadataGuardsCustomizedQuoteProductObjectColumns(t *testing.T) {
	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlContainsMatcher()))
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectExec(strings.Join([]string{
		"INSERT INTO `_System_Object`",
		"`Label` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`Label`), `Label`)",
		"`Description` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`Description`), `Description`)",
		"`ListFields` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`ListFields`), `ListFields`)",
		"`PathField` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`PathField`), `PathField`)",
		"`SharingModel` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`SharingModel`), `SharingModel`)",
		"`RequiredReadPermission` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`RequiredReadPermission`), `RequiredReadPermission`)",
		"`AccessPolicy` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`AccessPolicy`), `AccessPolicy`)",
		"`LastModifiedDate` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, NOW(), `LastModifiedDate`)",
	}, "\n")).
		WillReturnResult(sqlmock.NewResult(0, 1))

	desc := "tenant spec description"
	path := "Status__c"
	repo := NewSchemaRepository(db, nil)
	err = repo.BatchSaveObjectMetadata(context.Background(), []*models.ObjectMetadata{{
		ID:                     schema.GenerateObjectID("quote_product__c"),
		APIName:                "quote_product__c",
		Label:                  "Quote Product",
		PluralLabel:            "Quote Products",
		Description:            &desc,
		IsCustom:               true,
		Searchable:             true,
		SharingModel:           constants.SharingModelControlledByParent,
		ListFields:             []string{"Name", "Quote__c"},
		PathField:              &path,
		RequiredReadPermission: "ReadTenantQuoteProduct",
	}}, nil)
	if err != nil {
		t.Fatalf("BatchSaveObjectMetadata: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unmet sql expectations: %v", err)
	}
}

func TestBatchSaveFieldMetadataGuardsCustomizedQuoteProductFieldColumns(t *testing.T) {
	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlContainsMatcher()))
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectExec(strings.Join([]string{
		"INSERT INTO `_System_Field`",
		"`Type` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`Type`), `Type`)",
		"`Required` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`Required`), `Required`)",
		"`DefaultValue` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`DefaultValue`), `DefaultValue`)",
		"`HelpText` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`HelpText`), `HelpText`)",
		"`MinLength` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`MinLength`), `MinLength`)",
		"`ReferenceTo` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`ReferenceTo`), `ReferenceTo`)",
		"`RelationshipName` = IF(`IsCustomized` = 0 OR `IsCustomized` IS NULL, VALUES(`RelationshipName`), `RelationshipName`)",
	}, "\n")).
		WillReturnResult(sqlmock.NewResult(0, 1))

	help := "admin-owned help text must survive tenant re-apply"
	rel := "Product__r"
	repo := NewSchemaRepository(db, nil)
	err = repo.BatchSaveFieldMetadata(context.Background(), []schema.FieldWithContext{{
		ObjectID:  "obj_quote_product__c",
		FieldID:   schema.GenerateFieldID("quote_product__c", "Product__c"),
		TableName: "quote_product__c",
		Field: &models.FieldMetadata{
			APIName:          "Product__c",
			Label:            "Product",
			Type:             constants.FieldTypeReference,
			HelpText:         &help,
			ReferenceTo:      []string{"Product2"},
			RelationshipName: &rel,
		},
	}}, nil)
	if err != nil {
		t.Fatalf("BatchSaveFieldMetadata: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unmet sql expectations: %v", err)
	}
}

func sqlContainsMatcher() sqlmock.QueryMatcher {
	return sqlmock.QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
		for _, expected := range strings.Split(expectedSQL, "\n") {
			if expected == "" {
				continue
			}
			if !strings.Contains(actualSQL, expected) {
				return fmt.Errorf("actual SQL missing %q in %s", expected, actualSQL)
			}
		}
		return nil
	})
}
