package net.siegeln.cameleer.saas.portal;

import net.siegeln.cameleer.saas.config.TenantContext;
import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
import net.siegeln.cameleer.saas.identity.ServerApiClient;
import net.siegeln.cameleer.saas.identity.ServerApiClient.ServerHealthResponse;
import net.siegeln.cameleer.saas.license.LicenseEntity;
import net.siegeln.cameleer.saas.license.LicenseService;
import net.siegeln.cameleer.saas.tenant.TenantEntity;
import net.siegeln.cameleer.saas.tenant.TenantService;
import net.siegeln.cameleer.saas.tenant.TenantStatus;
import net.siegeln.cameleer.saas.tenant.Tier;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class TenantPortalServiceTest {

    @Mock
    private TenantService tenantService;

    @Mock
    private LicenseService licenseService;

    @Mock
    private ServerApiClient serverApiClient;

    @Mock
    private LogtoManagementClient logtoClient;

    private TenantPortalService tenantPortalService;

    private final UUID tenantId = UUID.randomUUID();

    @BeforeEach
    void setUp() {
        TenantContext.setTenantId(tenantId);
        tenantPortalService = new TenantPortalService(tenantService, licenseService, serverApiClient, logtoClient);
    }

    @AfterEach
    void tearDown() {
        TenantContext.clear();
    }

    // --- Helpers ---

    private TenantEntity tenantWithId(String name, String slug, Tier tier, TenantStatus status) throws Exception {
        var tenant = new TenantEntity();
        tenant.setName(name);
        tenant.setSlug(slug);
        tenant.setTier(tier);
        tenant.setStatus(status);
        var f = TenantEntity.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(tenant, tenantId);
        return tenant;
    }

    private LicenseEntity licenseWithId(UUID tid, String tier, Instant expiresAt) throws Exception {
        var license = new LicenseEntity();
        license.setTenantId(tid);
        license.setTier(tier);
        license.setToken("test-token-" + UUID.randomUUID());
        license.setIssuedAt(Instant.now());
        license.setExpiresAt(expiresAt);
        license.setFeatures(Map.of("feature1", true));
        license.setLimits(Map.of("maxApps", 10));
        var f = LicenseEntity.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(license, UUID.randomUUID());
        return license;
    }

    // --- getDashboard tests ---

    @Test
    void getDashboard_returnsDashboardData() throws Exception {
        var tenant = tenantWithId("Acme Corp", "acme-corp", Tier.LOW, TenantStatus.ACTIVE);
        tenant.setServerEndpoint("http://server:8080");
        var expiresAt = Instant.now().plus(Duration.ofDays(30));
        var license = licenseWithId(tenantId, "LOW", expiresAt);

        when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));
        when(serverApiClient.getHealth("http://server:8080")).thenReturn(new ServerHealthResponse(true, "UP"));
        when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license));

        var result = tenantPortalService.getDashboard();

        assertThat(result.name()).isEqualTo("Acme Corp");
        assertThat(result.slug()).isEqualTo("acme-corp");
        assertThat(result.tier()).isEqualTo("LOW");
        assertThat(result.status()).isEqualTo("ACTIVE");
        assertThat(result.serverHealthy()).isTrue();
        assertThat(result.serverStatus()).isEqualTo("UP");
        assertThat(result.serverEndpoint()).isEqualTo("http://server:8080");
        assertThat(result.licenseTier()).isEqualTo("LOW");
        assertThat(result.licenseDaysRemaining()).isGreaterThanOrEqualTo(29);
        assertThat(result.limits()).isNotEmpty();
        assertThat(result.features()).isNotEmpty();
    }

    @Test
    void getDashboard_handlesNoServer() throws Exception {
        var tenant = tenantWithId("Acme Corp", "acme-corp", Tier.LOW, TenantStatus.PROVISIONING);
        // serverEndpoint is null by default

        when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));
        when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.empty());

        var result = tenantPortalService.getDashboard();

        assertThat(result.serverHealthy()).isFalse();
        assertThat(result.serverStatus()).isEqualTo("NO_ENDPOINT");
        assertThat(result.serverEndpoint()).isNull();
        assertThat(result.licenseTier()).isNull();
        assertThat(result.licenseDaysRemaining()).isZero();
    }

    // --- getLicense tests ---

    @Test
    void getLicense_returnsLicenseData() throws Exception {
        var tenant = tenantWithId("Acme Corp", "acme-corp", Tier.LOW, TenantStatus.ACTIVE);
        var expiresAt = Instant.now().plus(Duration.ofDays(60));
        var license = licenseWithId(tenantId, "LOW", expiresAt);

        when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));
        when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license));

        var result = tenantPortalService.getLicense();

        assertThat(result).isNotNull();
        assertThat(result.id()).isEqualTo(license.getId());
        assertThat(result.tier()).isEqualTo("LOW");
        assertThat(result.token()).isEqualTo(license.getToken());
        assertThat(result.issuedAt()).isEqualTo(license.getIssuedAt());
        assertThat(result.expiresAt()).isEqualTo(expiresAt);
        assertThat(result.daysRemaining()).isGreaterThanOrEqualTo(59);
        assertThat(result.features()).isEqualTo(Map.of("feature1", true));
        assertThat(result.limits()).isEqualTo(Map.of("maxApps", 10));
    }

    @Test
    void getLicense_returnsNullWhenNoLicense() throws Exception {
        var tenant = tenantWithId("Acme Corp", "acme-corp", Tier.LOW, TenantStatus.ACTIVE);

        when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));
        when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.empty());

        var result = tenantPortalService.getLicense();

        assertThat(result).isNull();
    }

    // --- getSettings tests ---

    @Test
    void getSettings_returnsSettingsData() throws Exception {
        var tenant = tenantWithId("Acme Corp", "acme-corp", Tier.MID, TenantStatus.ACTIVE);
        tenant.setServerEndpoint("http://server:8080");

        when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));

        var result = tenantPortalService.getSettings();

        assertThat(result.name()).isEqualTo("Acme Corp");
        assertThat(result.slug()).isEqualTo("acme-corp");
        assertThat(result.tier()).isEqualTo("MID");
        assertThat(result.status()).isEqualTo("ACTIVE");
        assertThat(result.serverEndpoint()).isEqualTo("http://server:8080");
        assertThat(result.createdAt()).isNull(); // no @PrePersist called in test, createdAt is null
    }
}
