package ai.testers;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;

public final class Sinks {

    public static List<Consumer<AnalysisResult>> build(Config cfg, Consumer<AnalysisResult> frameworkCallback) {
        List<Consumer<AnalysisResult>> out = new ArrayList<>();
        for (String name : (cfg.sink == null ? "" : cfg.sink).split(",")) {
            switch (name.trim().toLowerCase()) {
                case "disk" -> out.add(disk(cfg.sinkDir, cfg.logFormat));
                case "return" -> out.add(r -> {});
                case "framework" -> { if (frameworkCallback != null) out.add(safe(frameworkCallback)); }
                default -> {}
            }
        }
        return out;
    }

    public static AnalysisResult dispatch(AnalysisResult r, List<Consumer<AnalysisResult>> sinks) {
        for (Consumer<AnalysisResult> s : sinks) {
            try { s.accept(r); } catch (Throwable ignored) {}
        }
        return r;
    }

    private static Consumer<AnalysisResult> safe(Consumer<AnalysisResult> c) {
        return r -> { try { c.accept(r); } catch (Throwable ignored) {} };
    }

    private static Consumer<AnalysisResult> disk(String dir, String fmt) {
        return r -> {
            try {
                Path p = Paths.get(dir);
                Files.createDirectories(p);
                String name = Instant.now().toString().replace(':', '-')
                        + "-" + r.kind + "-" + UUID.randomUUID().toString().substring(0, 8)
                        + "." + ext(fmt);
                Files.writeString(p.resolve(name), format(r, fmt));
            } catch (IOException ignored) {}
        };
    }

    private static String ext(String fmt) {
        return switch (fmt == null ? "" : fmt) {
            case "junit" -> "xml";
            case "tap" -> "tap";
            case "text" -> "txt";
            default -> "json";
        };
    }

    public static String format(AnalysisResult r, String fmt) {
        return switch (fmt == null ? "json" : fmt) {
            case "text" -> {
                StringBuilder sb = new StringBuilder();
                sb.append("[testersai:").append(r.kind).append("] ok=").append(r.ok)
                  .append(" skipped=").append(r.skipped).append(" reason=").append(r.reason).append('\n');
                for (AnalysisResult.Issue i : r.issues)
                    sb.append("  [").append(i.severity).append("] ").append(i.category)
                      .append(": ").append(i.message).append('\n');
                yield sb.toString();
            }
            case "tap" -> {
                StringBuilder sb = new StringBuilder();
                int n = r.issues.isEmpty() ? 1 : r.issues.size();
                sb.append("1..").append(n).append('\n');
                if (r.issues.isEmpty()) sb.append("ok 1 - ").append(r.kind).append(" clean\n");
                else {
                    int idx = 1;
                    for (AnalysisResult.Issue i : r.issues) {
                        boolean bad = "high".equals(i.severity) || "critical".equals(i.severity);
                        sb.append(bad ? "not ok " : "ok ").append(idx++)
                          .append(" - [").append(i.severity).append("] ")
                          .append(i.category).append(": ").append(i.message).append('\n');
                    }
                }
                yield sb.toString();
            }
            case "junit" -> {
                int failures = 0;
                for (AnalysisResult.Issue i : r.issues)
                    if ("high".equals(i.severity) || "critical".equals(i.severity)) failures++;
                StringBuilder cases = new StringBuilder();
                for (AnalysisResult.Issue i : r.issues) {
                    boolean bad = "high".equals(i.severity) || "critical".equals(i.severity);
                    cases.append("<testcase classname=\"testersai.").append(r.kind)
                         .append("\" name=\"").append(xml(i.category + ":" + trunc(i.message))).append("\">");
                    if (bad) cases.append("<failure message=\"").append(xml(i.message)).append("\">")
                                  .append(xml(i.evidence == null ? "" : i.evidence)).append("</failure>");
                    cases.append("</testcase>");
                }
                yield "<testsuite name=\"testersai." + r.kind + "\" tests=\""
                        + (r.issues.isEmpty() ? 1 : r.issues.size()) + "\" failures=\"" + failures + "\">"
                        + cases + "</testsuite>";
            }
            default -> r.raw == null ? "{}" : r.raw;
        };
    }

    private static String trunc(String s) { return s.length() <= 60 ? s : s.substring(0, 60); }
    private static String xml(String s) {
        return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
                .replace("\"", "&quot;").replace("'", "&apos;");
    }
}
