package net.siegeln.cameleer.saas.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.siegeln.cameleer.saas.tenant.TenantService;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Map;
import java.util.UUID;

/**
 * Interceptor handling tenant resolution (JWT org_id to TenantContext)
 * and tenant isolation (path variable validation). Fail-closed: any endpoint with
 * {tenantId} in its path is automatically isolated.
 * Platform admins (SCOPE_platform:admin) bypass all isolation checks.
 */
@Component
public class TenantIsolationInterceptor implements HandlerInterceptor {

    private final TenantService tenantService;

    public TenantIsolationInterceptor(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        var authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication instanceof JwtAuthenticationToken jwtAuth)) return true;

        // Strip context-path prefix to get the application-relative path.
        // getServletPath() returns empty string in MockMvc, so use getRequestURI() minus contextPath.
        String contextPath = request.getContextPath();
        String uri = request.getRequestURI();
        String path = (contextPath != null && !contextPath.isEmpty() && uri.startsWith(contextPath))
                ? uri.substring(contextPath.length()) : uri;

        // Vendor endpoints: platform:admin already enforced by Spring Security
        if (path.startsWith("/api/vendor/")) {
            return true;
        }

        // 1. Resolve: JWT organization_id -> TenantContext (applies to all non-vendor paths)
        Jwt jwt = jwtAuth.getToken();
        String orgId = jwt.getClaimAsString("organization_id");
        if (orgId != null) {
            tenantService.getByLogtoOrgId(orgId)
                    .ifPresent(tenant -> TenantContext.setTenantId(tenant.getId()));
        }

        // Tenant portal endpoints: tenant resolved from JWT org context (no path variable)
        if (path.startsWith("/api/tenant/")) {
            if (TenantContext.getTenantId() == null) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "No organization context");
                return false;
            }
            return true;
        }

        // 2. Validate: read path variables from Spring's HandlerMapping
        @SuppressWarnings("unchecked")
        Map<String, String> pathVars = (Map<String, String>) request.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        if (pathVars == null || pathVars.isEmpty()) return true;

        UUID resolvedTenantId = TenantContext.getTenantId();
        boolean isPlatformAdmin = jwtAuth.getAuthorities().stream()
                .anyMatch(a -> a.getAuthority().equals("SCOPE_platform:admin"));

        if (isPlatformAdmin) return true;

        // Check tenantId in path (e.g., /api/tenants/{tenantId}/license)
        if (pathVars.containsKey("tenantId")) {
            if (resolvedTenantId == null) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "No organization context");
                return false;
            }
            UUID pathTenantId = UUID.fromString(pathVars.get("tenantId"));
            if (!pathTenantId.equals(resolvedTenantId)) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Tenant mismatch");
                return false;
            }
        }

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        TenantContext.clear();
    }
}
