package com.magistr.app.config.tenant;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.util.Map;
import org.slf4j.MDC;
import io.opentelemetry.api.trace.Span;

/**
 * Interceptor: извлекает поддомен из Host header и кладёт в TenantContext.
 * 
 * Если тенант не настроен в TenantRoutingDataSource — 
 * сразу возвращает HTTP 404 (не допускает fallback на чужой тенант).
 *
 * Примеры:
 *   "swsu.zuev.company" → tenant = "swsu"
 *   "mgu.zuev.company"  → tenant = "mgu"
 *   "localhost"          → tenant = "default"
 *   "localhost:8080"     → tenant = "default"
 *
 * API управления тенантами (/api/database/**) пропускается без проверки,
 * чтобы администратор мог добавлять тенантов с любого домена.
 */
public class TenantInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(TenantInterceptor.class);

    private TenantRoutingDataSource routingDataSource;

    /**
     * Устанавливается после создания бина (из TenantDataSourceConfig).
     */
    public void setRoutingDataSource(TenantRoutingDataSource routingDataSource) {
        this.routingDataSource = routingDataSource;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String host = request.getHeader("Host");
        String tenant = resolveTenant(host);
        String path = request.getRequestURI();

        // API управления тенантами — всегда пропускаем
        // (нужно чтобы админ мог добавить тенант даже если его домен не настроен)
        if (path.startsWith("/api/database")) {
            TenantContext.setCurrentTenant(tenant);
            MDC.put("tenant.id", tenant);
            Span.current().setAttribute("tenant.id", tenant);
            log.debug("Запрос API управления БД: тенант='{}', строгая проверка отключена", tenant);
            return true;
        }

        // Проверяем, существует ли тенант
        if (routingDataSource != null && !routingDataSource.hasTenant(tenant)) {
            log.warn("Неизвестный тенант '{}' для заголовка Host '{}': возвращается 404", tenant, host);
            response.setStatus(404);
            response.setContentType("application/json;charset=UTF-8");
            new ObjectMapper().writeValue(response.getOutputStream(), Map.of(
                "error", "Тенант не найден",
                "tenant", tenant,
                "message", "Домен " + host + " не настроен. Обратитесь к администратору."
            ));
            return false; // Останавливаем обработку запроса
        }

        TenantContext.setCurrentTenant(tenant);
        MDC.put("tenant.id", tenant);
        Span.current().setAttribute("tenant.id", tenant);
        log.debug("Тенант '{}' определён по заголовку Host '{}'", tenant, host);
        return true;
    }

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

    private String resolveTenant(String host) {
        if (host == null || host.isBlank()) {
            return "default";
        }

        // Убираем порт (localhost:8080 → localhost)
        String hostname = host.contains(":") ? host.substring(0, host.indexOf(':')) : host;

        // localhost или IP → default
        if ("localhost".equalsIgnoreCase(hostname) || hostname.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) {
            return "default";
        }

        // Извлекаем первый поддомен: swsu.zuev.company → swsu
        int firstDot = hostname.indexOf('.');
        if (firstDot > 0) {
            return hostname.substring(0, firstDot).toLowerCase();
        }

        return "default";
    }
}
