package bootstrapimpl

import (
	"fmt"
	"regexp"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/shared/pkg/constants"
)

// objectQueryStr is the regex string passed to mock.ExpectQuery for the
// _System_Object SELECT.
var objectQueryStr = regexp.QuoteMeta(fmt.Sprintf(
	"SELECT `%s`, `%s`, `%s` FROM `%s`",
	constants.ColSysObject_ID, constants.ColSysObject_APIName,
	constants.FieldSysObject_ReadSource, constants.TableObject,
))

// fieldQueryStr is the regex string passed to mock.ExpectQuery for the
// _System_Field SELECT (prefix match — the WHERE clause varies but the SELECT
// header is stable and sufficient for routing).
var fieldQueryStr = regexp.QuoteMeta(fmt.Sprintf(
	"SELECT `%s`, COALESCE(NULLIF(`%s`,''), `%s`) FROM `%s`",
	constants.ColSysField_ObjectID,
	constants.ColSysField_PhysicalName, constants.ColSysField_APIName,
	constants.TableField,
))

// TestCollectNameFieldColumns_ReturnsMapForNameFields verifies that
// collectNameFieldColumns correctly returns the column set for objects that
// have IsNameField rows, and excludes formula-active / deleted / system fields.
func TestCollectNameFieldColumns_ReturnsMapForNameFields(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	// Object table: one object "Account" (id "obj1") with no custom ReadSource.
	objRows := sqlmock.NewRows([]string{
		constants.ColSysObject_ID,
		constants.ColSysObject_APIName,
		constants.FieldSysObject_ReadSource,
	}).AddRow("obj1", "Account", nil)
	mock.ExpectQuery(objectQueryStr).WillReturnRows(objRows)

	// Field table: two matching rows for "Account" — one with PhysicalName set,
	// one using ApiName fallback.
	fieldRows := sqlmock.NewRows([]string{
		constants.ColSysField_ObjectID,
		"col", // COALESCE result
	}).
		AddRow("obj1", "Name").
		AddRow("obj1", "Title")
	mock.ExpectQuery(fieldQueryStr).WillReturnRows(fieldRows)

	got, err := collectNameFieldColumns(db)
	if err != nil {
		t.Fatalf("collectNameFieldColumns() error = %v; want nil", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unmet sqlmock expectations: %v", err)
	}

	cols, ok := got["Account"]
	if !ok {
		t.Fatalf("collectNameFieldColumns() missing table key %q; got %v", "Account", got)
	}
	if !cols["Name"] {
		t.Errorf("collectNameFieldColumns()[Account] missing col %q; got %v", "Name", cols)
	}
	if !cols["Title"] {
		t.Errorf("collectNameFieldColumns()[Account] missing col %q; got %v", "Title", cols)
	}
}

// TestCollectNameFieldColumns_EmptyWhenNoNameFields verifies that the function
// returns an empty map (not an error) when the field query returns no rows.
func TestCollectNameFieldColumns_EmptyWhenNoNameFields(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(objectQueryStr).WillReturnRows(
		sqlmock.NewRows([]string{
			constants.ColSysObject_ID,
			constants.ColSysObject_APIName,
			constants.FieldSysObject_ReadSource,
		}),
	)
	mock.ExpectQuery(fieldQueryStr).WillReturnRows(
		sqlmock.NewRows([]string{constants.ColSysField_ObjectID, "col"}),
	)

	got, err := collectNameFieldColumns(db)
	if err != nil {
		t.Fatalf("collectNameFieldColumns() error = %v; want nil", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unmet sqlmock expectations: %v", err)
	}
	if len(got) != 0 {
		t.Errorf("collectNameFieldColumns() = %v; want empty map", got)
	}
}

// TestCollectNameFieldColumns_ObjectQueryError verifies that an error on the
// _System_Object query is propagated (caller decides how to handle it).
func TestCollectNameFieldColumns_ObjectQueryError(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(objectQueryStr).WillReturnError(fmt.Errorf("db error"))

	_, err = collectNameFieldColumns(db)
	if err == nil {
		t.Fatal("collectNameFieldColumns() expected error; got nil")
	}
}
