package hr.com.port.ips.eracun.helper;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class UrlLogSanitizer {
	
	// 1) Imaš gotov URL kao string
	//String safeUrl = UrlSanitizer.toSafeUrl("https://demo.moj-eracun.hr/api/fiscalization/status?username=12270&password=dRAnSore&companyId=69566178525&softwareId=Test-002&electronicId=0&messageType=0");
	//logger.debug("MER getFiscalizationStatus URL: " + safeUrl);

	// 2) S dodatnim ključevima (npr. maskiraj i companyId)
	//java.util.Set<String> extra = new java.util.HashSet<>();
	//extra.add("companyid");
	//String safeUrl2 = UrlSanitizer.toSafeUrl(urlString, extra);
	//logger.debug("MER URL (safe): " + safeUrl2);

	// 3) Ako već imaš URI
	//URI uri = new URI(urlString);
	//logger.debug("MER URL (safe): " + UrlSanitizer.toSafeUrl(uri));

    private UrlLogSanitizer() {}

    private static final String UTF_8 = "UTF-8";
    private static final String MASK = "***";

    // Pokriva tipične osjetljive ključeve (case-insensitive)
    private static final Set<String> DEFAULT_SENSITIVE_KEYS = new HashSet<>(Arrays.asList(
            "password", "pass", "pwd", "lozinka",
            "token", "authorization", "auth",
            "apikey", "api_key",
            "secret"
    ));

    // Fallback regex (ako URL nije validan URI): key=value → key=***
    private static final Pattern FALLBACK_QUERY_MASK =
            Pattern.compile("(?i)(password|pass|pwd|lozinka|token|authorization|auth|apikey|api_key|secret)=([^&#]*)");

    // Sanitizira kompletan URL string (maskira userinfo i query parametre).
    public static String toSafeUrl(String rawUrl) {
        return toSafeUrl(rawUrl, Collections.<String>emptySet());
    }

    // Sanitizira URL, uz dodatne osjetljive ključeve.
    public static String toSafeUrl(String rawUrl, Set<String> extraSensitiveKeys) {
        if (rawUrl == null) return null;
        try {
            URI uri = new URI(rawUrl);

            String scheme   = uri.getScheme();
            String host     = uri.getHost();
            int    port     = uri.getPort();
            String path     = uri.getRawPath();     // raw → zadržava enkodiranje
            String fragment = uri.getRawFragment();

            // Ukloni userinfo (user:pass@host) ako postoji
            String userInfo = null;

            // Sanitiziraj query
            String safeQuery = sanitizeQuery(uri.getRawQuery(), extraSensitiveKeys);

            // Ponovno složi URI (authority iz komponenti)
            URI safe = new URI(scheme, userInfo, host, port, path, safeQuery, fragment);
            return safe.toString();

        } catch (URISyntaxException use) {
            // Fallback: naivno zamaskiraj poznate parametre u stringu
            return naiveMask(rawUrl);
        }
    }

    // Sanitizira već parsiran URI.
    public static String toSafeUrl(URI uri) {
        return toSafeUrl(uri, Collections.<String>emptySet());
    }

    public static String toSafeUrl(URI uri, Set<String> extraSensitiveKeys) {
        if (uri == null) return null;
        try {
            String scheme   = uri.getScheme();
            String host     = uri.getHost();
            int    port     = uri.getPort();
            String path     = uri.getRawPath();
            String fragment = uri.getRawFragment();

            String safeQuery = sanitizeQuery(uri.getRawQuery(), extraSensitiveKeys);

            URI safe = new URI(scheme, null, host, port, path, safeQuery, fragment);
            return safe.toString();
        } catch (URISyntaxException e) {
            return naiveMask(uri.toString());
        }
    }

    // ---- helpers ----

    private static String sanitizeQuery(String rawQuery, Set<String> extraSensitiveKeys) {
        if (rawQuery == null || rawQuery.isEmpty()) return null;

        Map<String, List<String>> params = parseQuery(rawQuery);
        Map<String, List<String>> out = new LinkedHashMap<>();

        for (Map.Entry<String, List<String>> e : params.entrySet()) {
            String key = e.getKey();
            List<String> vals = e.getValue();

            boolean sensitive = isSensitiveKey(key, extraSensitiveKeys);
            List<String> safeVals = new ArrayList<>(vals.size());
            for (String v : vals) {
                safeVals.add(sensitive ? MASK : v);
            }
            out.put(key, safeVals);
        }
        return buildQuery(out);
    }

    private static Map<String, List<String>> parseQuery(String rawQuery) {
        Map<String, List<String>> map = new LinkedHashMap<>();
        String[] pairs = rawQuery.split("&");
        for (String p : pairs) {
            String[] kv = p.split("=", 2);
            String k = urlDecode(kv[0]);
            String v = (kv.length > 1) ? urlDecode(kv[1]) : "";
            map.computeIfAbsent(k, __ -> new ArrayList<>()).add(v);
        }
        return map;
    }

    private static String buildQuery(Map<String, List<String>> params) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, List<String>> e : params.entrySet()) {
            String k = urlEncode(e.getKey());
            for (String v : e.getValue()) {
                if (!first) sb.append('&');
                first = false;
                sb.append(k);
                sb.append('=');
                sb.append(urlEncode(v));
            }
        }
        return sb.toString();
    }

    private static boolean isSensitiveKey(String key, Set<String> extra) {
        if (key == null) return false;
        String k = key.toLowerCase(Locale.ROOT);
        if (DEFAULT_SENSITIVE_KEYS.contains(k)) return true;
        if (extra != null && extra.contains(k)) return true;
        // Heuristike na sufikse
        return k.endsWith("password") || k.endsWith("token") || k.endsWith("secret");
    }

    private static String urlDecode(String s) {
        try {
            return URLDecoder.decode(s, UTF_8);
        } catch (UnsupportedEncodingException e) {
            return s;
        }
    }

    private static String urlEncode(String s) {
        try {
            // URLEncoder koristi '+' za razmake; pretvori u %20 radi čitljivosti
            return URLEncoder.encode(s, UTF_8).replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            return s;
        }
    }

    private static String naiveMask(String raw) {
        if (raw == null) return null;
        // 1) Makni eventualni user:pass@
        String withoutUserInfo = raw.replaceFirst("(?i)://[^/@:]+:[^/@]*@", "://");

        // 2) Zamaskiraj poznate query parametre
        Matcher m = FALLBACK_QUERY_MASK.matcher(withoutUserInfo);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, m.group(1) + "=" + MASK);
        }
        m.appendTail(sb);
        return sb.toString();
    }
}