package com.efsi.kinometric;

import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * ScoringConfig - Parses scoring_config.json and provides configuration values.
 *
 * This allows tuning scoring parameters without code changes - just update the JSON.
 * Matches the Dart ScoringConfig and Python scoring implementation.
 *
 * IMPORTANT: All default/fallback values match production scoring_config.json.
 */
public class ScoringConfig {
    private static final String TAG = "ScoringConfig";

    // Scoring curve
    public final double[] knotsX;
    public final double[] knotsY;

    // CSV parsing (for CSV replay)
    public final int headerRows;
    public final int qDiffColumn;
    public final int sensortimeColumn;
    public final String sensortimeUnit;

    // Analysis parameters
    public final int startRowSkip;
    public final int minSamples;
    public final int[] targetWindowsSeconds;
    public final String aggregation;
    public final int outlierPercentile;
    public final double stableCapThreshold;
    public final double stableCapScore;

    // Stability
    public final double stableThreshold;

    // Rescale (production uses "none")
    public final String rescaleType;

    // Risk thresholds
    public final int criticalBelow;
    public final int highBelow;
    public final int moderateBelow;
    public final int lowAtOrAbove;

    // Fatigue detection
    public final double fatiguesMinShortTerm;
    public final double fatiguesMinDrop;
    public final double consistentMinShortTerm;
    public final double consistentMaxDrop;
    public final double unstableMaxShortTerm;
    public final double unstableMaxDrop;

    // Duration penalty
    public final double durationPenalty3sOnly;
    public final double durationPenalty5sMax;

    // Scoring mode
    public final String scoringMode;
    public final double[] durationKnotsX;
    public final double[] durationKnotsY;

    // 7-second bonus
    public final boolean sevenSecondBonusEnabled;
    public final double maxWithout7s;
    public final double[] bonusTierMaxQdiff;
    public final int[] bonusTierScore;

    // Quality bonus (duration mode)
    public final boolean qualityBonusEnabled;
    public final double qualityBonusThresholdPct;
    public final double qualityBonusValue;

    // Test recording indicators
    public final int minDurationSeconds;
    public final double stableTargetSeconds;
    public final int checkIntervalMs;

    /**
     * Parse scoring_config.json and create ScoringConfig instance.
     * All fallback defaults match production values.
     *
     * @param jsonString The full JSON content from scoring_config.json
     * @throws JSONException if JSON is malformed
     */
    public ScoringConfig(String jsonString) throws JSONException {
        JSONObject root = new JSONObject(jsonString);

        // Parse scoring_curve
        JSONObject scoringCurve = root.getJSONObject("scoring_curve");
        JSONArray knotsXArray = scoringCurve.getJSONArray("knots_x");
        JSONArray knotsYArray = scoringCurve.getJSONArray("knots_y");

        this.knotsX = new double[knotsXArray.length()];
        this.knotsY = new double[knotsYArray.length()];

        for (int i = 0; i < knotsXArray.length(); i++) {
            this.knotsX[i] = knotsXArray.getDouble(i);
        }
        for (int i = 0; i < knotsYArray.length(); i++) {
            this.knotsY[i] = knotsYArray.getDouble(i);
        }

        // Parse csv_parsing
        JSONObject csvParsing = root.getJSONObject("csv_parsing");
        this.headerRows = csvParsing.getInt("header_rows");
        this.qDiffColumn = csvParsing.getInt("q_diff_column");
        this.sensortimeColumn = csvParsing.getInt("sensortime_column");
        this.sensortimeUnit = csvParsing.getString("sensortime_unit");

        // Parse analysis
        JSONObject analysis = root.getJSONObject("analysis");
        this.startRowSkip = analysis.getInt("start_row_skip");
        this.minSamples = analysis.getInt("min_samples");

        JSONArray targetWindowsArray = analysis.getJSONArray("target_windows_seconds");
        this.targetWindowsSeconds = new int[targetWindowsArray.length()];
        for (int i = 0; i < targetWindowsArray.length(); i++) {
            this.targetWindowsSeconds[i] = targetWindowsArray.getInt(i);
        }

        this.aggregation = analysis.getString("aggregation");
        this.outlierPercentile = analysis.getInt("outlier_percentile");
        this.stableCapThreshold = analysis.optDouble("stable_cap_threshold", 0.005);
        this.stableCapScore = analysis.optDouble("stable_cap_score", 10.0);

        // Parse stability
        JSONObject stability = root.getJSONObject("stability");
        this.stableThreshold = stability.getDouble("stable_threshold");

        // Parse rescale
        JSONObject rescale = root.getJSONObject("rescale");
        this.rescaleType = rescale.getString("type");

        // Parse risk_thresholds
        JSONObject riskThresholds = root.getJSONObject("risk_thresholds");
        this.criticalBelow = riskThresholds.getInt("critical_below");
        this.highBelow = riskThresholds.getInt("high_below");
        this.moderateBelow = riskThresholds.getInt("moderate_below");
        this.lowAtOrAbove = riskThresholds.getInt("low_at_or_above");

        // Parse fatigue
        JSONObject fatigue = root.getJSONObject("fatigue");
        this.fatiguesMinShortTerm = fatigue.getDouble("fatigues_min_short_term");
        this.fatiguesMinDrop = fatigue.getDouble("fatigues_min_drop");
        this.consistentMinShortTerm = fatigue.getDouble("consistent_min_short_term");
        this.consistentMaxDrop = fatigue.getDouble("consistent_max_drop");
        this.unstableMaxShortTerm = fatigue.getDouble("unstable_max_short_term");
        this.unstableMaxDrop = fatigue.getDouble("unstable_max_drop");

        // Parse duration_penalty — defaults match production (3, 0)
        if (root.has("duration_penalty")) {
            JSONObject dp = root.getJSONObject("duration_penalty");
            this.durationPenalty3sOnly = dp.optDouble("3_second_only", 3);
            this.durationPenalty5sMax = dp.optDouble("5_second_max", 0);
        } else {
            this.durationPenalty3sOnly = 3;
            this.durationPenalty5sMax = 0;
        }

        // Parse scoring mode
        this.scoringMode = root.optString("scoring_mode", "qdiff");

        if (root.has("duration_scoring")) {
            JSONObject ds = root.getJSONObject("duration_scoring");
            JSONArray dkx = ds.getJSONArray("knots_x");
            JSONArray dky = ds.getJSONArray("knots_y");
            this.durationKnotsX = new double[dkx.length()];
            this.durationKnotsY = new double[dky.length()];
            for (int i = 0; i < dkx.length(); i++) {
                this.durationKnotsX[i] = dkx.getDouble(i);
            }
            for (int i = 0; i < dky.length(); i++) {
                this.durationKnotsY[i] = dky.getDouble(i);
            }
        } else {
            this.durationKnotsX = new double[]{0, 1, 2, 3, 5, 7, 10};
            this.durationKnotsY = new double[]{0, 2, 3, 5, 7, 8, 10};
        }

        // Parse quality_bonus from duration_scoring section
        if (root.has("duration_scoring")) {
            JSONObject ds = root.getJSONObject("duration_scoring");
            if (ds.has("quality_bonus")) {
                JSONObject qb = ds.getJSONObject("quality_bonus");
                this.qualityBonusEnabled = qb.optBoolean("enabled", false);
                this.qualityBonusThresholdPct = qb.optDouble("threshold_pct", 0.5);
                this.qualityBonusValue = qb.optDouble("bonus", 1.0);
            } else {
                this.qualityBonusEnabled = false;
                this.qualityBonusThresholdPct = 0.5;
                this.qualityBonusValue = 1.0;
            }
        } else {
            this.qualityBonusEnabled = false;
            this.qualityBonusThresholdPct = 0.5;
            this.qualityBonusValue = 1.0;
        }

        // Parse test_recording — live indicator config
        if (root.has("test_recording")) {
            JSONObject tr = root.getJSONObject("test_recording");
            this.minDurationSeconds = tr.optInt("min_duration_seconds", 15);
            this.stableTargetSeconds = tr.optDouble("stable_target_seconds", 7.0);
            this.checkIntervalMs = tr.optInt("check_interval_ms", 500);
        } else {
            this.minDurationSeconds = 15;
            this.stableTargetSeconds = 7.0;
            this.checkIntervalMs = 500;
        }

        // Parse seven_second_bonus — defaults match production
        if (root.has("seven_second_bonus")) {
            JSONObject ssb = root.getJSONObject("seven_second_bonus");
            this.sevenSecondBonusEnabled = ssb.optBoolean("enabled", true);
            this.maxWithout7s = ssb.optDouble("max_without_7s", 7);
            if (ssb.has("tiers")) {
                JSONArray tiers = ssb.getJSONArray("tiers");
                this.bonusTierMaxQdiff = new double[tiers.length()];
                this.bonusTierScore = new int[tiers.length()];
                for (int i = 0; i < tiers.length(); i++) {
                    JSONObject tier = tiers.getJSONObject(i);
                    this.bonusTierMaxQdiff[i] = tier.getDouble("max_qdiff");
                    this.bonusTierScore[i] = tier.getInt("score");
                }
            } else {
                this.bonusTierMaxQdiff = new double[]{0.0015, 0.002, 0.003};
                this.bonusTierScore = new int[]{10, 9, 8};
            }
        } else {
            this.sevenSecondBonusEnabled = true;
            this.maxWithout7s = 7;
            this.bonusTierMaxQdiff = new double[]{0.0015, 0.002, 0.003};
            this.bonusTierScore = new int[]{10, 9, 8};
        }

        Log.d(TAG, "ScoringConfig loaded successfully");
        Log.d(TAG, "  Knots: " + knotsX.length + " points");
        Log.d(TAG, "  Windows: " + targetWindowsSeconds.length + " sizes");
        Log.d(TAG, "  Rescale type: " + rescaleType);
        Log.d(TAG, "  Duration penalty: 3s=" + durationPenalty3sOnly + " 5s=" + durationPenalty5sMax);
        Log.d(TAG, "  7s bonus: " + (sevenSecondBonusEnabled ? "enabled (cap=" + maxWithout7s + ")" : "disabled"));
    }

    /**
     * Create default config matching production scoring_config.json exactly.
     * Used as fallback if JSON loading fails.
     */
    public static ScoringConfig createDefault() {
        String defaultJson = "{"
            + "\"version\": 1,"
            + "\"scoring_curve\": {"
            + "  \"knots_x\": [0.00137, 0.003455, 0.006777, 0.012668, 0.02, 0.03],"
            + "  \"knots_y\": [10, 9, 7, 5, 4, 3]"
            + "},"
            + "\"csv_parsing\": {"
            + "  \"header_rows\": 3,"
            + "  \"q_diff_column\": 5,"
            + "  \"sensortime_column\": 7,"
            + "  \"sensortime_unit\": \"milliseconds\""
            + "},"
            + "\"analysis\": {"
            + "  \"start_row_skip\": 200,"
            + "  \"min_samples\": 300,"
            + "  \"target_windows_seconds\": [3, 5],"
            + "  \"aggregation\": \"min\","
            + "  \"outlier_percentile\": 98,"
            + "  \"stable_cap_threshold\": 0.005,"
            + "  \"stable_cap_score\": 10.0"
            + "},"
            + "\"stability\": {"
            + "  \"stable_threshold\": 0.007"
            + "},"
            + "\"rescale\": {"
            + "  \"type\": \"none\""
            + "},"
            + "\"risk_thresholds\": {"
            + "  \"critical_below\": 4,"
            + "  \"high_below\": 6,"
            + "  \"moderate_below\": 8,"
            + "  \"low_at_or_above\": 8"
            + "},"
            + "\"fatigue\": {"
            + "  \"fatigues_min_short_term\": 7,"
            + "  \"fatigues_min_drop\": 3,"
            + "  \"consistent_min_short_term\": 7,"
            + "  \"consistent_max_drop\": 3,"
            + "  \"unstable_max_short_term\": 7,"
            + "  \"unstable_max_drop\": 2"
            + "},"
            + "\"duration_penalty\": {"
            + "  \"3_second_only\": 3,"
            + "  \"5_second_max\": 0,"
            + "  \"7_second_available\": 0"
            + "},"
            + "\"seven_second_bonus\": {"
            + "  \"enabled\": true,"
            + "  \"max_without_7s\": 7,"
            + "  \"tiers\": ["
            + "    {\"max_qdiff\": 0.0015, \"score\": 10},"
            + "    {\"max_qdiff\": 0.002, \"score\": 9},"
            + "    {\"max_qdiff\": 0.003, \"score\": 8}"
            + "  ]"
            + "},"
            + "\"test_recording\": {"
            + "  \"min_duration_seconds\": 15,"
            + "  \"stable_target_seconds\": 7.0,"
            + "  \"check_interval_ms\": 500"
            + "},"
            + "\"scoring_mode\": \"qdiff\","
            + "\"duration_scoring\": {"
            + "  \"knots_x\": [0, 1, 2, 3, 5, 7, 10],"
            + "  \"knots_y\": [0, 2, 3, 5, 7, 8, 10],"
            + "  \"quality_bonus\": {\"enabled\": false, \"threshold_pct\": 0.5, \"bonus\": 1.0}"
            + "}"
            + "}";

        try {
            return new ScoringConfig(defaultJson);
        } catch (JSONException e) {
            Log.e(TAG, "Failed to create default config: " + e.getMessage());
            return null;
        }
    }
}
