package packs

import (
	"context"
	"regexp"
	"testing"

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

// ---------------------------------------------------------------------------
// Direction-isolation tests for legacy field_mapping paths (Codex R3 BLOCK 2)
//
// These tests verify that the legacy field_mapping verify and soft-delete paths
// filter AND Direction='legacy' so connector rows sharing the same owner+tuple
// are never read nondeterministically and never soft-deleted as "stale" by a
// legacy apply pass.
// ---------------------------------------------------------------------------

// TestVerifyFieldMappings_IgnoresConnectorRows asserts that
// VerifyFieldMappingGroupsForOwner reads only rows with Direction='legacy' and
// does not confuse a connector row (Direction='inbound') for the same
// owner+tuple+sourceField as the expected legacy row.
func TestVerifyFieldMappings_IgnoresConnectorRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	// Use insertInheritFromLookup ActionType to skip the create-from-source guard
	// coverage check (that check is orthogonal to the Direction filter being tested).
	groups := []FieldMappingGroup{
		{
			SourceObject: "Contact",
			TargetObject: "Contact",
			ActionType:   "insertInheritFromLookup",
			Mappings: []FieldMappingDef{
				{SourceField: "Email", TargetField: "Email"},
			},
		},
	}

	// The verify query for the individual row must use Direction='legacy'.
	// Return the correctly typed legacy row.
	legacyCols := []string{
		"TargetField", "DefaultValue", "SourceLookupField", "ValueMap", "OnUnmappedValue",
		"SortOrder", "IsCustomized", "IsActive", "IsDeleted",
	}
	mock.ExpectQuery(regexp.QuoteMeta("AND Direction = 'legacy'")).
		WithArgs("pack-a", "Contact", "Contact", "insertInheritFromLookup", "Email").
		WillReturnRows(sqlmock.NewRows(legacyCols).
			AddRow("Email", nil, nil, nil, nil, 0, false, true, false))

	// The tuple stale-row scan must also use Direction='legacy'.
	scanCols := []string{"SourceField"}
	mock.ExpectQuery(regexp.QuoteMeta("AND Direction = 'legacy'")).
		WithArgs("pack-a", "Contact", "Contact", "insertInheritFromLookup").
		WillReturnRows(sqlmock.NewRows(scanCols).AddRow("Email"))

	// The create-from-source guard check is skipped for insertInheritFromLookup — no extra queries.
	if err := VerifyFieldMappingGroupsForOwner(context.Background(), db, groups, "pack-a"); err != nil {
		t.Fatalf("VerifyFieldMappingGroupsForOwner should pass for a clean legacy row: %v", err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations unmet: %v", err)
	}
}

// TestApplyLegacyFieldMappings_DoesNotDeleteConnectorRows asserts that
// reconcileTupleSoftDelete issues an UPDATE filtered by AND Direction='legacy',
// so connector rows for the same owner+tuple are never soft-deleted as "stale"
// during a legacy field_mapping apply pass.
func TestApplyLegacyFieldMappings_DoesNotDeleteConnectorRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	// reconcileTupleSoftDelete with a keepSourceFields list: the UPDATE must
	// contain AND Direction='legacy' so connector rows are not touched.
	mock.ExpectExec(regexp.QuoteMeta("AND Direction = 'legacy'")).
		WillReturnResult(sqlmock.NewResult(0, 0))

	if err := reconcileTupleSoftDelete(
		context.Background(), db,
		"Contact", "Contact", "insertInheritFromLookup",
		"pack-a",
		[]string{"Email"}, // keep Email; any other legacy row would be pruned
		"2026-01-01 00:00:00",
	); err != nil {
		t.Fatalf("reconcileTupleSoftDelete: %v", err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations unmet: %v", err)
	}
}
