package bootstrapimpl

import (
	"errors"
	"regexp"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

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

	mock.ExpectQuery(regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ReadSource` FROM `_System_Object`")).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ReadSource"}).
			AddRow("object-org", "Org__c", nil))
	mock.ExpectQuery("FROM `_System_Field`").
		WillReturnRows(sqlmock.NewRows([]string{"ObjectId", "PhysicalName", "FormulaMode", "FormulaStorageMode", "Formula", "ProjectionConfig"}).
			AddRow("object-org", "ID__c", "", "", "", "").
			AddRow("object-org", "", "", "", "", ""))

	columns, err := collectUniqueFieldColumns(db)
	if err != nil {
		t.Fatalf("collectUniqueFieldColumns: %v", err)
	}
	if !columns["Org__c"]["ID__c"] {
		t.Fatalf("unique columns = %#v, want Org__c.ID__c", columns)
	}
	if len(columns["Org__c"]) != 1 {
		t.Fatalf("unique columns = %#v, want empty physical names excluded", columns)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}

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

	mock.ExpectQuery(regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ReadSource` FROM `_System_Object`")).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ReadSource"}).
			AddRow("object-org", "Org__c", nil))
	mock.ExpectQuery("FROM `_System_Field`").
		WillReturnRows(sqlmock.NewRows([]string{"ObjectId", "PhysicalName", "FormulaMode", "FormulaStorageMode", "Formula", "ProjectionConfig"}).
			AddRow("object-org", "StoredFormula__c", "active", "write_materialized", "TEXT(ID__c)", "").
			AddRow("object-org", "ViewFormula__c", "active", "", "TEXT(ID__c)", "").
			AddRow("object-org", "ProjectedFormula__c", "active", "write_materialized", "TEXT(ID__c)", `{"source":"Org__c"}`))

	columns, err := collectUniqueFieldColumns(db)
	if err != nil {
		t.Fatalf("collectUniqueFieldColumns: %v", err)
	}
	if !columns["Org__c"]["StoredFormula__c"] {
		t.Fatalf("unique columns = %#v, want persisted active formula", columns)
	}
	if columns["Org__c"]["ViewFormula__c"] || columns["Org__c"]["ProjectedFormula__c"] {
		t.Fatalf("unique columns = %#v, want view-backed formulas excluded", columns)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectQuery(regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ReadSource` FROM `_System_Object`")).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ReadSource"}).
			AddRow("object-org", "Org__c", nil))
	mock.ExpectQuery("FROM `_System_Field`").
		WillReturnRows(sqlmock.NewRows([]string{"ObjectId", "PhysicalName", "FormulaMode", "FormulaStorageMode", "Formula", "ProjectionConfig"}).
			AddRow("object-org", "StoredFormula__c", "active", "not-a-storage-mode", "TEXT(ID__c)", ""))

	_, err = collectUniqueFieldColumns(db)
	if err == nil || !regexp.MustCompile(`resolve formula storage for unique field StoredFormula__c: invalid formulaStorageMode`).MatchString(err.Error()) {
		t.Fatalf("collectUniqueFieldColumns error = %v, want invalid formula storage error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectQuery("(?s)SELECT COUNT\\(\\*\\).*FROM INFORMATION_SCHEMA.STATISTICS").
		WithArgs("Org__c", "ID__c").
		WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(0))
	mock.ExpectExec(regexp.QuoteMeta("ALTER TABLE `Org__c` ADD UNIQUE INDEX `uniq_Org__c_ID__c` (`ID__c`)")).
		WillReturnError(errors.New("ddl rejected"))

	_, err = executeStandardIndexJobs(db, []standardIndexJob{{
		table:     "Org__c",
		indexName: "uniq_Org__c_ID__c",
		cols:      []string{"ID__c"},
		unique:    true,
	}}, 1)
	if err == nil || !regexp.MustCompile(`ensure unique index uniq_Org__c_ID__c on Org__c: ddl rejected`).MatchString(err.Error()) {
		t.Fatalf("executeStandardIndexJobs error = %v, want propagated DDL failure", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}

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

	mock.ExpectExec(regexp.QuoteMeta("CREATE INDEX `idx_org_search` ON `Org__c` (`Description__c`)")).
		WillReturnError(errors.New("key length too long"))

	created, err := executeStandardIndexJobs(db, []standardIndexJob{{
		table:     "Org__c",
		indexName: "idx_org_search",
		cols:      []string{"Description__c"},
	}}, 1)
	if err != nil {
		t.Fatalf("executeStandardIndexJobs error = %v, want non-fatal standard index failure", err)
	}
	if created != 0 {
		t.Fatalf("created = %d, want 0", created)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectQuery("(?s)SELECT COUNT\\(\\*\\).*FROM INFORMATION_SCHEMA.STATISTICS").
		WithArgs("Org__c", "ID__c").
		WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(0))
	mock.ExpectExec(regexp.QuoteMeta("ALTER TABLE `Org__c` ADD UNIQUE INDEX `uniq_Org__c_ID__c` (`ID__c`)")).
		WillReturnResult(sqlmock.NewResult(0, 1))

	created, err := executeStandardIndexJobs(db, []standardIndexJob{{
		table:     "Org__c",
		indexName: "uniq_Org__c_ID__c",
		cols:      []string{"ID__c"},
		unique:    true,
	}}, 1)
	if err != nil {
		t.Fatalf("executeStandardIndexJobs: %v", err)
	}
	if created != 1 {
		t.Fatalf("created = %d, want 1", created)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}
