package com.magistr.app.config.auth;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.magistr.app.model.Role;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    private final JwtTokenService jwtTokenService;
    private final ObjectMapper objectMapper;

    public AuthorizationInterceptor(JwtTokenService jwtTokenService, ObjectMapper objectMapper) {
        this.jwtTokenService = jwtTokenService;
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        AuthContext.clear();

        if (!request.getRequestURI().startsWith("/api/") || "OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }
        if (isPublicAuthEndpoint(request)) {
            return true;
        }

        String token = bearerToken(request.getHeader("Authorization"));
        AuthenticatedUser user = jwtTokenService.authenticate(
                token,
                com.magistr.app.config.tenant.TenantContext.getCurrentTenant()
        ).orElse(null);
        if (user == null) {
            writeError(response, HttpServletResponse.SC_UNAUTHORIZED, "Требуется вход в систему");
            return false;
        }

        RequireRoles roles = resolveRoles(handler);
        if (roles != null && Arrays.stream(roles.value()).noneMatch(role -> role == user.role())) {
            writeError(response, HttpServletResponse.SC_FORBIDDEN, "Недостаточно прав для выполнения операции");
            return false;
        }

        AuthContext.setCurrentUser(user);
        return true;
    }

    private boolean isPublicAuthEndpoint(HttpServletRequest request) {
        if (!request.getRequestURI().startsWith("/api/auth/")) {
            return false;
        }
        return "POST".equalsIgnoreCase(request.getMethod())
                && (request.getRequestURI().equals("/api/auth/login")
                || request.getRequestURI().equals("/api/auth/refresh")
                || request.getRequestURI().equals("/api/auth/logout"));
    }

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

    private RequireRoles resolveRoles(Object handler) {
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return null;
        }
        RequireRoles methodRoles = handlerMethod.getMethodAnnotation(RequireRoles.class);
        if (methodRoles != null) {
            return methodRoles;
        }
        return handlerMethod.getBeanType().getAnnotation(RequireRoles.class);
    }

    private String bearerToken(String header) {
        if (header == null || !header.startsWith("Bearer ")) {
            return null;
        }
        return header.substring("Bearer ".length()).trim();
    }

    private void writeError(HttpServletResponse response, int status, String message) throws IOException {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");
        objectMapper.writeValue(response.getOutputStream(), Map.of("message", message));
    }
}
