//
//  ElectricalCalculations.swift
//  Cable
//
//  Created by GPT on request.
//

import Foundation

struct ElectricalCalculations {
    private static let maxVoltageDropFraction = 0.05
    private static let copperResistivity = 0.017 // Ω⋅mm²/m
    private static let feetToMeters = 0.3048

    private static let standardMetricCrossSections: [Double] = [
        0.75, 1.0, 1.5, 2.5, 4.0, 6.0, 10.0, 16.0, 25.0, 35.0, 50.0, 70.0, 95.0, 120.0,
        150.0, 185.0, 240.0, 300.0, 400.0, 500.0, 630.0,
    ]

    // Convention: 1/0 = -1, 2/0 = -2, 3/0 = -3, 4/0 = -4
    private static let standardAWG: [Int] = [20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, -1, -2, -3, -4]
    private static let awgCrossSections: [Double] = [
        0.519, 0.823, 1.31, 2.08, 3.31, 5.26, 8.37, 13.3, 21.2, 33.6, 42.4, 53.5, 67.4, 85.0, 107.0,
    ]

    private static let standardFuses: [Double] = [
        1, 2, 3, 5, 7.5, 10, 15, 20, 25, 30, 35, 40, 50,
        60, 70, 80, 100, 125, 150, 175, 200, 225, 250,
        300, 350, 400, 450, 500, 600, 700, 800,
    ]

    /// Length must always be in meters. unitSystem controls the output format (mm² vs AWG).
    static func recommendedCrossSection(
        length: Double,
        current: Double,
        voltage: Double,
        unitSystem: UnitSystem
    ) -> Double {
        let lengthInMeters = length
        let maxVoltageDrop = voltage * maxVoltageDropFraction
        let minimumCrossSection = guardAgainstZero(maxVoltageDrop) {
            (2 * current * lengthInMeters * copperResistivity) / maxVoltageDrop
        }

        if unitSystem == .imperial {
            for (index, crossSection) in awgCrossSections.enumerated() where crossSection >= minimumCrossSection {
                return Double(standardAWG[index])
            }
            return Double(standardAWG.last ?? 0)
        } else {
            return standardMetricCrossSections.first { $0 >= max(standardMetricCrossSections.first ?? 0.75, minimumCrossSection) }
                ?? standardMetricCrossSections.last ?? 0.75
        }
    }

    static func voltageDrop(
        length: Double,
        current: Double,
        voltage: Double,
        unitSystem: UnitSystem,
        crossSection: Double? = nil
    ) -> Double {
        let selectedCrossSection = crossSection ?? recommendedCrossSection(
            length: length,
            current: current,
            voltage: voltage,
            unitSystem: unitSystem
        )

        let lengthInMeters = length
        let crossSectionMM2: Double
        if unitSystem == .metric {
            crossSectionMM2 = selectedCrossSection
        } else {
            crossSectionMM2 = crossSectionFromAWG(selectedCrossSection)
        }

        guard crossSectionMM2 > 0 else { return 0 }
        return (2 * current * lengthInMeters * copperResistivity) / crossSectionMM2
    }

    static func voltageDropPercentage(
        length: Double,
        current: Double,
        voltage: Double,
        unitSystem: UnitSystem,
        crossSection: Double? = nil
    ) -> Double {
        guard voltage != 0 else { return 0 }
        let drop = voltageDrop(
            length: length,
            current: current,
            voltage: voltage,
            unitSystem: unitSystem,
            crossSection: crossSection
        )
        return (drop / voltage) * 100
    }

    static func powerLoss(
        length: Double,
        current: Double,
        voltage: Double,
        unitSystem: UnitSystem,
        crossSection: Double? = nil
    ) -> Double {
        let drop = voltageDrop(
            length: length,
            current: current,
            voltage: voltage,
            unitSystem: unitSystem,
            crossSection: crossSection
        )
        return current * drop
    }

    static func recommendedFuse(forCurrent current: Double) -> Double {
        let target = (current * 1.25).rounded(.up)
        return standardFuses.first(where: { $0 >= target }) ?? standardFuses.last ?? target
    }

    private static func guardAgainstZero(_ divisor: Double, calculation: () -> Double) -> Double {
        guard divisor > 0 else { return 0 }
        return calculation()
    }

    private static func crossSectionFromAWG(_ awg: Double) -> Double {
        let index = standardAWG.firstIndex(of: Int(awg)) ?? -1
        if index >= 0 && index < awgCrossSections.count {
            return awgCrossSections[index]
        }
        return 0.75
    }

    /// Formats an AWG value for display: positive values as-is, negative as "X/0" notation.
    static func formatAWG(_ awg: Double) -> String {
        let intAWG = Int(awg)
        switch intAWG {
        case -1: return "1/0"
        case -2: return "2/0"
        case -3: return "3/0"
        case -4: return "4/0"
        default: return "\(intAWG)"
        }
    }
}
