package net.siegeln.cameleer.saas.tenant;

import net.siegeln.cameleer.saas.audit.AuditAction;
import net.siegeln.cameleer.saas.audit.AuditService;
import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class TenantServiceTest {

    @Mock
    private TenantRepository tenantRepository;

    @Mock
    private AuditService auditService;

    @Mock
    private LogtoManagementClient logtoClient;

    private TenantService tenantService;

    @BeforeEach
    void setUp() {
        tenantService = new TenantService(tenantRepository, auditService, logtoClient);
    }

    @Test
    void create_savesNewTenantWithCorrectFields() {
        var request = new CreateTenantRequest("Acme Corp", "acme-corp", "MID", null, null);
        var actorId = UUID.randomUUID();

        when(tenantRepository.existsBySlugAndStatusNot("acme-corp", TenantStatus.DELETED)).thenReturn(false);
        when(tenantRepository.save(any(TenantEntity.class))).thenAnswer(inv -> inv.getArgument(0));

        var result = tenantService.create(request, actorId);

        assertThat(result.getName()).isEqualTo("Acme Corp");
        assertThat(result.getSlug()).isEqualTo("acme-corp");
        assertThat(result.getTier()).isEqualTo(Tier.MID);
        assertThat(result.getStatus()).isEqualTo(TenantStatus.PROVISIONING);
    }

    @Test
    void create_throwsForDuplicateSlug() {
        var request = new CreateTenantRequest("Acme Corp", "acme-corp", null, null, null);

        when(tenantRepository.existsBySlugAndStatusNot("acme-corp", TenantStatus.DELETED)).thenReturn(true);

        assertThatThrownBy(() -> tenantService.create(request, UUID.randomUUID()))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Slug already taken");
    }

    @Test
    void create_logsAuditEvent() {
        var request = new CreateTenantRequest("Acme Corp", "acme-corp", null, null, null);
        var actorId = UUID.randomUUID();

        when(tenantRepository.existsBySlugAndStatusNot("acme-corp", TenantStatus.DELETED)).thenReturn(false);
        when(tenantRepository.save(any(TenantEntity.class))).thenAnswer(inv -> inv.getArgument(0));

        tenantService.create(request, actorId);

        var actionCaptor = ArgumentCaptor.forClass(AuditAction.class);
        verify(auditService).log(any(), any(), any(), actionCaptor.capture(), any(), any(), any(), any(), any());
        assertThat(actionCaptor.getValue()).isEqualTo(AuditAction.TENANT_CREATE);
    }

    @Test
    void create_defaultsToLowTier() {
        var request = new CreateTenantRequest("Acme Corp", "acme-corp", null, null, null);

        when(tenantRepository.existsBySlugAndStatusNot("acme-corp", TenantStatus.DELETED)).thenReturn(false);
        when(tenantRepository.save(any(TenantEntity.class))).thenAnswer(inv -> inv.getArgument(0));

        var result = tenantService.create(request, UUID.randomUUID());

        assertThat(result.getTier()).isEqualTo(Tier.LOW);
    }

    @Test
    void getById_returnsTenant() {
        var id = UUID.randomUUID();
        var entity = new TenantEntity();
        entity.setName("Test");
        entity.setSlug("test");

        when(tenantRepository.findById(id)).thenReturn(Optional.of(entity));

        var result = tenantService.getById(id);

        assertThat(result).isPresent();
        assertThat(result.get().getName()).isEqualTo("Test");
    }

    @Test
    void getBySlug_returnsTenant() {
        var entity = new TenantEntity();
        entity.setName("Test");
        entity.setSlug("test");

        when(tenantRepository.findBySlug("test")).thenReturn(Optional.of(entity));

        var result = tenantService.getBySlug("test");

        assertThat(result).isPresent();
        assertThat(result.get().getSlug()).isEqualTo("test");
    }
}
