import SwiftUI

struct CalibrateView: View {
    @Bindable var state: AppState

    private var volumeGal: Double { state.calVolumeGal }
    private var naclPpm: Double { Double(state.calNaclPpm) ?? 0 }
    private var clPpm: Double { Double(state.calClPpm) ?? 0 }
    private var bleachPct: Double { Double(state.calBleachPct) ?? 0 }
    private var tempC: Double { Double(state.calTempC) ?? 25 }

    var body: some View {
        Form {
            inputSection
            resultsSection
            cellConstantSection
            chlorineCalSection
            phCalibrationSection
        }
        .navigationTitle("Calibrate")
    }

    // MARK: - Inputs

    private var inputSection: some View {
        Section("Solution Parameters") {
            Stepper("Volume: \(Int(state.calVolumeGal)) gal",
                    value: $state.calVolumeGal, in: 5...30, step: 5)

            HStack {
                Text("NaCl ppm")
                Spacer()
                TextField("ppm", text: $state.calNaclPpm)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }

            HStack {
                Text("Free Cl ppm")
                Spacer()
                TextField("ppm", text: $state.calClPpm)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }

            HStack {
                Text("Bleach %")
                Spacer()
                TextField("%", text: $state.calBleachPct)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }

            HStack {
                Text("Temperature")
                Spacer()
                TextField("\u{00B0}C", text: $state.calTempC)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }
        }
    }

    // MARK: - Computed results

    private var resultsSection: some View {
        Section("Preparation") {
            let salt = saltGrams(volumeGal: volumeGal, ppm: naclPpm)
            let tbsp = salt / 17.0
            Text(String(format: "Salt: %.1f g (%.1f tbsp)", salt, tbsp))

            let bleach = bleachMl(volumeGal: volumeGal, clPpm: clPpm, bleachPct: bleachPct)
            let tsp = bleach / 5.0
            Text(String(format: "Bleach: %.1f mL (%.1f tsp)", bleach, tsp))

            let kappa = theoreticalConductivity(naclPpm: naclPpm, tempC: tempC)
            Text(String(format: "Theoretical \u{03BA}: %.2f mS/cm at %.0f\u{00B0}C", kappa, tempC))
        }
    }

    // MARK: - Cell constant from EIS

    private var cellConstantSection: some View {
        Section("Cell Constant") {
            Button("Calculate K from Last Sweep") {
                guard let rs = extractRs(points: state.eisPoints) else {
                    state.status = "No valid EIS data for Rs"
                    return
                }
                let kappa = theoreticalConductivity(naclPpm: naclPpm, tempC: tempC)
                let k = cellConstant(kappaMsCm: kappa, rsOhm: Double(rs))
                state.calRs = Double(rs)
                state.calCellConstant = k
                state.send(buildSysexSetCellK(Float(k)))
                state.status = String(format: "K = %.4f cm\u{207B}\u{00B9} (Rs = %.1f \u{2126})", k, rs)
            }
            .disabled(state.eisPoints.isEmpty)

            if let rs = state.calRs {
                Text(String(format: "Rs: %.1f \u{2126}", rs))
            }
            if let k = state.calCellConstant {
                Text(String(format: "Cell constant K: %.4f cm\u{207B}\u{00B9}", k))
            }
        }
    }

    // MARK: - Chlorine calibration

    private var chlorineCalSection: some View {
        Section("Chlorine Calibration") {
            if let f = state.clFactor {
                Text(String(format: "Cl factor: %.6f ppm/\u{00B5}A", f))
            }
            if let r = state.clResult {
                Text(String(format: "Last free Cl peak: %.3f \u{00B5}A", r.iFreeUa))
            }

            HStack {
                Text("Known Cl ppm")
                Spacer()
                TextField("ppm", text: $state.clCalKnownPpm)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }

            Button("Set Cl Factor") {
                guard let r = state.clResult else {
                    state.status = "No chlorine measurement"
                    return
                }
                let knownPpm = Double(state.clCalKnownPpm) ?? 0
                let peak = abs(Double(r.iFreeUa))
                guard peak > 0 else {
                    state.status = "Peak current is zero"
                    return
                }
                let factor = knownPpm / peak
                state.clFactor = factor
                state.send(buildSysexSetClFactor(Float(factor)))
                state.status = String(format: "Cl factor: %.6f ppm/\u{00B5}A", factor)
            }
            .disabled(state.clResult == nil)
        }
    }

    // MARK: - pH calibration

    private var phCalibrationSection: some View {
        Section("pH Calibration (Q/HQ peak-shift)") {
            if let s = state.phSlope, let o = state.phOffset {
                Text(String(format: "slope: %.4f mV/pH   offset: %.4f mV", s, o))
                if let peak = detectQhqPeak(state.lsvPoints) {
                    if abs(s) > 1e-6 {
                        let ph = (Double(peak) - o) / s
                        Text(String(format: "Computed pH: %.2f  (peak at %.1f mV)", ph, peak))
                    }
                }
            }

            HStack {
                Text("Known pH")
                Spacer()
                TextField("7.00", text: $state.phCalKnown)
                    .multilineTextAlignment(.trailing)
                    .frame(width: 80)
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }

            Button("Add Calibration Point") {
                guard let peak = detectQhqPeak(state.lsvPoints) else {
                    state.status = "No Q/HQ peak found in LSV data"
                    return
                }
                guard let ph = Double(state.phCalKnown) else { return }
                state.phCalPoints.append((ph: ph, mV: Double(peak)))
                state.status = String(format: "pH cal point: pH=%.2f peak=%.1f mV (%d pts)",
                    ph, peak, state.phCalPoints.count)
            }
            .disabled(state.lsvPoints.isEmpty)

            ForEach(Array(state.phCalPoints.enumerated()), id: \.offset) { i, pt in
                Text(String(format: "%d. pH=%.2f  peak=%.1f mV", i + 1, pt.ph, pt.mV))
                    .font(.caption)
            }

            Button("Clear Points") {
                state.phCalPoints.removeAll()
                state.status = "pH cal points cleared"
            }
            .disabled(state.phCalPoints.isEmpty)

            Button("Compute & Set pH Cal") {
                let pts = state.phCalPoints
                guard pts.count >= 2 else {
                    state.status = "Need at least 2 calibration points"
                    return
                }
                let n = Double(pts.count)
                let meanPh = pts.map(\.ph).reduce(0, +) / n
                let meanV = pts.map(\.mV).reduce(0, +) / n
                let num = pts.map { ($0.ph - meanPh) * ($0.mV - meanV) }.reduce(0, +)
                let den = pts.map { ($0.ph - meanPh) * ($0.ph - meanPh) }.reduce(0, +)
                guard abs(den) > 1e-12 else {
                    state.status = "Degenerate calibration data"
                    return
                }
                let slope = num / den
                let offset = meanV - slope * meanPh
                state.phSlope = slope
                state.phOffset = offset
                state.send(buildSysexSetPhCal(Float(slope), Float(offset)))
                state.status = String(format: "pH cal set: slope=%.4f offset=%.4f", slope, offset)
            }
            .disabled(state.phCalPoints.count < 2)
        }
    }

    // MARK: - Calculations

    private func saltGrams(volumeGal: Double, ppm: Double) -> Double {
        let liters = volumeGal * 3.78541
        return ppm * liters / 1000.0
    }

    private func bleachMl(volumeGal: Double, clPpm: Double, bleachPct: Double) -> Double {
        let liters = volumeGal * 3.78541
        let clNeededMg = clPpm * liters
        let bleachMgPerMl = bleachPct * 10.0
        return clNeededMg / bleachMgPerMl
    }

    private func theoreticalConductivity(naclPpm: Double, tempC: Double) -> Double {
        let kappa25 = naclPpm * 2.0 / 1000.0
        return kappa25 * (1.0 + 0.0212 * (tempC - 25.0))
    }

    private func extractRs(points: [EisPoint]) -> Float? {
        points.map(\.zReal).filter { $0.isFinite && $0 > 0 }.min()
    }

    private func cellConstant(kappaMsCm: Double, rsOhm: Double) -> Double {
        (kappaMsCm / 1000.0) * rsOhm
    }
}
