package de.avatic.lcc.e2e.util;

import de.avatic.lcc.e2e.testdata.TestCaseExpected;

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

/**
 * Collects and reports deviations between expected and actual values.
 */
public class DeviationReport {

    private final List<TestCaseDeviation> deviations = new ArrayList<>();

    public void addTestCase(String testCaseId, String testCaseName, TestCaseExpected expected, Map<String, Object> actual) {
        TestCaseDeviation deviation = new TestCaseDeviation(testCaseId, testCaseName);

        deviation.addField("MEK_A", expected.mekA(), (Double) actual.get("mekA"));
        deviation.addField("LOGISTIC_COST", expected.logisticCost(), (Double) actual.get("logisticCost"));
        deviation.addField("MEK_B", expected.mekB(), (Double) actual.get("mekB"));
        deviation.addField("FCA_FEE", expected.fcaFee(), (Double) actual.get("fcaFee"));
        deviation.addField("TRANSPORTATION", expected.transportation(), (Double) actual.get("transportation"));
        deviation.addField("D2D", expected.d2d(), (Double) actual.get("d2d"));
        deviation.addField("AIR_FREIGHT", expected.airFreight(), (Double) actual.get("airFreight"));
        deviation.addField("CUSTOM", expected.custom(), (Double) actual.get("custom"));
        deviation.addField("REPACKAGING", expected.repackaging(), (Double) actual.get("repackaging"));
        deviation.addField("HANDLING", expected.handling(), (Double) actual.get("handling"));
        deviation.addField("DISPOSAL", expected.disposal(), (Double) actual.get("disposal"));
        deviation.addField("SPACE", expected.space(), (Double) actual.get("space"));
        deviation.addField("CAPITAL", expected.capital(), (Double) actual.get("capital"));

        deviations.add(deviation);
    }

    public void addError(String testCaseId, String testCaseName, String errorMessage) {
        TestCaseDeviation deviation = new TestCaseDeviation(testCaseId, testCaseName);
        deviation.setError(errorMessage);
        deviations.add(deviation);
    }

    public String generateMarkdownTable() {
        StringBuilder sb = new StringBuilder();

        sb.append("\n\n");
        sb.append("# DEVIATION REPORT\n");
        sb.append("================================================================================\n\n");

        // Summary table per test case
        sb.append("## Summary by Test Case\n\n");
        sb.append("| Test | Name | Status | Max Deviation |\n");
        sb.append("|------|------|--------|---------------|\n");

        for (TestCaseDeviation dev : deviations) {
            if (dev.hasError()) {
                sb.append(String.format("| %s | %s | ERROR | %s |\n",
                    dev.testCaseId, truncate(dev.testCaseName, 30), dev.errorMessage));
            } else {
                double maxDev = dev.getMaxDeviation();
                String status = maxDev > 5.0 ? "⚠️ HIGH" : (maxDev > 1.0 ? "⚡ MEDIUM" : "✓ OK");
                sb.append(String.format("| %s | %s | %s | %.2f%% |\n",
                    dev.testCaseId, truncate(dev.testCaseName, 30), status, maxDev));
            }
        }

        // Detailed deviations per field
        sb.append("\n\n## Detailed Field Deviations\n\n");
        sb.append("| Test | Field | Expected | Actual | Deviation |\n");
        sb.append("|------|-------|----------|--------|----------|\n");

        for (TestCaseDeviation dev : deviations) {
            if (dev.hasError()) {
                sb.append(String.format("| %s | ERROR | - | - | %s |\n", dev.testCaseId, dev.errorMessage));
            } else {
                for (FieldDeviation field : dev.fields) {
                    if (field.deviationPercent > 1.0 || field.actual == null) {
                        sb.append(String.format("| %s | %s | %.4f | %s | %.2f%% |\n",
                            dev.testCaseId,
                            field.fieldName,
                            field.expected,
                            field.actual != null ? String.format("%.4f", field.actual) : "null",
                            field.deviationPercent));
                    }
                }
            }
        }

        // Field summary - which fields have issues across all tests
        sb.append("\n\n## Field Summary (Average Deviation Across All Tests)\n\n");
        sb.append("| Field | Avg Deviation | Max Deviation | Tests with >1% |\n");
        sb.append("|-------|---------------|---------------|----------------|\n");

        String[] fieldNames = {"MEK_A", "LOGISTIC_COST", "MEK_B", "FCA_FEE", "TRANSPORTATION",
            "D2D", "AIR_FREIGHT", "CUSTOM", "REPACKAGING", "HANDLING", "DISPOSAL", "SPACE", "CAPITAL"};

        for (String fieldName : fieldNames) {
            double sumDev = 0;
            double maxDev = 0;
            int countHigh = 0;
            int count = 0;

            for (TestCaseDeviation dev : deviations) {
                if (!dev.hasError()) {
                    for (FieldDeviation field : dev.fields) {
                        if (field.fieldName.equals(fieldName)) {
                            sumDev += field.deviationPercent;
                            maxDev = Math.max(maxDev, field.deviationPercent);
                            if (field.deviationPercent > 1.0) countHigh++;
                            count++;
                        }
                    }
                }
            }

            if (count > 0) {
                double avgDev = sumDev / count;
                sb.append(String.format("| %s | %.2f%% | %.2f%% | %d/%d |\n",
                    fieldName, avgDev, maxDev, countHigh, count));
            }
        }

        sb.append("\n================================================================================\n");

        return sb.toString();
    }

    private String truncate(String s, int maxLen) {
        return s.length() > maxLen ? s.substring(0, maxLen - 3) + "..." : s;
    }

    public static class TestCaseDeviation {
        String testCaseId;
        String testCaseName;
        List<FieldDeviation> fields = new ArrayList<>();
        String errorMessage;

        public TestCaseDeviation(String testCaseId, String testCaseName) {
            this.testCaseId = testCaseId;
            this.testCaseName = testCaseName;
        }

        public void addField(String fieldName, double expected, Double actual) {
            fields.add(new FieldDeviation(fieldName, expected, actual));
        }

        public void setError(String errorMessage) {
            this.errorMessage = errorMessage;
        }

        public boolean hasError() {
            return errorMessage != null;
        }

        public double getMaxDeviation() {
            return fields.stream()
                .mapToDouble(f -> f.deviationPercent)
                .max()
                .orElse(0.0);
        }
    }

    public static class FieldDeviation {
        String fieldName;
        double expected;
        Double actual;
        double deviationPercent;

        public FieldDeviation(String fieldName, double expected, Double actual) {
            this.fieldName = fieldName;
            this.expected = expected;
            this.actual = actual;

            if (actual == null) {
                // Null actual - if expected is ~0, no deviation; otherwise 100%
                this.deviationPercent = Math.abs(expected) < 0.001 ? 0.0 : 100.0;
            } else {
                double diff = Math.abs(expected - actual);
                this.deviationPercent = expected != 0 ? (diff / Math.abs(expected)) * 100 : diff * 100;
            }
        }
    }
}
