package net.siegeln.cameleer.saas.portal;

import com.fasterxml.jackson.databind.ObjectMapper;
import net.siegeln.cameleer.saas.TestcontainersConfig;
import net.siegeln.cameleer.saas.TestSecurityConfig;
import net.siegeln.cameleer.saas.license.LicenseService;
import net.siegeln.cameleer.saas.tenant.TenantEntity;
import net.siegeln.cameleer.saas.tenant.TenantRepository;
import net.siegeln.cameleer.saas.tenant.TenantStatus;
import net.siegeln.cameleer.saas.tenant.Tier;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import java.time.Duration;
import java.util.UUID;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@Import({TestcontainersConfig.class, TestSecurityConfig.class})
@ActiveProfiles("test")
class TenantPortalControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private TenantRepository tenantRepository;

    @Autowired
    private LicenseService licenseService;

    /**
     * Creates a tenant directly in the DB with the given logtoOrgId so the
     * TenantIsolationInterceptor can resolve it from the JWT organization_id claim.
     * Each test uses a unique orgId to avoid cross-test interference.
     */
    private TenantEntity createTenantWithOrgId(String name, String slug, String orgId) {
        var tenant = new TenantEntity();
        tenant.setName(name);
        tenant.setSlug(slug);
        tenant.setTier(Tier.LOW);
        tenant.setStatus(TenantStatus.ACTIVE);
        tenant.setLogtoOrgId(orgId);
        return tenantRepository.save(tenant);
    }

    @Test
    void dashboard_returnsDashboardData() throws Exception {
        String orgId = "test-org-dashboard-" + System.nanoTime();
        createTenantWithOrgId("Portal Org", "portal-dashboard-" + System.nanoTime(), orgId);

        mockMvc.perform(get("/api/tenant/dashboard")
                        .with(jwt().jwt(j -> j
                                .claim("sub", "test-user")
                                .claim("organization_id", orgId)
                                .claim("scope", "tenant:manage"))
                                .authorities(new SimpleGrantedAuthority("SCOPE_tenant:manage"))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").isNotEmpty())
                .andExpect(jsonPath("$.slug").isNotEmpty())
                .andExpect(jsonPath("$.tier").isNotEmpty())
                .andExpect(jsonPath("$.status").isNotEmpty());
    }

    @Test
    void license_returnsLicenseData() throws Exception {
        String orgId = "test-org-license-" + System.nanoTime();
        TenantEntity tenant = createTenantWithOrgId("License Portal Org", "portal-license-" + System.nanoTime(), orgId);
        licenseService.generateLicense(tenant, Duration.ofDays(365), UUID.randomUUID());

        mockMvc.perform(get("/api/tenant/license")
                        .with(jwt().jwt(j -> j
                                .claim("sub", "test-user")
                                .claim("organization_id", orgId)
                                .claim("scope", "tenant:manage"))
                                .authorities(new SimpleGrantedAuthority("SCOPE_tenant:manage"))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.tier").isNotEmpty())
                .andExpect(jsonPath("$.token").isNotEmpty());
    }

    @Test
    void license_returns404WhenNone() throws Exception {
        String orgId = "test-org-nolicense-" + System.nanoTime();
        createTenantWithOrgId("No License Org", "portal-nolicense-" + System.nanoTime(), orgId);

        mockMvc.perform(get("/api/tenant/license")
                        .with(jwt().jwt(j -> j
                                .claim("sub", "test-user")
                                .claim("organization_id", orgId)
                                .claim("scope", "tenant:manage"))
                                .authorities(new SimpleGrantedAuthority("SCOPE_tenant:manage"))))
                .andExpect(status().isNotFound());
    }

    @Test
    void settings_returnsSettings() throws Exception {
        String orgId = "test-org-settings-" + System.nanoTime();
        createTenantWithOrgId("Settings Org", "portal-settings-" + System.nanoTime(), orgId);

        mockMvc.perform(get("/api/tenant/settings")
                        .with(jwt().jwt(j -> j
                                .claim("sub", "test-user")
                                .claim("organization_id", orgId)
                                .claim("scope", "settings:manage"))
                                .authorities(new SimpleGrantedAuthority("SCOPE_settings:manage"))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").isNotEmpty())
                .andExpect(jsonPath("$.slug").isNotEmpty())
                .andExpect(jsonPath("$.tier").isNotEmpty())
                .andExpect(jsonPath("$.status").isNotEmpty());
    }

    @Test
    void dashboard_returns403WithoutOrgContext() throws Exception {
        mockMvc.perform(get("/api/tenant/dashboard")
                        .with(jwt().jwt(j -> j
                                .claim("sub", "test-user")
                                .claim("scope", "tenant:manage"))
                                .authorities(new SimpleGrantedAuthority("SCOPE_tenant:manage"))))
                .andExpect(status().isForbidden());
    }
}
