package com.erp.ai.security;

import com.erp.ai.model.AiAgentApiKey;
import com.erp.ai.service.AiAgentApiKeyService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;

/**
 * /api/ai/v1/** 엔드포인트에 대한 API 키 인증 필터.
 * Authorization: Bearer sk-pipe-{hex} 형식의 키를 검증합니다.
 * SubdomainResolverFilter 뒤, JwtAuthenticationFilter 앞에 위치합니다.
 */
@Slf4j
@RequiredArgsConstructor
public class AiApiKeyAuthFilter extends OncePerRequestFilter {

    private static final String API_KEY_PREFIX = "sk-pipe-";
    private static final String AI_API_PATH_PATTERN = "/api/ai/v1/**";
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final AiAgentApiKeyService aiAgentApiKeyService;

    /**
     * /api/ai/v1/** 외 경로는 이 필터를 건너뜁니다.
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getRequestURI();
        return !PATH_MATCHER.match(AI_API_PATH_PATTERN, path);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String authHeader = request.getHeader("Authorization");

        // Authorization 헤더가 없거나 sk-pipe- 로 시작하지 않으면 JWT 필터로 패스
        if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer " + API_KEY_PREFIX)) {
            filterChain.doFilter(request, response);
            return;
        }

        // "Bearer " 제거 후 실제 키 값 추출
        String rawKey = authHeader.substring(7); // "Bearer ".length() == 7

        // 키 검증 + last_used_at 갱신 (Epic C 서비스가 SHA-256 + 만료체크 + last_used 갱신을 수행)
        AiAgentApiKey apiKey;
        try {
            apiKey = aiAgentApiKeyService.validateKey(rawKey);
        } catch (Exception e) {
            log.error("[AiApiKeyAuthFilter] Key validation failure", e);
            sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "INTERNAL_ERROR", "Internal server error");
            return;
        }

        if (apiKey == null) {
            log.warn("[AiApiKeyAuthFilter] Invalid or expired API key. prefix={}",
                    rawKey.length() > 16 ? rawKey.substring(0, 16) + "..." : rawKey);
            sendError(response, HttpServletResponse.SC_UNAUTHORIZED,
                    "INVALID_API_KEY", "Invalid or expired API key");
            return;
        }

        // 만료일 이중 검증 (서비스에서 처리하지 않을 경우 대비)
        if (apiKey.getExpires_at() != null && apiKey.getExpires_at().isBefore(OffsetDateTime.now())) {
            log.warn("[AiApiKeyAuthFilter] API key expired. keyId={}", apiKey.getId());
            sendError(response, HttpServletResponse.SC_UNAUTHORIZED,
                    "API_KEY_EXPIRED", "API key has expired");
            return;
        }

        // 월간 토큰 한도 체크
        if (apiKey.getMonthly_token_limit() != null
                && apiKey.getMonthly_token_limit() > 0
                && apiKey.getTotal_tokens() != null
                && apiKey.getTotal_tokens() >= apiKey.getMonthly_token_limit()) {
            log.warn("[AiApiKeyAuthFilter] Monthly token limit exceeded. keyId={}", apiKey.getId());
            sendError(response, 429,
                    "TOKEN_LIMIT_EXCEEDED", "Monthly token limit exceeded");
            return;
        }

        // Request attribute 설정 (JwtAuthenticationFilter 와 동일한 키 사용)
        request.setAttribute("user_id", apiKey.getUser_id());
        request.setAttribute("company_code", apiKey.getCompany_code());
        request.setAttribute("role", "API_KEY_USER");
        request.setAttribute("user_type", "API_KEY_USER");
        request.setAttribute("api_key_id", apiKey.getId());
        // AiProxyController 가 사용하는 attribute 이름과 일치시킴
        request.setAttribute("ai_api_key_id", apiKey.getId());
        request.setAttribute("ai_api_key_user", apiKey.getUser_id());

        // SecurityContext 설정
        List<SimpleGrantedAuthority> authorities = List.of(
                new SimpleGrantedAuthority("ROLE_API_KEY_USER")
        );
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(apiKey.getUser_id(), null, authorities);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        log.debug("[AiApiKeyAuthFilter] Authenticated via API key. keyId={} userId={} companyCode={}",
                apiKey.getId(), apiKey.getUser_id(), apiKey.getCompany_code());

        filterChain.doFilter(request, response);
    }

    /**
     * 401/429 JSON 오류 응답 전송.
     */
    private void sendError(HttpServletResponse response, int status, String code, String message)
            throws IOException {
        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        Map<String, Object> body = Map.of(
                "error", Map.of(
                        "code", code,
                        "message", message
                )
        );
        response.getWriter().write(OBJECT_MAPPER.writeValueAsString(body));
    }
}
