package com.harshmallow.pilottoolkit.data

import android.content.Context
import android.util.Log
import org.json.JSONObject

/**
 * Loads aircraft and reference data from assets/ at app startup.
 * Call DataLoader.init(context) once from MainActivity.onCreate().
 *
 * File structure:
 *   assets/
 *     reference.json              ← flight levels, subgrade labels
 *     aircraft/
 *       G450.json, G500.json, ... ← one file per airframe
 *
 * Each aircraft file loads independently — a malformed file is skipped
 * with a warning rather than crashing the app.
 */
object DataLoader {

    private const val TAG = "DataLoader"

    var aircraftRegistry: List<Aircraft> = emptyList()
        private set
    var metersToFeetTable: List<FlightLevelEntry> = emptyList()
        private set
    var chinaFLAS: List<FlightLevelEntry> = emptyList()
        private set
    var chinaRVSMEastbound: Set<Int> = emptySet()
        private set
    var chinaRVSMWestbound: Set<Int> = emptySet()
        private set
    var pavementSubgrades: Map<String, String> = emptyMap()
        private set

    /** Aircraft IDs that failed to load, with error messages. */
    var loadErrors: Map<String, String> = emptyMap()
        private set

    fun init(context: Context) {
        loadReference(context)
        loadAircraft(context)
    }

    private fun loadReference(context: Context) {
        try {
            val json = context.assets.open("reference.json")
                .bufferedReader().use { it.readText() }
            val root = JSONObject(json)

            // Pavement subgrades
            root.optJSONObject("pavementSubgrades")?.let { sg ->
                pavementSubgrades = sg.keys().asSequence().associateWith { sg.getString(it) }
            }

            // Meters to feet
            root.optJSONObject("metersToFeet")?.optJSONArray("entries")?.let { arr ->
                metersToFeetTable = (0 until arr.length()).map { i ->
                    val pair = arr.getJSONArray(i)
                    FlightLevelEntry(pair.getInt(0), pair.getInt(1))
                }
            }

            // China FLAS
            root.optJSONObject("chinaFLAS")?.let { flas ->
                flas.optJSONArray("entries")?.let { arr ->
                    chinaFLAS = (0 until arr.length()).map { i ->
                        val pair = arr.getJSONArray(i)
                        FlightLevelEntry(pair.getInt(0), pair.getInt(1))
                    }
                }
                flas.optJSONArray("rvsmEastbound")?.let { arr ->
                    chinaRVSMEastbound = (0 until arr.length()).map { arr.getInt(it) }.toSet()
                }
                flas.optJSONArray("rvsmWestbound")?.let { arr ->
                    chinaRVSMWestbound = (0 until arr.length()).map { arr.getInt(it) }.toSet()
                }
            }

            Log.i(TAG, "reference.json loaded: ${metersToFeetTable.size} m/ft entries, ${chinaFLAS.size} FLAS entries")
        } catch (e: Exception) {
            Log.e(TAG, "Failed to load reference.json: ${e.message}", e)
        }
    }

    private fun loadAircraft(context: Context) {
        val aircraft = mutableListOf<Aircraft>()
        val errors = mutableMapOf<String, String>()

        // List all .json files in assets/aircraft/
        val files = try {
            context.assets.list("aircraft") ?: emptyArray()
        } catch (e: Exception) {
            Log.e(TAG, "Cannot list aircraft/ directory: ${e.message}", e)
            emptyArray()
        }

        for (filename in files.sorted()) {
            if (!filename.endsWith(".json")) continue
            val id = filename.removeSuffix(".json")
            try {
                val json = context.assets.open("aircraft/$filename")
                    .bufferedReader().use { it.readText() }
                val ac = parseAircraft(id, JSONObject(json))
                aircraft.add(ac)
                Log.i(TAG, "$filename loaded: ${ac.label}")
            } catch (e: Exception) {
                val msg = e.message ?: "Unknown error"
                errors[id] = msg
                Log.e(TAG, "Failed to load $filename: $msg", e)
            }
        }

        aircraftRegistry = aircraft
        loadErrors = errors

        if (errors.isNotEmpty()) {
            Log.w(TAG, "Aircraft load errors: ${errors.keys.joinToString()}")
        }
        Log.i(TAG, "Loaded ${aircraft.size} aircraft, ${errors.size} failed")
    }

    private fun parseAircraft(id: String, ac: JSONObject): Aircraft {
        return Aircraft(
            id = id,
            label = ac.getString("label"),
            available = ac.getBoolean("available"),
            weightLimits = ac.optJSONObject("weightLimits")?.let { wl ->
                WeightLimits(
                    minBew = wl.optInt("minBew", 0).takeIf { wl.has("minBew") },
                    mtow = wl.optInt("mtow", 0).takeIf { wl.has("mtow") },
                    maxRamp = wl.getInt("maxRamp")
                )
            },
            eswl = ac.optJSONObject("eswl")?.let { e ->
                if (e.optString("type") == "formula")
                    EswlConfig(e.getDouble("weightFactor"), e.getDouble("wheelFactor"))
                else null
            },
            eswlThresholds = ac.optJSONObject("eswl")?.let { e ->
                if (e.optString("type") == "thresholds") {
                    val sw = e.getJSONObject("singleWheel")
                    val dw = e.getJSONObject("dualWheel")
                    EswlThresholds(
                        singleWheel = EswlThresholdEntry(sw.getString("label"), sw.getInt("value"), sw.optString("unit", "lbs")),
                        dualWheel = EswlThresholdEntry(dw.getString("label"), dw.getInt("value"), dw.optString("unit", "lbs")),
                        note = e.getString("note")
                    )
                } else null
            },
            pcn = ac.optJSONObject("pcn")?.let { parsePavementFormulas(it) },
            pcr = ac.optJSONObject("pcr")?.let { parsePavementFormulas(it) },
            pcnError = ac.optString("pcnError").takeIf { it != "null" && it.isNotEmpty() },
            pcrError = ac.optString("pcrError").takeIf { it != "null" && it.isNotEmpty() },
            fuelBuckets = ac.optJSONArray("fuelBuckets")?.let { arr ->
                (0 until arr.length()).map { i ->
                    val pair = arr.getJSONArray(i)
                    FuelBucket(pair.getDouble(0), pair.getDouble(1))
                }
            }
        )
    }

    private fun parsePavementFormulas(obj: JSONObject): PavementFormulas {
        fun parseSubgrade(sub: JSONObject): SubgradeFormulas {
            fun lf(key: String): LinearFormula {
                val f = sub.getJSONObject(key)
                return LinearFormula(f.getDouble("slope"), f.getDouble("intercept"))
            }
            return SubgradeFormulas(lf("A"), lf("B"), lf("C"), lf("D"))
        }
        return PavementFormulas(
            rigid = parseSubgrade(obj.getJSONObject("rigid")),
            flexible = parseSubgrade(obj.getJSONObject("flexible"))
        )
    }
}
