package com.saas.shared.audit;

import com.saas.shared.core.TenantContext;
import com.saas.shared.security.JwtTokenProvider;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class AuditAspect {
    
    private final AuditService auditService;
    private final JwtTokenProvider jwtTokenProvider;
    
    @Around("@annotation(auditable)")
    public Object audit(ProceedingJoinPoint joinPoint, Auditable auditable) throws Throwable {
        Object result = joinPoint.proceed();
        
        try {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            String userId = auth != null && auth.isAuthenticated() ? auth.getName() : "ANONYMOUS";
            
            ServletRequestAttributes attributes = 
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
            
            // Extract tenant from JWT token (survives filter cleanup)
            String tenantId = extractTenantIdFromRequest(request);
            
            String entityId = extractEntityId(result);
            
            auditService.log(
                userId,
                tenantId,
                auditable.action(),
                auditable.entityType(),
                entityId,
                result,
                request
            );
        } catch (Exception e) {
            log.error("Failed to create audit log", e);
        }
        
        return result;
    }
    
    /**
     * Extract tenant ID from JWT token in request header.
     * This survives TenantIdentifierFilter cleanup, ensuring correct tenant attribution.
     */
    private String extractTenantIdFromRequest(HttpServletRequest request) {
        if (request == null) {
            return "SYSTEM";
        }
        
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            try {
                String token = authHeader.substring(7);
                String schemaName = jwtTokenProvider.getSchemaNameFromToken(token);
                
                if (schemaName != null && !schemaName.isEmpty() && !"saas_db".equals(schemaName)) {
                    return schemaName; // Return tenant schema for attribution
                }
            } catch (Exception e) {
                log.debug("Could not extract tenant from JWT for audit: {}", e.getMessage());
            }
        }
        
        return "SYSTEM"; // Default for admin/system operations
    }
    
    private String extractEntityId(Object result) {
        if (result == null) {
            return null;
        }
        
        try {
            if (result.getClass().getMethod("getId") != null) {
                Object id = result.getClass().getMethod("getId").invoke(result);
                return id != null ? id.toString() : null;
            }
        } catch (Exception ignored) {
        }
        
        return null;
    }
}
