package com.zioinfo.kintex.tenant;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zioinfo.kintex.common.ApiResponse;
import com.zioinfo.kintex.common.ApiResponse.ApiError;
import com.zioinfo.kintex.common.error.ErrorCode;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * 테넌트 컨텍스트 해소 필터 (PLANNING §1A-3 · §8-2 필터 계층).
 * <p>해소 우선순위: ① {@code X-Tenant-Id} 헤더 → ② Host 도메인 매칭 → ③ 기준 테넌트('kintex').
 * 명시적 {@code X-Tenant-Id}가 존재하지 않는 테넌트를 가리키면 <b>400</b>(오접속·위조 차단).
 * 그 외(헤더 없음·도메인 미매칭)는 기준 테넌트로 폴백해 기존 동작을 완전히 보존한다(회귀 0, 1단계).
 * <p>2단계에서 fail-closed(미해소 거부)·JWT 테넌트 클레임 검증으로 강화 — 설계 노트 참조.
 */
public class TenantContextFilter extends OncePerRequestFilter {

    private static final String HEADER = "X-Tenant-Id";

    private final TenantResolver resolver;
    private final ObjectMapper objectMapper;

    public TenantContextFilter(TenantResolver resolver, ObjectMapper objectMapper) {
        this.resolver = resolver;
        this.objectMapper = objectMapper;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String tenantId;
        String headerTenant = request.getHeader(HEADER);
        if (headerTenant != null && !headerTenant.isBlank()) {
            String candidate = headerTenant.trim();
            if (!resolver.exists(candidate)) {
                writeBadTenant(response);
                return;
            }
            tenantId = candidate;
        } else {
            // 도메인 매칭(§1A-3) → 폴백 기준 테넌트.
            String byDomain = resolver.resolveByDomain(request.getHeader("Host"));
            tenantId = (byDomain != null) ? byDomain : TenantContext.DEFAULT_TENANT;
        }

        try {
            TenantContext.set(tenantId);
            chain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }

    private void writeBadTenant(HttpServletResponse response) throws IOException {
        response.setStatus(ErrorCode.VALIDATION.status().value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(
                ApiResponse.fail(new ApiError(ErrorCode.VALIDATION.name(),
                        "알 수 없는 테넌트입니다."))));
    }
}
