package su.xserver.iikocon.handler;

import io.vertx.core.Handler;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.RoutingContext;
import su.xserver.iikocon.service.SettingsService;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SecurityHandler {
  private final SettingsService settings;

  public SecurityHandler(SettingsService settings) {
    this.settings = settings;
  }

  public Handler<RoutingContext> hostValidator() {
    return ctx -> settings.get("allowed_hosts").onComplete(ar -> {
      if (ar.succeeded() && ar.result() != null && !ar.result().isEmpty()) {
        String allowedHosts = ar.result();
        String requestHost = ctx.request().getHeader("Host");
        if (requestHost == null) {
          ctx.response().setStatusCode(400).end("Bad Request: Missing Host header");
          return;
        }
        String hostWithoutPort = requestHost.split(":")[0];
        Set<String> allowedSet = new HashSet<>(Arrays.asList(allowedHosts.split(",")));
        if (!allowedSet.contains(hostWithoutPort) && !allowedSet.contains(requestHost)) {
          ctx.response().setStatusCode(403).end("Forbidden: Invalid Host header");
          return;
        }
      }
      ctx.next();
    });
  }

  public Handler<RoutingContext> cspHeader() {
    return ctx -> settings.get("enable_csp").onComplete(ar -> {
      if (ar.succeeded() && "true".equals(ar.result())) {
        ctx.response().putHeader("Content-Security-Policy",
          "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'");
      }
      ctx.next();
    });
  }

  public Handler<RoutingContext> proxyHeadersHandler() {
    return ctx -> settings.get("use_proxy_headers").onComplete(useProxy -> {
      if (!useProxy.succeeded() || !"true".equals(useProxy.result())) {
        ctx.next();
        return;
      }
      settings.get("trusted_proxies").onComplete(trusted -> {
        if (!trusted.succeeded() || trusted.result() == null) {
          ctx.next();
          return;
        }
        String trustedProxies = trusted.result();
        SocketAddress remoteAddr = ctx.request().remoteAddress();
        if (remoteAddr == null) {
          ctx.next();
          return;
        }
        String clientIp = remoteAddr.host();
        if (isIpTrusted(clientIp, trustedProxies)) {
          String realIp = getRealIpFromHeaders(ctx.request());
          if (realIp != null) {
            ctx.put("realClientIp", realIp);
            ctx.put("originalClientIp", clientIp);
          }
        }
        ctx.next();
      });
    });
  }

  private boolean isIpTrusted(String ip, String trustedList) {
    String[] ips = trustedList.split(",");
    for (String trusted : ips) {
      trusted = trusted.trim();
      if (trusted.contains("/")) {
        if (ipMatchesCidr(ip, trusted)) return true;
      } else if (ip.equals(trusted)) {
        return true;
      }
    }
    return false;
  }

  private boolean ipMatchesCidr(String ip, String cidr) {
    try {
      String[] parts = cidr.split("/");
      String network = parts[0];
      int prefix = Integer.parseInt(parts[1]);
      return ip.startsWith(network.substring(0, network.lastIndexOf('.')));
    } catch (Exception e) {
      return false;
    }
  }

  private String getRealIpFromHeaders(HttpServerRequest request) {
    String xff = request.getHeader("X-Forwarded-For");
    if (xff != null && !xff.isEmpty()) {
      return xff.split(",")[0].trim();
    }
    String xri = request.getHeader("X-Real-IP");
    if (xri != null && !xri.isEmpty()) {
      return xri;
    }
    return null;
  }
}
