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.license.LicenseEntity;
import net.siegeln.cameleer.saas.license.LicenseService;
import net.siegeln.cameleer.saas.tenant.TenantEntity;
import net.siegeln.cameleer.saas.tenant.TenantService;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class TenantPortalService {

    private final TenantService tenantService;
    private final LicenseService licenseService;
    private final ServerApiClient serverApiClient;
    private final LogtoManagementClient logtoClient;

    public TenantPortalService(TenantService tenantService,
                               LicenseService licenseService,
                               ServerApiClient serverApiClient,
                               LogtoManagementClient logtoClient) {
        this.tenantService = tenantService;
        this.licenseService = licenseService;
        this.serverApiClient = serverApiClient;
        this.logtoClient = logtoClient;
    }

    // --- Inner records ---

    public record DashboardData(
            String name, String slug, String tier, String status,
            boolean serverHealthy, String serverStatus, String serverEndpoint,
            String licenseTier, long licenseDaysRemaining,
            Map<String, Object> limits, Map<String, Object> features
    ) {}

    public record LicenseData(
            UUID id, String tier, Map<String, Object> features, Map<String, Object> limits,
            Instant issuedAt, Instant expiresAt, String token, long daysRemaining
    ) {}

    public record TenantSettingsData(
            String name, String slug, String tier, String status,
            String serverEndpoint, Instant createdAt
    ) {}

    // --- Helpers ---

    private TenantEntity resolveTenant() {
        UUID tenantId = TenantContext.getTenantId();
        return tenantService.getById(tenantId)
                .orElseThrow(() -> new IllegalStateException("Tenant not found: " + tenantId));
    }

    private long daysUntil(Instant instant) {
        if (instant == null) return 0;
        long days = ChronoUnit.DAYS.between(Instant.now(), instant);
        return Math.max(0, days);
    }

    // --- Service methods ---

    public DashboardData getDashboard() {
        TenantEntity tenant = resolveTenant();
        String endpoint = tenant.getServerEndpoint();

        boolean serverHealthy = false;
        String serverStatus = "NO_ENDPOINT";
        if (endpoint != null && !endpoint.isBlank()) {
            var health = serverApiClient.getHealth(endpoint);
            serverHealthy = health.healthy();
            serverStatus = health.status();
        }

        String licenseTier = null;
        long licenseDaysRemaining = 0;
        Map<String, Object> limits = Map.of();
        Map<String, Object> features = Map.of();

        var licenseOpt = licenseService.getActiveLicense(tenant.getId());
        if (licenseOpt.isPresent()) {
            LicenseEntity lic = licenseOpt.get();
            licenseTier = lic.getTier();
            licenseDaysRemaining = daysUntil(lic.getExpiresAt());
            limits = lic.getLimits() != null ? lic.getLimits() : Map.of();
            features = lic.getFeatures() != null ? lic.getFeatures() : Map.of();
        }

        return new DashboardData(
                tenant.getName(), tenant.getSlug(),
                tenant.getTier().name(), tenant.getStatus().name(),
                serverHealthy, serverStatus, endpoint,
                licenseTier, licenseDaysRemaining,
                limits, features
        );
    }

    public LicenseData getLicense() {
        TenantEntity tenant = resolveTenant();
        return licenseService.getActiveLicense(tenant.getId())
                .map(lic -> new LicenseData(
                        lic.getId(), lic.getTier(),
                        lic.getFeatures() != null ? lic.getFeatures() : Map.of(),
                        lic.getLimits() != null ? lic.getLimits() : Map.of(),
                        lic.getIssuedAt(), lic.getExpiresAt(),
                        lic.getToken(), daysUntil(lic.getExpiresAt())
                ))
                .orElse(null);
    }

    public Map<String, Object> getOidcConfig() {
        TenantEntity tenant = resolveTenant();
        String endpoint = tenant.getServerEndpoint();
        if (endpoint == null || endpoint.isBlank()) {
            return Map.of();
        }
        return serverApiClient.getOidcConfig(endpoint);
    }

    public void updateOidcConfig(Map<String, Object> config) {
        TenantEntity tenant = resolveTenant();
        String endpoint = tenant.getServerEndpoint();
        if (endpoint == null || endpoint.isBlank()) {
            throw new IllegalStateException("Tenant has no server endpoint configured");
        }
        serverApiClient.pushOidcConfig(endpoint, config);
    }

    public List<Map<String, Object>> listTeamMembers() {
        TenantEntity tenant = resolveTenant();
        String orgId = tenant.getLogtoOrgId();
        if (orgId == null || orgId.isBlank()) {
            return List.of();
        }
        return logtoClient.listOrganizationMembers(orgId);
    }

    public String inviteTeamMember(String email, String roleId) {
        TenantEntity tenant = resolveTenant();
        String orgId = tenant.getLogtoOrgId();
        if (orgId == null || orgId.isBlank()) {
            throw new IllegalStateException("Tenant has no Logto organization configured");
        }
        return logtoClient.createAndInviteUser(email, orgId, roleId);
    }

    public void removeTeamMember(String userId) {
        TenantEntity tenant = resolveTenant();
        String orgId = tenant.getLogtoOrgId();
        if (orgId == null || orgId.isBlank()) {
            throw new IllegalStateException("Tenant has no Logto organization configured");
        }
        logtoClient.removeUserFromOrganization(orgId, userId);
    }

    public void changeTeamMemberRole(String userId, String roleId) {
        TenantEntity tenant = resolveTenant();
        String orgId = tenant.getLogtoOrgId();
        if (orgId == null || orgId.isBlank()) {
            throw new IllegalStateException("Tenant has no Logto organization configured");
        }
        logtoClient.assignOrganizationRole(orgId, userId, roleId);
    }

    public TenantSettingsData getSettings() {
        TenantEntity tenant = resolveTenant();
        return new TenantSettingsData(
                tenant.getName(), tenant.getSlug(),
                tenant.getTier().name(), tenant.getStatus().name(),
                tenant.getServerEndpoint(), tenant.getCreatedAt()
        );
    }
}
