package service

import (
	"baron-sso-backend/internal/domain"
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"gorm.io/gorm"
)

func TestTenantService_RegisterTenant_DuplicateSlug(t *testing.T) {
	mockRepo := new(MockTenantRepoForSvc)
	svc := NewTenantService(mockRepo, nil, nil, nil)

	ctx := context.Background()
	slug := "duplicate-slug"

	// Mock: slug already exists
	mockRepo.On("FindBySlug", ctx, slug).Return(&domain.Tenant{ID: "existing-id", Slug: slug}, nil)

	tenant, err := svc.RegisterTenant(ctx, "New Name", slug, domain.TenantTypeCompany, "", nil, nil, "")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "already exists")
	assert.Nil(t, tenant)
}

func TestTenantService_RegisterTenant_InvalidSlug(t *testing.T) {
	svc := NewTenantService(nil, nil, nil, nil)
	ctx := context.Background()

	// Case 1: Too short
	_, err := svc.RegisterTenant(ctx, "Name", "a", domain.TenantTypeCompany, "", nil, nil, "")
	assert.Error(t, err)

	// Case 2: Invalid characters
	_, err = svc.RegisterTenant(ctx, "Name", "Invalid Slug!", domain.TenantTypeCompany, "", nil, nil, "")
	assert.Error(t, err)
}

func TestTenantService_RequestRegistration_EmailMismatch(t *testing.T) {
	svc := NewTenantService(nil, nil, nil, nil)
	ctx := context.Background()

	// admin email domain (gmail.com) != tenant domain (company.com)
	tenant, err := svc.RequestRegistration(ctx, "Name", "slug", "", "company.com", "admin@gmail.com")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "must match")
	assert.Nil(t, tenant)
}

func TestTenantService_ApproveTenant_NotFound(t *testing.T) {
	mockRepo := new(MockTenantRepoForSvc)
	svc := NewTenantService(mockRepo, nil, nil, nil)

	ctx := context.Background()
	id := "non-existent-id"

	mockRepo.On("FindByID", ctx, id).Return(nil, gorm.ErrRecordNotFound)

	err := svc.ApproveTenant(ctx, id)
	assert.Error(t, err)
	assert.True(t, errors.Is(err, gorm.ErrRecordNotFound))
}

func TestTenantService_GetTenantByDomain_Inactive(t *testing.T) {
	mockRepo := new(MockTenantRepoForSvc)
	svc := NewTenantService(mockRepo, nil, nil, nil)

	ctx := context.Background()
	domainName := "inactive.com"

	mockRepo.On("FindByDomain", ctx, domainName).Return(&domain.Tenant{
		ID:     "t1",
		Status: domain.TenantStatusPending,
	}, nil)

	tenant, err := svc.GetTenantByDomain(ctx, domainName)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "not active")
	assert.Nil(t, tenant)
}

func TestTenantService_ApproveTenant_UserNotFound(t *testing.T) {
	mockRepo := new(MockTenantRepoForSvc)
	mockUserRepo := new(MockUserRepoForTenant)
	mockOutbox := new(MockKetoOutboxRepositoryShared)

	svc := NewTenantService(mockRepo, mockUserRepo, nil, mockOutbox)
	ctx := context.Background()
	tenantID := "t1"
	adminEmail := "notfound@tenant.com"

	tenant := &domain.Tenant{
		ID:     tenantID,
		Slug:   "tenant-slug",
		Config: domain.JSONMap{"adminEmail": adminEmail},
	}

	mockRepo.On("FindByID", ctx, tenantID).Return(tenant, nil)
	mockRepo.On("Update", ctx, mock.Anything).Return(nil)
	// User not found in DB
	mockUserRepo.On("FindByEmail", adminEmail).Return(nil, gorm.ErrRecordNotFound)

	// Outbox should not be called since user is not found

	err := svc.ApproveTenant(ctx, tenantID)
	assert.NoError(t, err) // Should succeed but just log that user is not found
	mockRepo.AssertExpectations(t)
	mockUserRepo.AssertExpectations(t)
	mockOutbox.AssertNotCalled(t, "Create")
}
