package com.mosquito.project.web;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;

@Component
public class UrlValidator {

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

    private static final String[] ALLOWED_SCHEMES = {"http", "https"};

    public boolean isAllowedUrl(String url) {
        if (url == null || url.isBlank()) {
            log.warn("URL is null or blank");
            return false;
        }

        try {
            URI uri = new URI(url);

            if (!uri.isAbsolute()) {
                log.warn("URL is not absolute: {}", url);
                return false;
            }

            String scheme = uri.getScheme().toLowerCase();
            if (!isAllowedScheme(scheme)) {
                log.warn("URL scheme '{}' is not allowed: {}", scheme, url);
                return false;
            }

            if (!isAllowedHost(uri.getHost())) {
                log.warn("URL host is not allowed: {}", uri.getHost());
                return false;
            }

            return true;
        } catch (URISyntaxException e) {
            log.warn("Invalid URL syntax: {} - {}", url, e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("Error validating URL: {} - {}", url, e.getMessage(), e);
            return false;
        }
    }

    private boolean isAllowedScheme(String scheme) {
        for (String allowed : ALLOWED_SCHEMES) {
            if (allowed.equals(scheme)) {
                return true;
            }
        }
        return false;
    }

    private boolean isAllowedHost(String host) {
        if (host == null || host.isBlank()) {
            return false;
        }

        try {
            InetAddress address = InetAddress.getByName(host);

            if (address.isLoopbackAddress()) {
                log.warn("Host is loopback address: {}", host);
                return false;
            }

            if (address.isSiteLocalAddress()) {
                log.warn("Host is site-local (internal) address: {}", host);
                return false;
            }

            if (address.isAnyLocalAddress()) {
                log.warn("Host is any-local address: {}", host);
                return false;
            }

            if (address.isLinkLocalAddress()) {
                log.warn("Host is link-local address: {}", host);
                return false;
            }

            if (address.isMulticastAddress()) {
                log.warn("Host is multicast address: {}", host);
                return false;
            }

            String hostLower = host.toLowerCase();
            if (hostLower.equals("localhost") || hostLower.equals("127.0.0.1") ||
                hostLower.equals("::1") || hostLower.equals("0.0.0.0")) {
                log.warn("Host is localhost variant: {}", host);
                return false;
            }

            if (isPrivateIpRange(address)) {
                log.warn("Host is in private IP range: {}", host);
                return false;
            }

            return true;
        } catch (Exception e) {
            log.error("Error resolving host: {} - {}", host, e.getMessage());
            return false;
        }
    }

    private boolean isPrivateIpRange(InetAddress address) {
        byte[] addr = address.getAddress();

        if (addr.length != 4) {
            return false;
        }

        int first = addr[0] & 0xFF;
        int second = addr[1] & 0xFF;

        if (first == 10) {
            return true;
        }

        if (first == 172 && second >= 16 && second <= 31) {
            return true;
        }

        if (first == 192 && second == 168) {
            return true;
        }

        return false;
    }

    public String sanitizeUrl(String url) {
        if (!isAllowedUrl(url)) {
            return null;
        }
        try {
            URI uri = new URI(url);
            return uri.toString();
        } catch (URISyntaxException e) {
            return null;
        }
    }
}
