package records

import (
	"context"
	"strings"
	"testing"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestGroupMembershipClosureMutationAffectsClosure(t *testing.T) {
	tests := []struct {
		name    string
		object  string
		kind    groupMembershipClosureMutationKind
		updates models.SObject
		want    bool
	}{
		{"group member insert", constants.TableGroupMember, groupMembershipClosureMutationInsert, nil, true},
		{"group member update", constants.TableGroupMember, groupMembershipClosureMutationUpdate, models.SObject{"Name": "ignored"}, true},
		{"group member delete", constants.TableGroupMember, groupMembershipClosureMutationDelete, nil, true},
		{"group member bulk insert", constants.TableGroupMember, groupMembershipClosureMutationBulkInsert, nil, true},
		{"group insert", constants.TableGroup, groupMembershipClosureMutationInsert, nil, true},
		{"group delete", constants.TableGroup, groupMembershipClosureMutationDelete, nil, true},
		{"group bulk insert", constants.TableGroup, groupMembershipClosureMutationBulkInsert, nil, true},
		{"group isdeleted update", constants.TableGroup, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldIsDeleted: 1}, true},
		{"group type update", constants.TableGroup, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldGroup_Type: "Role"}, true},
		{"group user role update", constants.TableGroup, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldGroup_UserRoleID: "00E-role"}, true},
		{"group unrelated update", constants.TableGroup, groupMembershipClosureMutationUpdate, models.SObject{"Name": "Engineering"}, false},
		{"user insert", constants.TableUser, groupMembershipClosureMutationInsert, nil, true},
		{"user delete", constants.TableUser, groupMembershipClosureMutationDelete, nil, true},
		{"user bulk insert", constants.TableUser, groupMembershipClosureMutationBulkInsert, nil, true},
		{"user isdeleted update case-insensitive", strings.ToLower(constants.TableUser), groupMembershipClosureMutationUpdate, models.SObject{strings.ToLower(constants.FieldIsDeleted): 1}, true},
		{"user role reassignment", constants.TableUser, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldUserRoleID: "00E-role"}, true},
		{"user isactive update", constants.TableUser, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldIsActive: false}, false},
		{"userrole insert", constants.TableUserRole, groupMembershipClosureMutationInsert, nil, true},
		{"userrole parent update", constants.TableUserRole, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldUserRole_ParentRoleID: "00E-parent"}, true},
		{"userrole soft delete update", constants.TableUserRole, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldIsDeleted: 1}, true},
		{"userrole rename", constants.TableUserRole, groupMembershipClosureMutationUpdate, models.SObject{constants.FieldName: "Sales"}, false},
		{"non-source table", "Account", groupMembershipClosureMutationInsert, nil, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := groupMembershipClosureMutationAffectsClosure(tt.object, tt.kind, tt.updates)
			if got != tt.want {
				t.Fatalf("groupMembershipClosureMutationAffectsClosure() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestPhysicalDeleteRawRejectsGroupMembershipClosureSourceTables(t *testing.T) {
	ps := &PersistenceService{}
	for _, objectName := range []string{constants.TableGroupMember, constants.TableGroup, constants.TableUser, constants.TableUserRole} {
		t.Run(objectName, func(t *testing.T) {
			err := ps.PhysicalDeleteRaw(context.Background(), objectName, "record-id")
			if err == nil {
				t.Fatal("expected PhysicalDeleteRaw to reject closure source table")
			}
			if !appErrors.IsInternal(err) {
				t.Fatalf("expected internal error, got %T: %v", err, err)
			}
		})
	}
}

type retryingClosureStateTxKey struct{}

type retryingClosureStateTxManager struct {
	identity struct{}
}

func (*retryingClosureStateTxManager) WithTransaction(ctx context.Context, fn func(context.Context) error) error {
	return fn(context.WithValue(ctx, retryingClosureStateTxKey{}, true))
}

func (*retryingClosureStateTxManager) WithPessimisticTransaction(ctx context.Context, fn func(context.Context) error) error {
	return fn(context.WithValue(ctx, retryingClosureStateTxKey{}, true))
}

func (m *retryingClosureStateTxManager) WithRetry(ctx context.Context, fn func(context.Context) error, _ int) error {
	return m.WithTransaction(ctx, fn)
}

func (*retryingClosureStateTxManager) WithPessimisticRetry(ctx context.Context, fn func(context.Context) error, _ int) error {
	if err := fn(context.WithValue(ctx, retryingClosureStateTxKey{}, true)); err == nil {
		return nil
	}
	return fn(context.WithValue(ctx, retryingClosureStateTxKey{}, true))
}

func (*retryingClosureStateTxManager) IsTransactionActive(ctx context.Context) bool {
	active, _ := ctx.Value(retryingClosureStateTxKey{}).(bool)
	return active
}

func (m *retryingClosureStateTxManager) IsPessimisticTx(ctx context.Context) bool {
	return m.IsTransactionActive(ctx)
}

func (m *retryingClosureStateTxManager) ExtractTx(ctx context.Context) interface{} {
	if !m.IsTransactionActive(ctx) {
		return nil
	}
	return &m.identity
}

func (*retryingClosureStateTxManager) DetachTx(ctx context.Context) context.Context { return ctx }
