package ai.testers;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/** Minimal JSON encoder + a tiny extractor for issues arrays. Zero deps. */
final class Json {

    static String obj(Map<String, ?> map) {
        StringBuilder sb = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<String, ?> e : map.entrySet()) {
            if (!first) sb.append(',');
            first = false;
            sb.append(quote(e.getKey())).append(':').append(value(e.getValue()));
        }
        return sb.append('}').toString();
    }

    @SuppressWarnings("unchecked")
    private static String value(Object v) {
        if (v == null) return "null";
        if (v instanceof Boolean || v instanceof Number) return v.toString();
        if (v instanceof Map<?, ?>) return obj((Map<String, ?>) v);
        if (v instanceof Iterable<?>) {
            StringBuilder sb = new StringBuilder("[");
            Iterator<?> it = ((Iterable<?>) v).iterator();
            boolean first = true;
            while (it.hasNext()) { if (!first) sb.append(','); first = false; sb.append(value(it.next())); }
            return sb.append(']').toString();
        }
        if (v.getClass().isArray()) {
            StringBuilder sb = new StringBuilder("[");
            int n = java.lang.reflect.Array.getLength(v);
            for (int i = 0; i < n; i++) { if (i > 0) sb.append(','); sb.append(value(java.lang.reflect.Array.get(v, i))); }
            return sb.append(']').toString();
        }
        return quote(v.toString());
    }

    static String quote(String s) {
        StringBuilder sb = new StringBuilder(s.length() + 2).append('"');
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '"': sb.append("\\\""); break;
                case '\\': sb.append("\\\\"); break;
                case '\n': sb.append("\\n"); break;
                case '\r': sb.append("\\r"); break;
                case '\t': sb.append("\\t"); break;
                default:
                    if (c < 0x20) sb.append(String.format("\\u%04x", (int) c));
                    else sb.append(c);
            }
        }
        return sb.append('"').toString();
    }

    // ---- minimal extractors for parsing server responses.
    @SuppressWarnings("unchecked")
    static List<Object> getArray(String json, String key) {
        // Delegates to a tiny parser below to avoid a dependency.
        Object parsed = Parser.parse(json);
        if (parsed instanceof Map<?, ?> m) {
            Object v = ((Map<String, Object>) m).get(key);
            if (v instanceof List<?>) return (List<Object>) v;
        }
        return List.of();
    }

    @SuppressWarnings("unchecked")
    static String s(Map<?, ?> m, String key, String def) {
        Object v = m.get(key);
        return v == null ? def : v.toString();
    }

    // ---- Tiny JSON parser (subset: objects, arrays, strings, numbers, bools, null).
    static final class Parser {
        private final String s; private int i;
        private Parser(String s) { this.s = s; }
        static Object parse(String s) { try { return new Parser(s.trim()).value(); } catch (Exception e) { return null; } }
        private Object value() {
            skip();
            char c = s.charAt(i);
            if (c == '{') return obj();
            if (c == '[') return arr();
            if (c == '"') return str();
            if (c == 't' || c == 'f') return bool();
            if (c == 'n') { i += 4; return null; }
            return num();
        }
        private void skip() { while (i < s.length() && Character.isWhitespace(s.charAt(i))) i++; }
        private Map<String, Object> obj() {
            java.util.LinkedHashMap<String, Object> m = new java.util.LinkedHashMap<>();
            i++; skip();
            if (s.charAt(i) == '}') { i++; return m; }
            while (true) {
                skip();
                String k = str();
                skip(); i++; // :
                m.put(k, value());
                skip();
                if (s.charAt(i) == ',') { i++; continue; }
                i++; return m;
            }
        }
        private List<Object> arr() {
            List<Object> a = new ArrayList<>();
            i++; skip();
            if (s.charAt(i) == ']') { i++; return a; }
            while (true) {
                a.add(value()); skip();
                if (s.charAt(i) == ',') { i++; skip(); continue; }
                i++; return a;
            }
        }
        private String str() {
            StringBuilder sb = new StringBuilder();
            i++; // "
            while (s.charAt(i) != '"') {
                char c = s.charAt(i++);
                if (c == '\\') {
                    char e = s.charAt(i++);
                    switch (e) {
                        case '"': sb.append('"'); break;
                        case '\\': sb.append('\\'); break;
                        case '/': sb.append('/'); break;
                        case 'n': sb.append('\n'); break;
                        case 'r': sb.append('\r'); break;
                        case 't': sb.append('\t'); break;
                        case 'u': sb.append((char) Integer.parseInt(s.substring(i, i + 4), 16)); i += 4; break;
                        default: sb.append(e);
                    }
                } else sb.append(c);
            }
            i++; return sb.toString();
        }
        private Object bool() {
            if (s.charAt(i) == 't') { i += 4; return Boolean.TRUE; }
            i += 5; return Boolean.FALSE;
        }
        private Object num() {
            int start = i;
            while (i < s.length() && "-+0123456789.eE".indexOf(s.charAt(i)) >= 0) i++;
            String t = s.substring(start, i);
            if (t.contains(".") || t.contains("e") || t.contains("E")) return Double.parseDouble(t);
            return Long.parseLong(t);
        }
    }
}
