USB-Meter / USB Meter / Views / Meter / Tabs / ChargeRecord / MeterChargeRecordTabView.swift
1 contributor
1715 lines | 70.767kb
//
//  MeterChargeRecordTabView.swift
//  USB Meter
//

import SwiftUI

struct MeterChargeRecordTabView: View, Equatable {
    static func == (lhs: MeterChargeRecordTabView, rhs: MeterChargeRecordTabView) -> Bool {
        true
    }

    var body: some View {
        MeterChargeRecordContentView()
    }
}

struct MeterChargeRecordContentView: View {
    private struct SessionMetricRow {
        let label: String
        let value: String
    }

    private enum InitialCheckpointMode: String, CaseIterable, Identifiable {
        case known
        case unknown
        case flat

        var id: String { rawValue }

        var title: String {
            switch self {
            case .known:   return "Known"
            case .unknown: return "Unknown"
            case .flat:    return "Flat"
            }
        }
    }

    private enum ActiveMode: Hashable {
        case chargeSession
        case standbyPower
    }

    private enum FinalCheckpoint: Hashable {
        case full
        case skip
        case custom

        var label: String {
            switch self {
            case .full:   return "Full"
            case .skip:   return "Skip"
            case .custom: return "Other %"
            }
        }

        var icon: String {
            switch self {
            case .full:   return "battery.100percent"
            case .skip:   return "minus.circle"
            case .custom: return "pencil"
            }
        }
    }

    private enum SessionStartRequirement: Identifiable {
        case existingSession
        case device
        case chargingType
        case chargingMode
        case charger
        case initialCheckpointEmpty
        case initialCheckpointInvalid

        var id: String {
            switch self {
            case .existingSession:         return "existing-session"
            case .device:                  return "device"
            case .chargingType:            return "charging-type"
            case .chargingMode:            return "charging-mode"
            case .charger:                 return "charger"
            case .initialCheckpointEmpty:  return "initial-checkpoint-empty"
            case .initialCheckpointInvalid:return "initial-checkpoint-invalid"
            }
        }

        var message: String {
            switch self {
            case .existingSession:          return "Stop or pause the current session before starting another one."
            case .device:                   return "Select the device that is charging."
            case .chargingType:             return "Choose the charging type for this session."
            case .chargingMode:             return "Choose whether the device is on or off for this session."
            case .charger:                  return "Select the wireless charger used in this session."
            case .initialCheckpointEmpty:   return "Enter the initial battery percentage."
            case .initialCheckpointInvalid: return "Initial battery percentage must be between 0 and 100."
            }
        }
    }

@EnvironmentObject private var appData: AppData
    @EnvironmentObject private var usbMeter: Meter

    @State private var showingInlineTargetEditor = false
    @State private var draftTargetText = ""
    @State private var showingStopConfirm = false
    @State private var finalCheckpointMode: FinalCheckpoint = .skip
    @State private var finalCheckpointText = ""
    @State private var pendingCheckpointDeletion: ChargeCheckpointSummary?
    @State private var draftChargingTransportMode: ChargingTransportMode?
    @State private var draftChargingStateMode: ChargingStateMode?
    @State private var initialCheckpointMode: InitialCheckpointMode = .known
    @State private var initialCheckpoint = ""
    @State private var showsMeterTotalsInfo = false
    @State private var activeMode: ActiveMode = .chargeSession
    @State private var detectedTrimWindow: ChargingWindowDetector.DetectedWindow?
    @State private var trimBannerDismissedForSessionID: UUID?

    private var shouldShowTrimBanner: Bool {
        guard let session = openChargeSession,
              session.isTrimmed == false,
              trimBannerDismissedForSessionID != session.id else { return false }
        guard let window = detectedTrimWindow else { return false }
        return window.trimRatio > ChargingWindowDetector.significantTrimThreshold
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 14) {
                statusHeader

                if let openChargeSession {
                    chargingMonitorCard(openChargeSession)

                    if shouldShowTrimBanner {
                        trimDetectionBanner(for: openChargeSession)
                    }

                    if shouldShowSessionChart(for: openChargeSession) {
                        sessionChartCard(
                            timeRange: sessionChartFixedTimeRange(for: openChargeSession),
                            session: openChargeSession
                        )
                    }
                } else {
                    liveMeterStripView
                    modePicker

                    switch activeMode {
                    case .chargeSession:
                        chargeSessionSetupCard
                    case .standbyPower:
                        standbyPowerCard
                    }
                }
            }
            .padding()
        }
        .background(
            LinearGradient(
                colors: [.pink.opacity(0.14), Color.clear],
                startPoint: .topLeading,
                endPoint: .bottomTrailing
            )
            .ignoresSafeArea()
        )
        .alert(item: $pendingCheckpointDeletion) { checkpoint in
            Alert(
                title: Text("Delete Battery Checkpoint"),
                message: Text("Remove the checkpoint at \(checkpoint.timestamp.format()) with \(checkpoint.batteryPercent.format(decimalDigits: 0))% from this session?"),
                primaryButton: .destructive(Text("Delete")) {
                    if let openChargeSession {
                        _ = appData.deleteBatteryCheckpoint(
                            checkpointID: checkpoint.id,
                            for: openChargeSession.id
                        )
                    }
                },
                secondaryButton: .cancel()
            )
        }
        .onAppear {
            syncActiveSessionRestore()
            syncDraftSelections()
            runTrimDetection()
        }
        .onChange(of: selectedChargedDevice?.id) { _ in
            syncDraftSelections()
        }
        .onChange(of: openChargeSession?.id) { _ in
            syncActiveSessionRestore()
            syncDraftSelections()
            showingInlineTargetEditor = false
            draftTargetText = ""
            detectedTrimWindow = nil
            trimBannerDismissedForSessionID = nil
            runTrimDetection()
        }
        .onChange(of: openChargeSession?.aggregatedSamples.count) { _ in
            syncActiveSessionRestore()
            runTrimDetection()
        }
    }

    private func syncActiveSessionRestore() {
        guard let session = openChargeSession else { return }
        guard session.status.isOpen else { return }
        guard session.meterMACAddress == meterMACAddress else { return }
        usbMeter.restoreChargeMonitoringIfNeeded(from: session)
    }

    private func runTrimDetection() {
        guard let session = openChargeSession,
              session.isTrimmed == false,
              !session.aggregatedSamples.isEmpty else {
            detectedTrimWindow = nil
            return
        }
        let sessionEnd = session.endedAt ?? session.lastObservedAt
        detectedTrimWindow = ChargingWindowDetector.detect(
            samples: session.aggregatedSamples,
            sessionStart: session.startedAt,
            sessionEnd: sessionEnd
        )
    }

    // MARK: - Computed Properties

    private var meterMACAddress: String {
        usbMeter.btSerial.macAddress.description
    }

    private var selectedChargedDevice: ChargedDeviceSummary? {
        appData.currentChargedDeviceSummary(for: meterMACAddress)
    }

    private var availableChargedDevices: [ChargedDeviceSummary] {
        appData.deviceSummaries
    }

    private var selectedChargedDeviceID: Binding<UUID?> {
        Binding(
            get: { selectedChargedDevice?.id },
            set: { newValue in
                guard let newValue else { return }
                _ = appData.assignChargedDevice(newValue, to: meterMACAddress)
            }
        )
    }

    private var selectedCharger: ChargedDeviceSummary? {
        appData.currentChargerSummary(for: meterMACAddress)
    }

    private var availableChargers: [ChargedDeviceSummary] {
        appData.chargerSummaries
    }

    private var selectedChargerID: Binding<UUID?> {
        Binding(
            get: { selectedCharger?.id },
            set: { newValue in
                guard let newValue else { return }
                _ = appData.assignCharger(newValue, to: meterMACAddress)
            }
        )
    }

    private var openChargeSession: ChargeSessionSummary? {
        appData.activeChargeSessionSummary(for: meterMACAddress)
    }

    private var showsMeterTotalsCard: Bool {
        usbMeter.supportsRecordingView
            || usbMeter.supportsDataGroupCommands
            || usbMeter.recordedAH > 0
            || usbMeter.recordedWH > 0
            || usbMeter.recordingDuration > 0
    }

    private var selectedDraftTransportMode: ChargingTransportMode? {
        openChargeSession?.chargingTransportMode ?? draftChargingTransportMode
    }

    private var selectedDraftChargingStateMode: ChargingStateMode? {
        openChargeSession?.chargingStateMode ?? draftChargingStateMode
    }

    private var initialCheckpointValue: Double? {
        guard initialCheckpointMode == .known else { return nil }
        let normalized = initialCheckpoint
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: ",", with: ".")
        guard let value = Double(normalized), value >= 0, value <= 100 else { return nil }
        return value
    }

    private var hasInitialCheckpointInput: Bool {
        initialCheckpoint.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
    }

    private var shouldRequireInitialCheckpoint: Bool {
        initialCheckpointMode == .known
    }

    private var requiresExplicitTransportSelection: Bool {
        (selectedChargedDevice?.supportedChargingModes.count ?? 0) > 1
    }

    private var requiresExplicitChargingStateSelection: Bool {
        (selectedChargedDevice?.supportedChargingStateModes.count ?? 0) > 1
    }

    private var startRequirements: [SessionStartRequirement] {
        var requirements: [SessionStartRequirement] = []

        if openChargeSession != nil {
            requirements.append(.existingSession)
        }

        guard let selectedChargedDevice else {
            requirements.append(.device)
            return requirements
        }

        guard let chargingTransportMode = selectedDraftTransportMode else {
            requirements.append(.chargingType)
            return requirements
        }

        if selectedChargedDevice.supportedChargingModes.contains(chargingTransportMode) == false {
            requirements.append(.chargingType)
        }

        guard let chargingStateMode = selectedDraftChargingStateMode else {
            requirements.append(.chargingMode)
            return requirements
        }

        if selectedChargedDevice.supportedChargingStateModes.contains(chargingStateMode) == false {
            requirements.append(.chargingMode)
        }

        if chargingTransportMode == .wireless, selectedCharger == nil {
            requirements.append(.charger)
        }

        if shouldRequireInitialCheckpoint {
            if hasInitialCheckpointInput == false {
                requirements.append(.initialCheckpointEmpty)
            } else if initialCheckpointValue == nil {
                requirements.append(.initialCheckpointInvalid)
            }
        }

        return requirements
    }

    private var canStartSession: Bool {
        startRequirements.isEmpty
    }

    private var headerStatusTitle: String {
        guard let openChargeSession else { return "Idle" }
        return openChargeSession.status.title
    }

    private var headerStatusColor: Color {
        guard let openChargeSession else { return .secondary }
        switch openChargeSession.status {
        case .active:    return .red
        case .paused:    return .orange
        case .completed: return .green
        case .abandoned: return .secondary
        }
    }

    private func shouldShowSessionChart(for session: ChargeSessionSummary) -> Bool {
        sessionChartFixedTimeRange(for: session) != nil || usesChargeRecordBuffer(for: session)
    }

    private func sessionChartFixedTimeRange(for session: ChargeSessionSummary) -> ClosedRange<Date>? {
        if usesChargeRecordBuffer(for: session) {
            return nil
        }
        return session.effectiveTimeRange
    }

    private func sessionChartLiveTrimBounds(for session: ChargeSessionSummary) -> (lower: Date?, upper: Date?) {
        guard usesChargeRecordBuffer(for: session) else {
            return (nil, nil)
        }
        return (session.trimStart, session.trimEnd)
    }

    private func usesChargeRecordBuffer(for session: ChargeSessionSummary) -> Bool {
        session.status.isOpen && session.meterMACAddress == meterMACAddress
    }

    private var showsWirelessChargerSection: Bool {
        let transportMode = selectedDraftTransportMode ?? selectedChargedDevice?.supportedChargingModes.first
        return transportMode == .wireless
    }

    // MARK: - Status Header

    private var statusHeader: some View {
        HStack {
            Image(systemName: "bolt.fill")
                .foregroundColor(.pink)
            Text("Charging Session")
                .font(.system(.title3, design: .rounded).weight(.bold))
            Spacer()
            Text(headerStatusTitle)
                .font(.caption.weight(.bold))
                .foregroundColor(headerStatusColor)
                .padding(.horizontal, 10)
                .padding(.vertical, 6)
                .meterCard(tint: headerStatusColor, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999)
        }
        .padding(.horizontal, 18)
        .padding(.vertical, 12)
        .meterCard(tint: .pink, fillOpacity: 0.18, strokeOpacity: 0.24)
    }

    // MARK: - Mode Picker

    private var modePicker: some View {
        Picker("", selection: $activeMode) {
            Label("Charge Session", systemImage: "bolt.fill").tag(ActiveMode.chargeSession)
            Label("Standby Power", systemImage: "powersleep").tag(ActiveMode.standbyPower)
        }
        .pickerStyle(.segmented)
        .labelsHidden()
    }

    // MARK: - Charge Session Setup

    private var chargeSessionSetupCard: some View {
        VStack(alignment: .leading, spacing: 0) {
            // Device
            setupRow(icon: "iphone", iconColor: .blue) {
                Picker(selection: selectedChargedDeviceID) {
                    Text("Choose device").tag(UUID?.none)
                    ForEach(availableChargedDevices) { device in
                        Text(device.name).tag(Optional(device.id))
                    }
                } label: {
                    HStack(spacing: 8) {
                        if let device = selectedChargedDevice {
                            ChargedDeviceIdentityLabelView(chargedDevice: device, iconPointSize: 15)
                                .font(.subheadline.weight(.semibold))
                        } else {
                            Text(availableChargedDevices.isEmpty ? "No devices available" : "Choose device")
                                .foregroundColor(.secondary)
                                .font(.subheadline)
                        }
                        Spacer(minLength: 8)
                        Image(systemName: "chevron.up.chevron.down")
                            .font(.caption.weight(.semibold))
                            .foregroundColor(.secondary)
                    }
                }
                .pickerStyle(.menu)
                .disabled(availableChargedDevices.isEmpty)
            }

            // Charging type — only when device supports multiple
            if requiresExplicitTransportSelection, let device = selectedChargedDevice {
                Divider().padding(.leading, 46)
                setupRow(icon: draftChargingTransportMode?.symbolName ?? "bolt.slash", iconColor: .orange) {
                    Text("Type")
                        .foregroundColor(.secondary)
                        .font(.subheadline)
                    Spacer()
                    compactSelectionMenu(
                        title: draftChargingTransportMode?.title ?? "Choose",
                        options: device.supportedChargingModes.map { mode in
                            CompactSelectionOption(
                                id: mode.id, title: mode.title,
                                isSelected: draftChargingTransportMode == mode,
                                action: { draftChargingTransportMode = mode }
                            )
                        }
                    )
                }
            }

            // Charging state — only when device supports multiple
            if requiresExplicitChargingStateSelection, let device = selectedChargedDevice {
                Divider().padding(.leading, 46)
                setupRow(icon: draftChargingStateMode == .off ? "power.circle" : "power", iconColor: .purple) {
                    Text("Mode")
                        .foregroundColor(.secondary)
                        .font(.subheadline)
                    Spacer()
                    compactSelectionMenu(
                        title: draftChargingStateMode?.title ?? "Choose",
                        options: device.supportedChargingStateModes.map { mode in
                            CompactSelectionOption(
                                id: mode.id, title: mode.title,
                                isSelected: draftChargingStateMode == mode,
                                action: { draftChargingStateMode = mode }
                            )
                        }
                    )
                }
            }

            // Wireless charger — only when wireless transport
            if showsWirelessChargerSection {
                Divider().padding(.leading, 46)
                setupRow(icon: "antenna.radiowaves.left.and.right", iconColor: .teal) {
                    Picker(selection: selectedChargerID) {
                        Text("Choose charger").tag(UUID?.none)
                        ForEach(availableChargers) { charger in
                            Text(charger.name).tag(Optional(charger.id))
                        }
                    } label: {
                        HStack(spacing: 8) {
                            if let charger = selectedCharger {
                                ChargedDeviceIdentityLabelView(chargedDevice: charger, iconPointSize: 15)
                                    .font(.subheadline.weight(.semibold))
                                if charger.chargerIdleCurrentAmps == nil {
                                    Image(systemName: "exclamationmark.triangle.fill")
                                        .foregroundColor(.orange)
                                        .font(.caption)
                                }
                            } else {
                                Text(availableChargers.isEmpty ? "No chargers available" : "Choose charger")
                                    .foregroundColor(.secondary)
                                    .font(.subheadline)
                            }
                            Spacer(minLength: 8)
                            Image(systemName: "chevron.up.chevron.down")
                                .font(.caption.weight(.semibold))
                                .foregroundColor(.secondary)
                        }
                    }
                    .pickerStyle(.menu)
                    .disabled(availableChargers.isEmpty)
                }
            }

            // Battery checkpoint
            Divider().padding(.leading, 46)
            setupRow(icon: "battery.75percent", iconColor: .green) {
                if initialCheckpointMode == .known {
                    Button { adjustInitialCheckpoint(by: -1) } label: {
                        Image(systemName: "minus.circle").font(.title3)
                    }
                    .buttonStyle(.plain)

                    TextField("—", text: $initialCheckpoint)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                        .frame(width: 52)
                        .multilineTextAlignment(.center)

                    Text("%")
                        .font(.subheadline)
                        .foregroundColor(.secondary)

                    Button { adjustInitialCheckpoint(by: 1) } label: {
                        Image(systemName: "plus.circle").font(.title3)
                    }
                    .buttonStyle(.plain)
                } else {
                    Text(initialCheckpointMode == .flat
                         ? "Flat (device off / discharged)"
                         : "Unknown")
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
                Spacer()
                compactSelectionMenu(
                    title: initialCheckpointMode.title,
                    options: InitialCheckpointMode.allCases.map { mode in
                        CompactSelectionOption(
                            id: mode.id, title: mode.title,
                            isSelected: initialCheckpointMode == mode,
                            action: { initialCheckpointMode = mode }
                        )
                    }
                )
            }

            // Requirement errors
            if startRequirements.isEmpty == false {
                Divider()
                VStack(alignment: .leading, spacing: 6) {
                    ForEach(startRequirements) { requirement in
                        Label(requirement.message, systemImage: "exclamationmark.circle")
                            .font(.caption)
                            .foregroundColor(.orange)
                    }
                }
                .padding(.horizontal, 14)
                .padding(.vertical, 10)
            }

            // Start button
            Divider()
            Button("Start Session") {
                startSession()
            }
            .frame(maxWidth: .infinity)
            .padding(.vertical, 11)
            .font(.subheadline.weight(.semibold))
            .foregroundColor(canStartSession ? .green : .secondary)
            .buttonStyle(.plain)
            .disabled(!canStartSession)
        }
        .meterCard(tint: .orange, fillOpacity: 0.14, strokeOpacity: 0.20)
    }

    // MARK: - Standby Power Card

    private var standbyPowerCard: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack(spacing: 10) {
                Image(systemName: "powersleep")
                    .foregroundColor(.orange)
                    .font(.title3)
                VStack(alignment: .leading, spacing: 2) {
                    Text("Charger Standby Power")
                        .font(.subheadline.weight(.semibold))
                    Text("Measure idle draw with no device connected.")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
            }

            NavigationLink(
                destination: ChargerStandbyPowerWizardView(
                    preferredMeterMACAddress: meterMACAddress
                )
            ) {
                HStack {
                    Image(systemName: "plus.circle.fill")
                        .foregroundColor(.orange)
                    Text("New Measurement")
                        .font(.subheadline.weight(.semibold))
                    Spacer()
                    Image(systemName: "chevron.right")
                        .font(.caption.weight(.semibold))
                        .foregroundColor(.secondary)
                }
                .padding(.vertical, 10)
                .padding(.horizontal, 14)
                .meterCard(tint: .orange, fillOpacity: 0.12, strokeOpacity: 0.18, cornerRadius: 14)
            }
            .buttonStyle(.plain)
        }
        .padding(18)
        .meterCard(tint: .orange, fillOpacity: 0.10, strokeOpacity: 0.16)
    }

    // MARK: - Live Meter Strip (idle state)

    private var liveMeterStripView: some View {
        let columns = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
        return LazyVGrid(columns: columns, spacing: 8) {
            metricCell(label: "Power", value: "\(usbMeter.power.format(decimalDigits: 2)) W", tint: .yellow)
            metricCell(label: "Current", value: "\(usbMeter.current.format(decimalDigits: 3)) A", tint: .blue)
            metricCell(label: "Voltage", value: "\(usbMeter.voltage.format(decimalDigits: 2)) V", tint: .teal)
        }
    }

    // MARK: - Charging Monitor Card

    private func chargingMonitorCard(_ openChargeSession: ChargeSessionSummary) -> some View {
        let displayedEnergyWh = displayedSessionEnergyWh(for: openChargeSession)
        let displayedChargeAh = displayedSessionChargeAh(for: openChargeSession)
        let canAddCheckpoint = appData.canAddBatteryCheckpoint(to: openChargeSession.id)
        let batteryPrediction = selectedChargedDevice?.batteryLevelPrediction(
            for: openChargeSession,
            effectiveEnergyWhOverride: displayedEnergyWh
        )

        return VStack(alignment: .leading, spacing: 14) {
            // Header
            HStack {
                if let device = selectedChargedDevice {
                    ChargedDeviceIdentityLabelView(chargedDevice: device, iconPointSize: 16)
                        .font(.headline)
                } else {
                    Text("Charging Monitor").font(.headline)
                }
                Spacer()
                Text(openChargeSession.status.title)
                    .font(.caption.weight(.bold))
                    .foregroundColor(headerStatusColor)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .meterCard(tint: headerStatusColor, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999)
            }

            // Orphaned session warning — device was deleted from library
            if selectedChargedDevice == nil {
                VStack(alignment: .leading, spacing: 8) {
                    Label("Device removed from library", systemImage: "exclamationmark.triangle.fill")
                        .font(.subheadline.weight(.semibold))
                        .foregroundColor(.orange)
                    Text("The device associated with this session no longer exists. Stop the session to close it.")
                        .font(.caption)
                        .foregroundColor(.secondary)
                    Button("Terminate Session") {
                        _ = appData.stopChargeSession(
                            sessionID: openChargeSession.id,
                            finalBatteryPercent: nil
                        )
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 9)
                    .meterCard(tint: .red, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 12)
                    .buttonStyle(.plain)
                }
                .padding(14)
                .meterCard(tint: .orange, fillOpacity: 0.10, strokeOpacity: 0.16, cornerRadius: 16)
            }

            // Battery prediction gauge
            if let batteryPrediction {
                batteryGaugeSection(
                    prediction: batteryPrediction,
                    session: openChargeSession,
                    displayedEnergyWh: displayedEnergyWh
                )
            }

            // Metrics grid
            sessionMetricsGrid(
                for: openChargeSession,
                displayedEnergyWh: displayedEnergyWh,
                hasPrediction: batteryPrediction != nil
            )

            if openChargeSession.stopThresholdAmps > 0 {
                Text("Stop threshold: \(openChargeSession.stopThresholdAmps.format(decimalDigits: 2)) A")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }

            if let sessionWarning = sessionWarning(for: openChargeSession) {
                Label(sessionWarning, systemImage: "exclamationmark.triangle")
                    .font(.caption)
                    .foregroundColor(.orange)
            }

            if openChargeSession.isPaused {
                Label(
                    "Paused \(openChargeSession.pausedAt?.format() ?? openChargeSession.lastObservedAt.format()). Auto-stops after 10 min.",
                    systemImage: "pause.circle"
                )
                .font(.caption)
                .foregroundColor(.secondary)
            }

            if openChargeSession.requiresCompletionConfirmation && !showingStopConfirm {
                completionConfirmationCard(openChargeSession)
            }

            BatteryCheckpointSectionView(
                sessionID: openChargeSession.id,
                checkpoints: openChargeSession.checkpoints,
                message: "Checkpoints are used for capacity estimation and the typical charge curve.",
                canAddCheckpoint: canAddCheckpoint,
                requirementMessage: appData.batteryCheckpointCaptureRequirementMessage(for: openChargeSession.id),
                effectiveEnergyWhOverride: displayedEnergyWh,
                measuredChargeAhOverride: displayedChargeAh,
                onDelete: { checkpoint in
                    pendingCheckpointDeletion = checkpoint
                }
            )

            targetSectionView(
                for: openChargeSession,
                predictedPercent: batteryPrediction?.predictedPercent
            )

            if showingStopConfirm {
                stopConfirmPanel(for: openChargeSession)
            } else {
                HStack(spacing: 10) {
                    if openChargeSession.status == .active {
                        Button("Pause") {
                            _ = appData.pauseChargeSession(sessionID: openChargeSession.id, from: usbMeter)
                        }
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 10)
                        .meterCard(tint: .orange, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                        .buttonStyle(.plain)
                    } else if openChargeSession.status == .paused {
                        Button("Resume") {
                            _ = appData.resumeChargeSession(sessionID: openChargeSession.id, from: usbMeter)
                        }
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 10)
                        .meterCard(tint: .blue, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                        .buttonStyle(.plain)
                    }

                    Button("Terminate Session") {
                        finalCheckpointMode = .skip
                        finalCheckpointText = ""
                        showingStopConfirm = true
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 10)
                    .meterCard(tint: .red, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                    .buttonStyle(.plain)
                }
            }
        }
        .padding(18)
        .meterCard(tint: .green, fillOpacity: 0.14, strokeOpacity: 0.20)
    }

    // MARK: - Battery Gauge Section

    private func batteryGaugeSection(
        prediction: BatteryLevelPrediction,
        session: ChargeSessionSummary,
        displayedEnergyWh: Double
    ) -> some View {
        let percent = prediction.predictedPercent
        let color = batteryColor(for: percent)
        let duration = displayedSessionDuration(for: session)
        let rateWhPerSec: Double? = duration > 300 && displayedEnergyWh > 0.01
            ? displayedEnergyWh / duration
            : nil

        let etaToFull: String? = {
            guard let rate = rateWhPerSec, rate > 0.0001, percent < 98 else { return nil }
            let remaining = max(prediction.estimatedCapacityWh - displayedEnergyWh, 0)
            let seconds = remaining / rate
            return seconds > 120 ? formatETA(seconds) : nil
        }()

        let etaToTarget: String? = {
            guard let target = session.targetBatteryPercent, target > percent + 1,
                  let rate = rateWhPerSec, rate > 0.0001 else { return nil }
            let targetEnergyWh = (target / 100) * prediction.estimatedCapacityWh
            let remaining = max(targetEnergyWh - displayedEnergyWh, 0)
            let seconds = remaining / rate
            return seconds > 120 ? formatETA(seconds) : nil
        }()

        return VStack(spacing: 10) {
            HStack(alignment: .lastTextBaseline, spacing: 8) {
                HStack(alignment: .lastTextBaseline, spacing: 3) {
                    Text("\(Int(percent.rounded()))")
                        .font(.system(size: 52, weight: .bold, design: .rounded))
                        .foregroundColor(color)
                        .monospacedDigit()
                    Text("%")
                        .font(.title2.weight(.semibold))
                        .foregroundColor(color.opacity(0.8))
                }
                Spacer()
                VStack(alignment: .trailing, spacing: 2) {
                    Text("\(prediction.estimatedCapacityWh.format(decimalDigits: 2)) Wh")
                        .font(.callout.weight(.bold))
                        .foregroundColor(.orange)
                        .monospacedDigit()
                    Text("est. capacity")
                        .font(.caption2)
                        .foregroundColor(.secondary)
                }
            }

            batteryProgressBar(
                percent: percent,
                startPercent: session.startBatteryPercent,
                targetPercent: session.targetBatteryPercent
            )

            HStack(spacing: 14) {
                if let etaToFull {
                    VStack(alignment: .leading, spacing: 1) {
                        HStack(spacing: 4) {
                            Image(systemName: "clock.fill")
                                .font(.caption)
                                .foregroundColor(.green)
                            Text(etaToFull)
                                .font(.caption.weight(.bold))
                        }
                        Text("to full")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                }
                if let etaToTarget, let target = session.targetBatteryPercent {
                    VStack(alignment: .leading, spacing: 1) {
                        HStack(spacing: 4) {
                            Image(systemName: "bell.badge.fill")
                                .font(.caption)
                                .foregroundColor(.indigo)
                            Text(etaToTarget)
                                .font(.caption.weight(.bold))
                        }
                        Text("to \(Int(target.rounded()))%")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                }
                Spacer()
                Text("anchored to \(prediction.anchorDescription) at \(prediction.anchorPercent.format(decimalDigits: 0))%")
                    .font(.caption2)
                    .foregroundColor(.secondary)
                    .multilineTextAlignment(.trailing)
            }
        }
        .padding(14)
        .meterCard(tint: color, fillOpacity: 0.10, strokeOpacity: 0.16, cornerRadius: 16)
    }

    private func batteryProgressBar(
        percent: Double,
        startPercent: Double?,
        targetPercent: Double?
    ) -> some View {
        let color = batteryColor(for: percent)
        return GeometryReader { geo in
            let width = geo.size.width
            ZStack(alignment: .leading) {
                Capsule()
                    .fill(Color.primary.opacity(0.10))
                Rectangle()
                    .fill(
                        LinearGradient(
                            colors: [color.opacity(0.6), color],
                            startPoint: .leading,
                            endPoint: .trailing
                        )
                    )
                    .frame(width: max(width * CGFloat(percent / 100), 4))
                    .animation(.easeInOut(duration: 0.4), value: percent)
                if let start = startPercent, start > 2, start < 98 {
                    Rectangle()
                        .fill(Color.white.opacity(0.55))
                        .frame(width: 2, height: 20)
                        .offset(x: width * CGFloat(start / 100) - 1)
                }
                if let target = targetPercent {
                    Rectangle()
                        .fill(Color.indigo.opacity(0.9))
                        .frame(width: 2.5, height: 20)
                        .offset(x: width * CGFloat(target / 100) - 1.25)
                }
            }
            .clipShape(Capsule())
        }
        .frame(height: 20)
    }

    private func batteryColor(for percent: Double) -> Color {
        if percent >= 75 { return .green }
        if percent >= 35 { return .orange }
        return .red
    }

    private func formatETA(_ seconds: TimeInterval) -> String {
        let totalMinutes = Int(seconds / 60)
        if totalMinutes < 60 { return "\(totalMinutes)m" }
        let hours = totalMinutes / 60
        let minutes = totalMinutes % 60
        return minutes == 0 ? "\(hours)h" : "\(hours)h \(minutes)m"
    }

    // MARK: - Session Metrics Grid

    private func sessionMetricsGrid(
        for session: ChargeSessionSummary,
        displayedEnergyWh: Double,
        hasPrediction: Bool
    ) -> some View {
        let displayedDuration = displayedSessionDuration(for: session)
        let capacityFallback: Double? = hasPrediction ? nil : (
            session.capacityEstimateWh
                ?? selectedChargedDevice?.estimatedBatteryCapacityWh(for: session.chargingTransportMode)
                ?? selectedChargedDevice?.estimatedBatteryCapacityWh
        )
        let columns = [GridItem(.flexible()), GridItem(.flexible())]

        return LazyVGrid(columns: columns, spacing: 8) {
            metricCell(label: "Energy", value: "\(displayedEnergyWh.format(decimalDigits: 3)) Wh", tint: .blue)
            metricCell(label: "Duration", value: formatDuration(displayedDuration), tint: .teal)

            if shouldShowChargingTransport(for: session) {
                metricCell(label: "Type", value: session.chargingTransportMode.title, tint: .orange)
            }
            if shouldShowChargingState(for: session) {
                metricCell(label: "Mode", value: session.chargingStateMode.title, tint: .purple)
            }

            metricCell(label: "Auto Stop", value: autoStopLabel(for: session), tint: .secondary)

            if let capacity = capacityFallback {
                metricCell(label: "Est. Capacity", value: "\(capacity.format(decimalDigits: 2)) Wh", tint: .orange)
            }
        }
    }

    private func metricCell(label: String, value: String, tint: Color) -> some View {
        VStack(alignment: .leading, spacing: 3) {
            Text(label)
                .font(.caption2)
                .foregroundColor(.secondary)
            Text(value)
                .font(.subheadline.weight(.semibold))
                .lineLimit(1)
                .minimumScaleFactor(0.7)
                .monospacedDigit()
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.horizontal, 12)
        .padding(.vertical, 10)
        .meterCard(tint: tint, fillOpacity: 0.08, strokeOpacity: 0.12, cornerRadius: 12)
    }

    private func completionConfirmationCard(_ openChargeSession: ChargeSessionSummary) -> some View {
        VStack(alignment: .leading, spacing: 10) {
            Label("Charging may have stopped", systemImage: "questionmark.circle.fill")
                .font(.subheadline.weight(.semibold))

            if let contradictionPercent = openChargeSession.completionContradictionPercent {
                Text("Current dropped but estimated level is only \(contradictionPercent.format(decimalDigits: 0))%.")
                    .font(.caption)
                    .foregroundColor(.secondary)
            } else {
                Text("Current dropped to the stop threshold but the prediction doesn't confirm a full charge yet.")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }

            HStack(spacing: 10) {
                Button("Finish") {
                    finalCheckpointMode = .skip
                    finalCheckpointText = ""
                    showingStopConfirm = true
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 9)
                .meterCard(tint: .orange, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                .buttonStyle(.plain)

                Button("Keep Monitoring") {
                    _ = appData.continueChargeSessionMonitoring(sessionID: openChargeSession.id)
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 9)
                .meterCard(tint: .blue, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                .buttonStyle(.plain)
            }
        }
        .padding(14)
        .meterCard(tint: .orange, fillOpacity: 0.10, strokeOpacity: 0.16, cornerRadius: 16)
    }

    // MARK: - Target Section

    private func targetSectionView(for session: ChargeSessionSummary, predictedPercent: Double?) -> some View {
        let draftBelowPrediction: Bool = {
            guard let draft = parsedDraftTarget, let predicted = predictedPercent else { return false }
            return draft <= predicted
        }()
        let savedBelowPrediction: Bool = {
            guard let saved = session.targetBatteryPercent, let predicted = predictedPercent else { return false }
            return saved <= predicted
        }()

        return HStack(alignment: .center, spacing: 8) {
            Image(systemName: "bell.badge")
                .foregroundColor(.indigo)
                .font(.subheadline)

            Text("Notify at")
                .font(.subheadline.weight(.semibold))

            Spacer(minLength: 8)

            if showingInlineTargetEditor {
                Button {
                    let current = parsedDraftTarget ?? session.targetBatteryPercent ?? 80
                    let next = max(current - 1, 1)
                    draftTargetText = next.format(decimalDigits: 0)
                } label: {
                    Image(systemName: "minus.circle")
                        .font(.title3)
                }
                .buttonStyle(.plain)

                TextField("—", text: $draftTargetText)
                    .keyboardType(.decimalPad)
                    .textFieldStyle(.roundedBorder)
                    .frame(width: 48)
                    .multilineTextAlignment(.center)
                    .foregroundColor(draftBelowPrediction ? .orange : .primary)

                Text("%")
                    .font(.subheadline)
                    .foregroundColor(.secondary)

                if draftBelowPrediction {
                    Button {} label: {
                        Image(systemName: "exclamationmark.triangle.fill")
                            .font(.body.weight(.semibold))
                            .foregroundColor(.orange)
                    }
                    .buttonStyle(.plain)
                    .help("Battery is already predicted at \(predictedPercent!.format(decimalDigits: 0))% — this alert won't fire. Raise the value or add a checkpoint to correct the prediction.")
                }

                Button {
                    let current = parsedDraftTarget ?? session.targetBatteryPercent ?? 80
                    let next = min(current + 1, 100)
                    draftTargetText = next.format(decimalDigits: 0)
                } label: {
                    Image(systemName: "plus.circle")
                        .font(.title3)
                }
                .buttonStyle(.plain)

                Button {
                    if let value = parsedDraftTarget {
                        _ = appData.setTargetBatteryPercent(value, for: session.id)
                    }
                    showingInlineTargetEditor = false
                } label: {
                    Image(systemName: "checkmark.circle.fill")
                        .foregroundColor(parsedDraftTarget != nil ? .indigo : .secondary)
                        .font(.title3)
                }
                .buttonStyle(.plain)
                .disabled(parsedDraftTarget == nil)

                Button {
                    showingInlineTargetEditor = false
                    draftTargetText = ""
                } label: {
                    Image(systemName: "xmark.circle")
                        .foregroundColor(.secondary)
                        .font(.title3)
                }
                .buttonStyle(.plain)

            } else {
                if let targetPercent = session.targetBatteryPercent {
                    Text("\(targetPercent.format(decimalDigits: 0))%")
                        .font(.subheadline.weight(.semibold))
                        .foregroundColor(savedBelowPrediction ? .orange : .indigo)

                    if savedBelowPrediction {
                        Button {} label: {
                            Image(systemName: "exclamationmark.triangle.fill")
                                .font(.callout.weight(.semibold))
                                .foregroundColor(.orange)
                        }
                        .buttonStyle(.plain)
                        .help("Battery is already predicted at \(predictedPercent!.format(decimalDigits: 0))% — this alert won't fire. Raise the value or add a checkpoint to correct the prediction.")
                    }

                    Button {
                        _ = appData.setTargetBatteryPercent(nil, for: session.id)
                    } label: {
                        Image(systemName: "xmark.circle.fill")
                            .foregroundColor(.secondary)
                            .font(.callout)
                    }
                    .buttonStyle(.plain)
                    .help("Remove alert")
                }

                Button {
                    draftTargetText = session.targetBatteryPercent.map {
                        $0.format(decimalDigits: 0)
                    } ?? "80"
                    showingInlineTargetEditor = true
                } label: {
                    Image(systemName: session.targetBatteryPercent == nil ? "plus" : "pencil")
                        .font(.caption.weight(.semibold))
                        .frame(width: 30, height: 30)
                        .contentShape(Rectangle())
                }
                .meterCard(tint: .indigo, fillOpacity: 0.12, strokeOpacity: 0.18, cornerRadius: 10)
                .buttonStyle(.plain)
                .help(session.targetBatteryPercent == nil ? "Set battery alert" : "Edit battery alert")
            }
        }
    }

    private var parsedDraftTarget: Double? {
        let normalized = draftTargetText
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: ",", with: ".")
        guard let value = Double(normalized), value >= 1, value <= 100 else { return nil }
        return value
    }

    private func stopConfirmPanel(for session: ChargeSessionSummary) -> some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Final Checkpoint (optional)")
                .font(.subheadline.weight(.semibold))

            HStack(spacing: 8) {
                ForEach([FinalCheckpoint.full, .skip, .custom], id: \.self) { mode in
                    Button {
                        finalCheckpointMode = mode
                        if mode != .custom { finalCheckpointText = "" }
                    } label: {
                        VStack(spacing: 5) {
                            Image(systemName: mode.icon)
                                .font(.title3)
                                .foregroundColor(finalCheckpointMode == mode ? .primary : .secondary)
                            Text(mode.label)
                                .font(.caption.weight(.semibold))
                                .foregroundColor(finalCheckpointMode == mode ? .primary : .secondary)
                        }
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 10)
                        .background(finalCheckpointMode == mode ? Color.primary.opacity(0.10) : Color.clear)
                        .meterCard(
                            tint: finalCheckpointMode == mode ? .primary : .secondary,
                            fillOpacity: finalCheckpointMode == mode ? 0.08 : 0.04,
                            strokeOpacity: finalCheckpointMode == mode ? 0.20 : 0.10,
                            cornerRadius: 12
                        )
                    }
                    .buttonStyle(.plain)
                }
            }

            if finalCheckpointMode == .custom {
                HStack(spacing: 8) {
                    Button { adjustFinalCheckpoint(by: -1) } label: {
                        Image(systemName: "minus.circle").font(.title3)
                    }
                    .buttonStyle(.plain)

                    TextField("—", text: $finalCheckpointText)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                        .frame(width: 56)
                        .multilineTextAlignment(.center)

                    Text("%").foregroundColor(.secondary)

                    Button { adjustFinalCheckpoint(by: 1) } label: {
                        Image(systemName: "plus.circle").font(.title3)
                    }
                    .buttonStyle(.plain)

                    Spacer()
                }
            }

            HStack(spacing: 8) {
                Button("Discard") {
                    _ = appData.deleteChargeSession(sessionID: session.id)
                    showingStopConfirm = false
                    finalCheckpointText = ""
                    finalCheckpointMode = .full
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 9)
                .meterCard(tint: .secondary, fillOpacity: 0.10, strokeOpacity: 0.14, cornerRadius: 14)
                .buttonStyle(.plain)

                let saveDisabled = finalCheckpointMode == .custom
                    && finalCheckpointText.isEmpty == false
                    && parsedFinalCheckpoint == nil

                Button("Save") {
                    _ = appData.stopChargeSession(
                        sessionID: session.id,
                        finalBatteryPercent: resolvedFinalCheckpoint
                    )
                    showingStopConfirm = false
                    finalCheckpointText = ""
                    finalCheckpointMode = .full
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 9)
                .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                .buttonStyle(.plain)
                .disabled(saveDisabled)

                Button("Cancel") {
                    showingStopConfirm = false
                    finalCheckpointText = ""
                    finalCheckpointMode = .full
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 9)
                .meterCard(tint: .secondary, fillOpacity: 0.10, strokeOpacity: 0.14, cornerRadius: 14)
                .buttonStyle(.plain)
            }
        }
        .padding(14)
        .meterCard(tint: .red, fillOpacity: 0.06, strokeOpacity: 0.14, cornerRadius: 16)
    }

    private var parsedFinalCheckpoint: Double? {
        let normalized = finalCheckpointText
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: ",", with: ".")
        guard let value = Double(normalized), value >= 0, value <= 100 else { return nil }
        return value
    }

    private var resolvedFinalCheckpoint: Double? {
        switch finalCheckpointMode {
        case .full:   return 100.0
        case .skip:   return nil
        case .custom: return parsedFinalCheckpoint
        }
    }

    private func adjustFinalCheckpoint(by delta: Double) {
        let current = parsedFinalCheckpoint ?? 0
        let next = min(max(current + delta, 0), 100)
        finalCheckpointText = next.format(decimalDigits: 0)
    }

    // MARK: - Trim Detection Banner

    @ViewBuilder
    private func trimDetectionBanner(for session: ChargeSessionSummary) -> some View {
        if let window = detectedTrimWindow {
            HStack(spacing: 12) {
                Image(systemName: "scissors.circle.fill")
                    .font(.title3)
                    .foregroundColor(.blue)

                VStack(alignment: .leading, spacing: 2) {
                    Text("Charging ended early")
                        .font(.subheadline.weight(.semibold))
                    Text("Active charging detected between \(window.start.format(as: "HH:mm")) and \(window.end.format(as: "HH:mm")). The rest may be standby or another device.")
                        .font(.caption)
                        .foregroundColor(.secondary)
                        .fixedSize(horizontal: false, vertical: true)
                }

                Spacer(minLength: 0)

                VStack(spacing: 6) {
                    Button("Apply") {
                        _ = appData.setSessionTrim(
                            sessionID: session.id,
                            start: window.start,
                            end: window.end
                        )
                        trimBannerDismissedForSessionID = session.id
                    }
                    .font(.caption.weight(.semibold))
                    .buttonStyle(.borderedProminent)
                    .controlSize(.small)
                    .tint(.blue)

                    Button {
                        trimBannerDismissedForSessionID = session.id
                    } label: {
                        Image(systemName: "xmark")
                            .font(.caption2.weight(.semibold))
                            .foregroundColor(.secondary)
                    }
                    .buttonStyle(.plain)
                }
            }
            .padding(14)
            .background(
                RoundedRectangle(cornerRadius: 14)
                    .fill(Color.blue.opacity(0.10))
                    .overlay(RoundedRectangle(cornerRadius: 14).stroke(Color.blue.opacity(0.22), lineWidth: 1))
            )
            .transition(.opacity.combined(with: .move(edge: .top)))
        }
    }

    private func sessionChartCard(timeRange: ClosedRange<Date>?, session: ChargeSessionSummary) -> some View {
        let hasRangeSelector = session.aggregatedSamples.isEmpty == false

        return VStack(alignment: .leading, spacing: 12) {
            HStack(spacing: 8) {
                Image(systemName: "chart.xyaxis.line")
                    .foregroundColor(.blue)
                Text("Session Chart")
                    .font(.headline)
                ContextInfoButton(
                    title: "Session Chart",
                    message: usesChargeRecordBuffer(for: session)
                        ? "This chart combines the persisted session curve with current live data from this meter."
                        : "This chart is scoped to the saved session window for \(session.sessionKind.shortTitle.lowercased()) charging."
                )
                Spacer(minLength: 0)
            }

            MeasurementChartView(
                timeRange: timeRange,
                timeRangeLowerBound: sessionChartLiveTrimBounds(for: session).lower,
                timeRangeUpperBound: sessionChartLiveTrimBounds(for: session).upper,
                showsRangeSelector: hasRangeSelector,
                rebasesEnergyToVisibleRangeStart: true,
                extendsTimelineToPresent: false,
                showsTemperatureSeries: false,
                rangeSelectorConfiguration: hasRangeSelector
                    ? MeasurementChartRangeSelectorConfiguration(
                        keepAction: MeasurementChartSelectionAction(
                            title: "Keep Selection",
                            shortTitle: "Keep",
                            systemName: "scissors",
                            tone: .destructive,
                            handler: { range in
                                _ = appData.setSessionTrim(
                                    sessionID: session.id,
                                    start: range.lowerBound,
                                    end: range.upperBound
                                )
                                trimBannerDismissedForSessionID = session.id
                            }
                        ),
                        removeAction: nil,
                        resetAction: MeasurementChartResetAction(
                            title: "Reset Trim",
                            shortTitle: "Reset",
                            systemName: "arrow.counterclockwise",
                            tone: .reversible,
                            confirmationTitle: "Reset session trim?",
                            confirmationButtonTitle: "Reset trim",
                            handler: {
                                _ = appData.setSessionTrim(sessionID: session.id, start: nil, end: nil)
                            }
                        )
                    )
                    : nil
            )
            .environmentObject(usbMeter.chargeRecordMeasurements)
            .frame(maxWidth: .infinity, alignment: .topLeading)
        }
        .padding(18)
        .meterCard(tint: .blue, fillOpacity: 0.14, strokeOpacity: 0.20)
    }

    private var meterTotalsCard: some View {
        return VStack(alignment: .leading, spacing: 12) {
            HStack(spacing: 8) {
                Text("Meter Recorder")
                    .font(.headline)

                Spacer(minLength: 0)

                Button {
                    showsMeterTotalsInfo.toggle()
                } label: {
                    Image(systemName: "info.circle")
                        .font(.body.weight(.semibold))
                        .foregroundColor(.secondary)
                }
                .buttonStyle(.plain)
                .accessibilityLabel("Meter recorder info")
                .popover(isPresented: $showsMeterTotalsInfo, arrowEdge: .top) {
                    VStack(alignment: .leading, spacing: 10) {
                        Text("Meter Recorder")
                            .font(.headline)
                        Text("These values come directly from the meter's built-in recorder. Keep them visible while comparing the app session against what the meter captured on its own.")
                            .font(.body)
                            .fixedSize(horizontal: false, vertical: true)
                    }
                    .padding(16)
                    .frame(width: 280, alignment: .leading)
                }
            }

            ChargeRecordMetricsTableView(
                labels: ["Energy", "Duration", "Meter Threshold"],
                values: [
                    "\(usbMeter.recordedWH.format(decimalDigits: 3)) Wh",
                    usbMeter.recordingDurationDescription,
                    usbMeter.supportsRecordingThreshold ? "\(usbMeter.recordingTreshold.format(decimalDigits: 2)) A" : "Read-only"
                ]
            )

            if let recordingBootedAt = usbMeter.recordingBootedAt {
                Text("Recorder uptime suggests the meter booted at \(recordingBootedAt.format()).")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
        }
        .padding(18)
        .meterCard(tint: .teal, fillOpacity: 0.14, strokeOpacity: 0.20)
    }

    // MARK: - Helpers

    private func setupRow<Content: View>(
        icon: String,
        iconColor: Color = .secondary,
        @ViewBuilder content: () -> Content
    ) -> some View {
        HStack(spacing: 10) {
            Image(systemName: icon)
                .foregroundColor(iconColor)
                .font(.body.weight(.medium))
                .frame(width: 22, alignment: .center)
            content()
        }
        .padding(.horizontal, 14)
        .padding(.vertical, 11)
    }

    private func autoStopLabel(for session: ChargeSessionSummary) -> String {
        if session.autoStopEnabled == false {
            return "Manual"
        }
        if let sessionWarning = sessionWarning(for: session), session.chargingTransportMode == .wireless {
            return sessionWarning.contains("idle-current") ? "Blocked" : "Manual"
        }
        if session.stopThresholdAmps > 0 {
            return "\(session.stopThresholdAmps.format(decimalDigits: 2)) A"
        }
        return "Learning"
    }

    private func sessionMetricRows(
        for session: ChargeSessionSummary,
        displayedEnergyWh: Double
    ) -> [SessionMetricRow] {
        var rows: [SessionMetricRow] = []

        if shouldShowChargingTransport(for: session) {
            rows.append(SessionMetricRow(label: "Type", value: session.chargingTransportMode.title))
        }

        if shouldShowChargingState(for: session) {
            rows.append(SessionMetricRow(label: "Mode", value: session.chargingStateMode.title))
        }

        rows.append(SessionMetricRow(label: "Energy", value: "\(displayedEnergyWh.format(decimalDigits: 3)) Wh"))
        rows.append(SessionMetricRow(label: "Duration", value: formatDuration(displayedSessionDuration(for: session))))
        rows.append(SessionMetricRow(label: "Auto Stop", value: autoStopLabel(for: session)))
        return rows
    }

    private func shouldShowChargingTransport(for session: ChargeSessionSummary) -> Bool {
        guard let selectedChargedDevice else { return true }
        return selectedChargedDevice.supportedChargingModes.count > 1
            || selectedChargedDevice.supportedChargingModes.contains(session.chargingTransportMode) == false
    }

    private func shouldShowChargingState(for session: ChargeSessionSummary) -> Bool {
        guard let selectedChargedDevice else { return true }
        return selectedChargedDevice.supportedChargingStateModes.count > 1
            || selectedChargedDevice.supportedChargingStateModes.contains(session.chargingStateMode) == false
    }

    private func displayedSessionEnergyWh(for session: ChargeSessionSummary) -> Double {
        let storedEnergyWh = session.effectiveOrMeasuredEnergyWh
        guard session.isTrimmed == false else { return storedEnergyWh }
        guard session.status.isOpen else { return storedEnergyWh }
        guard session.meterMACAddress == meterMACAddress else { return storedEnergyWh }
        if let baselineEnergyWh = session.meterEnergyBaselineWh {
            return max(storedEnergyWh, max(usbMeter.recordedWH - baselineEnergyWh, 0))
        }
        return storedEnergyWh
    }

    private func displayedSessionChargeAh(for session: ChargeSessionSummary) -> Double {
        let storedChargeAh = session.measuredChargeAh
        guard session.isTrimmed == false else { return storedChargeAh }
        guard session.status.isOpen else { return storedChargeAh }
        guard session.meterMACAddress == meterMACAddress else { return storedChargeAh }
        if let baselineChargeAh = session.meterChargeBaselineAh {
            return max(storedChargeAh, max(usbMeter.recordedAH - baselineChargeAh, 0))
        }
        return storedChargeAh
    }

    private func displayedSessionDuration(for session: ChargeSessionSummary) -> TimeInterval {
        let storedDuration = max(session.effectiveDuration, 0)
        guard session.isTrimmed == false else { return storedDuration }
        guard session.status.isOpen else { return storedDuration }
        guard session.meterMACAddress == meterMACAddress else { return storedDuration }
        return max(storedDuration, max(usbMeter.chargeRecordDuration, 0))
    }

    private func formatDuration(_ duration: TimeInterval) -> String {
        let totalSeconds = Int(duration.rounded(.down))
        let hours = totalSeconds / 3600
        let minutes = (totalSeconds % 3600) / 60
        let seconds = totalSeconds % 60
        if hours > 0 {
            return String(format: "%d:%02d:%02d", hours, minutes, seconds)
        }
        return String(format: "%02d:%02d", minutes, seconds)
    }

    private func sessionWarning(for session: ChargeSessionSummary) -> String? {
        guard session.chargingTransportMode == .wireless,
              let chargerID = session.chargerID,
              let charger = appData.chargedDeviceSummary(id: chargerID) else {
            return nil
        }
        guard charger.chargerIdleCurrentAmps == nil else { return nil }
        return "The selected charger has no idle-current measurement. Wireless stop-threshold learning and precise auto-stop are unavailable for this session."
    }

    private func startSession() {
        guard let selectedChargedDevice,
              let chargingTransportMode = selectedDraftTransportMode,
              let chargingStateMode = selectedDraftChargingStateMode else {
            return
        }

        let chargerID = chargingTransportMode == .wireless ? selectedCharger?.id : nil
        let didStart = appData.startChargeSession(
            for: usbMeter,
            chargedDeviceID: selectedChargedDevice.id,
            chargerID: chargerID,
            chargingTransportMode: chargingTransportMode,
            chargingStateMode: chargingStateMode,
            autoStopEnabled: false,
            initialBatteryPercent: initialCheckpointMode == .known ? initialCheckpointValue : nil,
            startsFromFlatBattery: initialCheckpointMode == .flat
        )

        if didStart {
            initialCheckpoint = ""
            initialCheckpointMode = .known
        }
    }

    private func adjustInitialCheckpoint(by delta: Double) {
        guard initialCheckpointMode == .known else { return }
        let currentValue = initialCheckpointValue ?? 0
        let nextValue = min(max(currentValue + delta, 0), 100)
        initialCheckpoint = nextValue.format(decimalDigits: 0)
    }

    private func syncDraftSelections() {
        guard let selectedChargedDevice else {
            draftChargingTransportMode = nil
            draftChargingStateMode = nil
            return
        }

        if let openChargeSession {
            draftChargingTransportMode = openChargeSession.chargingTransportMode
            draftChargingStateMode = openChargeSession.chargingStateMode
            return
        }

        if let draftChargingTransportMode,
           selectedChargedDevice.supportedChargingModes.contains(draftChargingTransportMode) == false {
            self.draftChargingTransportMode = nil
        }

        if let draftChargingStateMode,
           selectedChargedDevice.supportedChargingStateModes.contains(draftChargingStateMode) == false {
            self.draftChargingStateMode = nil
        }

        if selectedChargedDevice.supportedChargingModes.count == 1 {
            draftChargingTransportMode = selectedChargedDevice.supportedChargingModes.first
        }

        if let draftChargingTransportMode {
            draftChargingStateMode = draftChargingStateMode
                ?? selectedChargedDevice.defaultChargingStateMode(for: draftChargingTransportMode)
        } else if selectedChargedDevice.supportedChargingStateModes.count == 1 {
            draftChargingStateMode = selectedChargedDevice.supportedChargingStateModes.first
        }
    }

    private struct CompactSelectionOption: Identifiable {
        let id: String
        let title: String
        let isSelected: Bool
        let action: () -> Void
    }

    private func compactSelectionMenu(
        title: String,
        options: [CompactSelectionOption]
    ) -> some View {
        Menu {
            ForEach(options) { option in
                Button {
                    option.action()
                } label: {
                    if option.isSelected {
                        Label(option.title, systemImage: "checkmark")
                    } else {
                        Text(option.title)
                    }
                }
            }
        } label: {
            HStack(spacing: 8) {
                Text(title)
                    .foregroundColor(.primary)
                Spacer()
                Image(systemName: "chevron.up.chevron.down")
                    .font(.caption.weight(.semibold))
                    .foregroundColor(.secondary)
            }
            .padding(.horizontal, 12)
            .padding(.vertical, 9)
            .frame(width: 160, alignment: .leading)
            .meterCard(tint: .secondary, fillOpacity: 0.08, strokeOpacity: 0.14, cornerRadius: 12)
        }
        .buttonStyle(.plain)
    }
}