import Foundation
import SwiftData

struct BatteryConfiguration: Identifiable, Hashable {
    enum Chemistry: String, CaseIterable, Identifiable {
        case agm = "AGM"
        case gel = "Gel"
        case floodedLeadAcid = "Flooded Lead Acid"
        case lithiumIronPhosphate = "LiFePO4"
        case lithiumIon = "Lithium Ion"

        var id: Self { self }

        var displayName: String {
            rawValue
        }

        var usableCapacityFraction: Double {
            switch self {
            case .floodedLeadAcid:
                return 0.5
            case .agm:
                return 0.5
            case .gel:
                return 0.6
            case .lithiumIronPhosphate:
                return 0.9
            case .lithiumIon:
                return 0.85
            }
        }
    }

    let id: UUID
    var name: String
    var nominalVoltage: Double
    var capacityAmpHours: Double
    var usableCapacityOverrideFraction: Double?
    var chargeVoltage: Double
    var cutOffVoltage: Double
    var minimumTemperatureCelsius: Double
    var maximumTemperatureCelsius: Double
    var chemistry: Chemistry
    var iconName: String
    var colorName: String
    var system: ElectricalSystem
    
    init(
        id: UUID = UUID(),
        name: String,
        nominalVoltage: Double = 12.8,
        capacityAmpHours: Double = 100,
        chemistry: Chemistry = .lithiumIronPhosphate,
        usableCapacityOverrideFraction: Double? = nil,
        chargeVoltage: Double = 14.4,
        cutOffVoltage: Double = 10.8,
        minimumTemperatureCelsius: Double = -20,
        maximumTemperatureCelsius: Double = 60,
        iconName: String = "battery.100",
        colorName: String = "blue",
        system: ElectricalSystem
    ) {
        self.id = id
        self.name = name
        self.nominalVoltage = nominalVoltage
        self.capacityAmpHours = capacityAmpHours
        self.usableCapacityOverrideFraction = usableCapacityOverrideFraction
        self.chargeVoltage = chargeVoltage
        self.cutOffVoltage = cutOffVoltage
        self.minimumTemperatureCelsius = minimumTemperatureCelsius
        self.maximumTemperatureCelsius = maximumTemperatureCelsius
        self.chemistry = chemistry
        self.iconName = iconName
        self.colorName = colorName
        self.system = system
    }

    init(savedBattery: SavedBattery, system: ElectricalSystem) {
        self.id = savedBattery.id
        self.name = savedBattery.name
        self.nominalVoltage = savedBattery.nominalVoltage
        self.capacityAmpHours = savedBattery.capacityAmpHours
        self.usableCapacityOverrideFraction = savedBattery.usableCapacityOverrideFraction
        self.chargeVoltage = savedBattery.chargeVoltage ?? 14.4
        self.cutOffVoltage = savedBattery.cutOffVoltage ?? 10.8
        self.minimumTemperatureCelsius = savedBattery.minimumTemperatureCelsius ?? -20
        self.maximumTemperatureCelsius = savedBattery.maximumTemperatureCelsius ?? 60
        if self.maximumTemperatureCelsius < self.minimumTemperatureCelsius {
            let correctedMin = min(self.minimumTemperatureCelsius, self.maximumTemperatureCelsius)
            let correctedMax = max(self.minimumTemperatureCelsius, self.maximumTemperatureCelsius)
            self.minimumTemperatureCelsius = correctedMin
            self.maximumTemperatureCelsius = correctedMax
        }
        self.chemistry = savedBattery.chemistry
        self.iconName = savedBattery.iconName
        self.colorName = savedBattery.colorName
        self.system = system
    }

    var energyWattHours: Double {
        nominalVoltage * capacityAmpHours
    }

    var defaultUsableCapacityFraction: Double {
        chemistry.usableCapacityFraction
    }

    var usableCapacityFraction: Double {
        if let override = usableCapacityOverrideFraction {
            return max(0, min(1, override))
        }
        return defaultUsableCapacityFraction
    }

    var defaultUsableCapacityAmpHours: Double {
        capacityAmpHours * defaultUsableCapacityFraction
    }

    var usableCapacityAmpHours: Double {
        capacityAmpHours * usableCapacityFraction
    }

    var usableEnergyWattHours: Double {
        usableCapacityAmpHours * nominalVoltage
    }

    func apply(to savedBattery: SavedBattery) {
        savedBattery.name = name
        savedBattery.nominalVoltage = nominalVoltage
        savedBattery.capacityAmpHours = capacityAmpHours
        savedBattery.usableCapacityOverrideFraction = usableCapacityOverrideFraction
        savedBattery.chargeVoltage = chargeVoltage
        savedBattery.cutOffVoltage = cutOffVoltage
        savedBattery.minimumTemperatureCelsius = minimumTemperatureCelsius
        savedBattery.maximumTemperatureCelsius = maximumTemperatureCelsius
        savedBattery.chemistry = chemistry
        savedBattery.iconName = iconName
        savedBattery.colorName = colorName
        savedBattery.system = system
        savedBattery.timestamp = Date()
    }
}

extension BatteryConfiguration {
    static func == (lhs: BatteryConfiguration, rhs: BatteryConfiguration) -> Bool {
        lhs.id == rhs.id &&
        lhs.name == rhs.name &&
        lhs.nominalVoltage == rhs.nominalVoltage &&
        lhs.capacityAmpHours == rhs.capacityAmpHours &&
        lhs.usableCapacityOverrideFraction == rhs.usableCapacityOverrideFraction &&
        lhs.chargeVoltage == rhs.chargeVoltage &&
        lhs.cutOffVoltage == rhs.cutOffVoltage &&
        lhs.minimumTemperatureCelsius == rhs.minimumTemperatureCelsius &&
        lhs.maximumTemperatureCelsius == rhs.maximumTemperatureCelsius &&
        lhs.chemistry == rhs.chemistry &&
        lhs.iconName == rhs.iconName &&
        lhs.colorName == rhs.colorName
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
        hasher.combine(nominalVoltage)
        hasher.combine(capacityAmpHours)
        hasher.combine(usableCapacityOverrideFraction)
        hasher.combine(chargeVoltage)
        hasher.combine(cutOffVoltage)
        hasher.combine(minimumTemperatureCelsius)
        hasher.combine(maximumTemperatureCelsius)
        hasher.combine(chemistry)
        hasher.combine(iconName)
        hasher.combine(colorName)
    }
}
