package app.voltplan.cable.ui.bom

import android.content.Context
import app.voltplan.cable.R
import app.voltplan.cable.affiliate.AmazonAffiliate
import app.voltplan.cable.calc.ElectricalCalculations
import app.voltplan.cable.data.UnitSystem
import app.voltplan.cable.data.model.SavedBattery
import app.voltplan.cable.data.model.SavedCharger
import app.voltplan.cable.data.model.SavedLoad
import app.voltplan.cable.data.model.chemistry
import app.voltplan.cable.data.model.effectivePowerWatts
import app.voltplan.cable.data.model.usableCapacityAmpHours
import java.util.Locale
import kotlin.math.roundToInt

enum class BomCategory(val titleRes: Int, val subtitleRes: Int) {
    COMPONENTS(R.string.bom_category_components_title, R.string.bom_category_components_subtitle),
    BATTERIES(R.string.bom_category_batteries_title, R.string.bom_category_batteries_subtitle),
    CABLES(R.string.bom_category_cables_title, R.string.bom_category_cables_subtitle),
    FUSES(R.string.bom_category_fuses_title, R.string.bom_category_fuses_subtitle),
    ACCESSORIES(R.string.bom_category_accessories_title, R.string.bom_category_accessories_subtitle),
}

sealed interface BomDestination {
    data class Affiliate(val url: String, val country: String?) : BomDestination
    data class AmazonSearch(val query: String, val country: String?) : BomDestination
}

/** A single, possibly-merged, line in the bill of materials. */
data class BomItem(
    val id: String,
    val title: String,
    val detail: String,
    val symbol: String,
    val category: BomCategory,
    val quantity: Int,
    val isPrimary: Boolean,
    val destination: BomDestination,
    val metricText: String,
    /** (componentId, logicalId) pairs whose completion this item represents. */
    val storageKeys: List<Pair<String, String>>,
)

data class BomSection(val category: BomCategory, val items: List<BomItem>)

object BomBuilder {

    fun crossSectionLabel(load: SavedLoad, unit: UnitSystem): String {
        val cs = ElectricalCalculations.recommendedCrossSection(load.length, load.current, load.voltage, unit)
        return if (unit == UnitSystem.METRIC) String.format(Locale.US, "%.1f mm²", cs)
        else "AWG ${ElectricalCalculations.formatAWG(cs)}"
    }

    private fun gaugeQuery(load: SavedLoad, unit: UnitSystem): String {
        val cs = ElectricalCalculations.recommendedCrossSection(load.length, load.current, load.voltage, unit)
        return if (unit == UnitSystem.METRIC) String.format(Locale.US, "%.1f mm²", cs)
        else "AWG ${ElectricalCalculations.formatAWG(cs)}"
    }

    fun build(
        context: Context,
        loads: List<SavedLoad>,
        batteries: List<SavedBattery>,
        chargers: List<SavedCharger>,
        unit: UnitSystem,
    ): List<BomSection> {
        val items = mutableListOf<BomItem>()
        val country: String? = java.util.Locale.getDefault().country.ifBlank { null }

        // Loads → component / cables / fuse / terminals
        for (load in loads.filter { it.length > 0 && it.crossSection > 0 }) {
            val name = load.name.ifBlank { context.getString(R.string.component_fallback_name) }
            val gauge = crossSectionLabel(load, unit)
            val lengthDisplay = LoadLength.display(load.length, unit)
            val fuse = ElectricalCalculations.recommendedFuse(load.current).roundToInt()

            // Component
            val compDest: BomDestination = load.affiliateURLString?.let { BomDestination.Affiliate(it, load.affiliateCountryCode) }
                ?: BomDestination.AmazonSearch(load.name.ifBlank { context.getString(R.string.bom_search_device_fallback, load.power, load.voltage) }, country)
            items += BomItem(
                id = "${load.id}::component",
                title = name,
                detail = String.format(Locale.US, "%.0f W @ %.1f V", load.power, load.voltage),
                symbol = "bolt.fill",
                category = BomCategory.COMPONENTS,
                quantity = 1,
                isPrimary = true,
                destination = compDest,
                metricText = "1×",
                storageKeys = listOf(load.id to "component"),
            )
            // Cables
            items += cableItem(context, load, "cable-red", R.string.bom_item_cable_red, R.string.bom_search_cable_red, gauge, lengthDisplay, unit, country)
            items += cableItem(context, load, "cable-black", R.string.bom_item_cable_black, R.string.bom_search_cable_black, gauge, lengthDisplay, unit, country)
            // Fuse
            items += BomItem(
                id = "${load.id}::fuse",
                title = context.getString(R.string.bom_item_fuse),
                detail = context.getString(R.string.bom_fuse_detail, fuse),
                symbol = "bolt.square",
                category = BomCategory.FUSES,
                quantity = 1,
                isPrimary = false,
                destination = BomDestination.AmazonSearch(context.getString(R.string.bom_search_fuse, fuse), country),
                metricText = "1× · ${fuse} A",
                storageKeys = listOf(load.id to "fuse"),
            )
            // Terminals
            items += BomItem(
                id = "${load.id}::terminals",
                title = context.getString(R.string.bom_item_terminals),
                detail = context.getString(R.string.bom_terminals_detail, gauge),
                symbol = "wrench.adjustable",
                category = BomCategory.ACCESSORIES,
                quantity = 4,
                isPrimary = false,
                destination = BomDestination.AmazonSearch(context.getString(R.string.bom_search_terminals, gauge), country),
                metricText = "4× · $gauge",
                storageKeys = listOf(load.id to "terminals"),
            )
        }

        // Batteries
        for (b in batteries) {
            val name = b.name.ifBlank { context.getString(R.string.component_fallback_name) }
            val dest: BomDestination = b.affiliateURLString?.let { BomDestination.Affiliate(it, b.affiliateCountryCode) }
                ?: BomDestination.AmazonSearch(
                    context.getString(R.string.bom_search_battery, b.capacityAmpHours.roundToInt(), b.nominalVoltage.roundToInt(), b.chemistry.displayName), country,
                )
            items += BomItem(
                id = "${b.id}::battery",
                title = name,
                detail = String.format(Locale.US, "%.0f Ah @ %.1f V • %s • %.0f Ah", b.capacityAmpHours, b.nominalVoltage, b.chemistry.displayName, b.usableCapacityAmpHours),
                symbol = b.iconName,
                category = BomCategory.BATTERIES,
                quantity = 1,
                isPrimary = true,
                destination = dest,
                metricText = "1×",
                storageKeys = listOf(b.id to "battery"),
            )
        }

        // Chargers
        for (c in chargers) {
            val name = c.name.ifBlank { context.getString(R.string.component_fallback_name) }
            val dest: BomDestination = c.affiliateURLString?.let { BomDestination.Affiliate(it, c.affiliateCountryCode) }
                ?: BomDestination.AmazonSearch(
                    context.getString(R.string.bom_search_charger, c.outputVoltage.roundToInt(), c.maxCurrentAmps.roundToInt()), country,
                )
            items += BomItem(
                id = "${c.id}::charger",
                title = name,
                detail = String.format(Locale.US, "%.0f W • %.0f A @ %.1f V • %.0f V AC", c.effectivePowerWatts, c.maxCurrentAmps, c.outputVoltage, c.inputVoltage),
                symbol = c.iconName,
                category = BomCategory.COMPONENTS,
                quantity = 1,
                isPrimary = true,
                destination = dest,
                metricText = "1×",
                storageKeys = listOf(c.id to "charger"),
            )
        }

        return BomCategory.entries.mapNotNull { cat ->
            val catItems = items.filter { it.category == cat }
            if (catItems.isEmpty()) null else BomSection(cat, catItems)
        }
    }

    private fun cableItem(
        context: Context,
        load: SavedLoad,
        logical: String,
        titleRes: Int,
        searchRes: Int,
        gauge: String,
        lengthDisplay: String,
        unit: UnitSystem,
        country: String?,
    ) = BomItem(
        id = "${load.id}::$logical",
        title = context.getString(titleRes),
        detail = "$lengthDisplay • $gauge",
        symbol = "bolt.horizontal.circle",
        category = BomCategory.CABLES,
        quantity = 1,
        isPrimary = false,
        destination = BomDestination.AmazonSearch(context.getString(searchRes, gauge), country),
        metricText = "$lengthDisplay · $gauge",
        storageKeys = listOf(load.id to logical),
    )
}

object LoadLength {
    fun display(meters: Double, unit: UnitSystem): String {
        val v = if (unit == UnitSystem.IMPERIAL) meters * ElectricalCalculations.FEET_PER_METER else meters
        return String.format(Locale.US, "%.1f %s", v, unit.lengthUnit)
    }
}

fun BomDestination.toUrl(): String? = when (this) {
    is BomDestination.Affiliate -> url
    is BomDestination.AmazonSearch -> AmazonAffiliate.searchUrl(query, country)
}

fun BomDestination.isAffiliate(): Boolean = this is BomDestination.Affiliate
