// LoopFollow
// TreatmentsView.swift

import SwiftUI

private func formatBG(_ mgdl: Double, units: String = Storage.shared.units.value) -> String {
    if units == "mg/dL" {
        return "\(Int(round(mgdl))) mg/dL"
    }

    let mmolValue = mgdl * GlucoseConversion.mgDlToMmolL
    return String(format: "%.1f mmol\u{2060}/\u{2060}L", mmolValue)
}

private func formatBG(_ mgdlValue: Int, units: String = Storage.shared.units.value) -> String {
    formatBG(Double(mgdlValue), units: units)
}

struct TreatmentsView: View {
    var onBack: (() -> Void)?

    @StateObject private var viewModel = TreatmentsViewModel()
    @State private var selectedFilter: TreatmentFilter = .all
    @ObservedObject private var device = Storage.shared.device
    @ObservedObject private var graphTimeZoneEnabled = Storage.shared.graphTimeZoneEnabled
    @ObservedObject private var graphTimeZoneIdentifier = Storage.shared.graphTimeZoneIdentifier

    private var isLoopDevice: Bool {
        device.value == "Loop"
    }

    private var isTrioDevice: Bool {
        device.value == "Trio"
    }

    private var automaticFilterLabel: String {
        if isTrioDevice {
            return "SMB"
        }
        if isLoopDevice {
            return "Automatic"
        }
        if viewModel.hasAutomaticEntries {
            return "Automatic"
        }
        if viewModel.hasSMBEntries {
            return "SMB"
        }
        return "Automatic"
    }

    private var automaticLabelIsSMB: Bool {
        automaticFilterLabel == "SMB"
    }

    var body: some View {
        NavigationView {
            VStack {
                Text("Treatments")
                    .font(.largeTitle.weight(.bold))
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal)
                    .padding(.top, 1)

                // Filter Pickers
                VStack(spacing: 12) {
                    Picker("Filter", selection: $selectedFilter) {
                        ForEach(TreatmentFilter.primaryFilters, id: \.self) { filter in
                            Text(filter.displayName(automaticLabel: automaticFilterLabel)).tag(filter)
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())

                    Picker("Bolus Filter", selection: $selectedFilter) {
                        ForEach(TreatmentFilter.bolusFilters(isLoopDevice: isLoopDevice, isTrioDevice: isTrioDevice, automaticLabelIsSMB: automaticLabelIsSMB), id: \.self) { filter in
                            Text(filter.displayName(automaticLabel: automaticFilterLabel)).tag(filter)
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                .padding(.horizontal)
                .padding(.top, 1)
                .padding(.bottom, 1)

                if let timeZoneNoticeText {
                    HStack(spacing: 6) {
                        Image(systemName: "globe")
                        Text(timeZoneNoticeText)
                    }
                    .font(.caption)
                    .foregroundColor(.secondary)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal)
                    .padding(.bottom, 0)
                    .background(Color(.systemBackground))
                }

                List {
                    if viewModel.isInitialLoading {
                        HStack {
                            Spacer()
                            ProgressView()
                                .padding()
                            Spacer()
                        }
                    } else if filteredGroupedTreatments.isEmpty {
                        Text("No recent treatments")
                            .foregroundColor(.secondary)
                            .padding()
                    } else {
                        ForEach(sortedDayKeys, id: \.self) { dayKey in
                            Section {
                                ForEach(dayRowsForDay(dayKey)) { row in
                                    if let hourLabel = row.hourLabel {
                                        Text(hourLabel)
                                            .font(.caption.weight(.semibold))
                                            .foregroundColor(.secondary)
                                            .frame(maxWidth: .infinity, alignment: .leading)
                                            .padding(.horizontal, 16)
                                            .padding(.top, 6)
                                            .padding(.bottom, 2)
                                            .background(Color(.systemBackground))
                                    } else if let treatment = row.treatment {
                                        TreatmentRow(treatment: treatment)
                                    }
                                }
                            } header: {
                                ZStack(alignment: .leading) {
                                    Color(.systemBackground)
                                    Text(formatDayHeader(dayKey))
                                        .frame(maxWidth: .infinity, alignment: .leading)
                                        .padding(.horizontal, 1)
                                        .padding(.top, 1)
                                        .padding(.bottom, 2)
                                }
                                .frame(maxWidth: .infinity)
                            }
                        }

                        Section {
                            if viewModel.isLoadingMore {
                                HStack {
                                    Spacer()
                                    ProgressView()
                                        .padding()
                                    Spacer()
                                }
                            } else {
                                Button(action: {
                                    viewModel.loadMoreIfNeeded()
                                }) {
                                    HStack {
                                        Spacer()
                                        VStack {
                                            Text("Load More")
                                                .font(.headline)
                                                .foregroundColor(.blue)
                                            Text("Tap to load older treatments")
                                                .font(.caption)
                                                .foregroundColor(.secondary)
                                        }
                                        Spacer()
                                    }
                                    .padding(.vertical, 8)
                                }
                            }
                        }
                    }
                }
                .listStyle(.plain)
                .environment(\.defaultMinListHeaderHeight, 0)
                .overlay(alignment: .top) {
                    Color(.systemBackground)
                        .frame(height: 12)
                        .offset(y: -1)
                        .allowsHitTesting(false)
                }
                .navigationTitle("")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    if let onBack {
                        ToolbarItem(placement: .navigationBarLeading) {
                            Button(action: onBack) {
                                Image(systemName: "chevron.left")
                            }
                        }
                    }
                }
                .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
                .refreshable {
                    viewModel.refreshTreatments()
                }
                .onAppear {
                    if viewModel.groupedTreatments.isEmpty {
                        viewModel.loadInitialTreatments()
                    }
                    normalizeSelectedFilter(for: device.value)
                }
                .onChange(of: device.value) { newValue in
                    normalizeSelectedFilter(for: newValue)
                }
            }
        }
    }

    // Filtered grouped treatments based on selectedFilter
    private var filteredGroupedTreatments: [String: [Treatment]] {
        switch selectedFilter {
        case .all:
            return viewModel.groupedTreatments
        case .carb:
            return groupedTreatments(matching: [.carb])
        case .basal:
            return groupedTreatments(matching: [.tempBasal])
        case .bolusAll:
            return groupedTreatments(matching: [.bolusAutomatic, .bolusManual, .smb])
        case .bolusAutomatic:
            if automaticLabelIsSMB {
                return groupedTreatments(matching: [.bolusAutomatic, .smb])
            }
            return groupedTreatments(matching: [.bolusAutomatic])
        case .bolusSMB:
            return groupedTreatments(matching: [.smb])
        case .bolusManual:
            return groupedTreatments(matching: [.bolusManual])
        }
    }

    private func groupedTreatments(matching types: Set<TreatmentType>) -> [String: [Treatment]] {
        var filtered: [String: [Treatment]] = [:]
        for (key, treatments) in viewModel.groupedTreatments {
            let filteredTreatments = treatments.filter { types.contains($0.type) }
            if !filteredTreatments.isEmpty {
                filtered[key] = filteredTreatments
            }
        }
        return filtered
    }

    private func normalizeSelectedFilter(for _: String) {
        let availableFilters = TreatmentFilter.primaryFilters + TreatmentFilter.bolusFilters(isLoopDevice: isLoopDevice, isTrioDevice: isTrioDevice, automaticLabelIsSMB: automaticLabelIsSMB)
        if !availableFilters.contains(selectedFilter) {
            selectedFilter = .bolusAll
        }
    }

    private var sortedDayKeys: [String] {
        Array(Set(filteredGroupedTreatments.keys.compactMap(dayKey(from:))))
            .sorted { lhs, rhs in
                let lhsDate = dayKeyDate(lhs)
                let rhsDate = dayKeyDate(rhs)

                switch (lhsDate, rhsDate) {
                case let (left?, right?):
                    return left > right
                case (_?, nil):
                    return true
                case (nil, _?):
                    return false
                case (nil, nil):
                    return lhs > rhs
                }
            }
    }

    private func treatmentsForDay(_ daySectionKey: String) -> [Treatment] {
        let matchingHourKeys = filteredGroupedTreatments.keys.filter { dayKey(from: $0) == daySectionKey }
        return matchingHourKeys
            .flatMap { filteredGroupedTreatments[$0] ?? [] }
            .sorted { $0.date > $1.date }
    }

    private func dayRowsForDay(_ daySectionKey: String) -> [DayRow] {
        let treatments = treatmentsForDay(daySectionKey)
        var rows: [DayRow] = []
        var previousHourKey: String?

        for treatment in treatments {
            if treatment.hourKey != previousHourKey {
                rows.append(
                    DayRow(
                        id: "hour-\(treatment.hourKey)",
                        hourLabel: formatHourBanner(treatment.hourKey),
                        treatment: nil
                    )
                )
                previousHourKey = treatment.hourKey
            }

            rows.append(
                DayRow(
                    id: "treatment-\(treatment.id)",
                    hourLabel: nil,
                    treatment: treatment
                )
            )
        }

        return rows
    }

    private func hourKeyDate(_ hourKey: String) -> Date? {
        let components = hourKey.split(separator: "-")
        guard components.count == 4,
              let year = Int(components[0]),
              let month = Int(components[1]),
              let day = Int(components[2]),
              let hour = Int(components[3])
        else {
            return nil
        }

        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day
        dateComponents.hour = hour

        let calendar = dateTimeUtils.displayCalendar()
        return calendar.date(from: dateComponents)
    }

    private func formatHourBanner(_ hourKey: String) -> String {
        guard let date = hourKeyDate(hourKey) else {
            return hourKey
        }

        let formatter = DateFormatter()
        formatter.locale = Locale.current
        if dateTimeUtils.is24Hour() {
            formatter.dateFormat = "HH:mm"
        } else {
            formatter.dateFormat = "h a"
        }
        dateTimeUtils.applyDisplayTimeZone(to: formatter)
        return formatter.string(from: date)
    }

    private func dayKey(from hourKey: String) -> String? {
        let components = hourKey.split(separator: "-")
        guard components.count == 4 else { return nil }
        return "\(components[0])-\(components[1])-\(components[2])"
    }

    private func dayKeyDate(_ dayKey: String) -> Date? {
        let components = dayKey.split(separator: "-")
        guard components.count == 3,
              let year = Int(components[0]),
              let month = Int(components[1]),
              let day = Int(components[2])
        else {
            return nil
        }

        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day

        let calendar = dateTimeUtils.displayCalendar()
        return calendar.date(from: dateComponents)
    }

    private func formatDayHeader(_ dayKey: String) -> String {
        guard let date = dayKeyDate(dayKey) else {
            return dayKey
        }

        let dayDateFormatter = DateFormatter()
        dayDateFormatter.locale = Locale.current
        dayDateFormatter.setLocalizedDateFormatFromTemplate("EEEE MMM d")
        dateTimeUtils.applyDisplayTimeZone(to: dayDateFormatter)

        return dayDateFormatter.string(from: date)
    }

    private var timeZoneNoticeText: String? {
        guard graphTimeZoneEnabled.value,
              let overrideTimeZone = TimeZone(identifier: graphTimeZoneIdentifier.value)
        else {
            return nil
        }

        return "Time Zone Override On: \(overrideTimeZone.identifier)"
    }
}

private struct DayRow: Identifiable {
    let id: String
    let hourLabel: String?
    let treatment: Treatment?
}

struct TreatmentDetailView: View {
    let treatment: Treatment
    @StateObject private var viewModel = TreatmentDetailViewModel()

    var body: some View {
        List {
            // Treatment Info Section (no header)
            Section {
                HStack {
                    Image(systemName: treatment.icon)
                        .foregroundColor(treatment.color)
                        .opacity(treatment.type == .tempBasal ? 0.5 : 1.0)
                        .frame(width: 24)
                    Text(treatment.title)
                        .font(.headline)
                    if let subtitle = treatment.subtitle {
                        Text(subtitle)
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                    }
                    Spacer()
                }
            }

            // Glucose at time
            if viewModel.isLoading {
                Section {
                    HStack {
                        Spacer()
                        ProgressView()
                            .padding()
                        Spacer()
                    }
                }
            } else if let detail = viewModel.detail, detail.glucose > 0 {
                Section(header: Text("Glucose value")) {
                    HStack {
                        Text(formatBG(detail.glucose))
                        Spacer()
                    }
                }
            }

            if !viewModel.isLoading, let detail = viewModel.detail {
                if detail.iob != nil || detail.cob != nil || detail.eventualBG != nil {
                    Section(header: Text("Device Data")) {
                        HStack(spacing: 20) {
                            if let iob = detail.iob {
                                VStack(alignment: .leading) {
                                    Text("IOB")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                    Text(String(format: "%.2f U", iob))
                                        .font(.headline)
                                }
                                .frame(maxWidth: .infinity, alignment: .leading)
                            }

                            if let cob = detail.cob {
                                VStack(alignment: .leading) {
                                    Text("COB")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                    Text(String(format: "%.0f g", cob))
                                        .font(.headline)
                                }
                                .frame(maxWidth: .infinity, alignment: .leading)
                            }

                            if let eventualBG = detail.eventualBG, eventualBG > 0 {
                                VStack(alignment: .leading) {
                                    Text("Eventual")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                    Text(formatBG(eventualBG))
                                        .font(.headline)
                                }
                                .frame(maxWidth: .infinity, alignment: .leading)
                            }
                        }
                        .padding(.vertical, 4)
                    }
                }

                // Prediction Range
                if detail.minBG > 0 || detail.maxBG > 0 {
                    Section(header: Text("Prediction")) {
                        if detail.minBG > 0 && detail.maxBG > 0 {
                            VStack(alignment: .leading, spacing: 4) {
                                HStack {
                                    Text("Min")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                    Spacer()
                                    Text(formatBG(detail.minBG))
                                        .font(.subheadline)
                                }
                                HStack {
                                    Text("Max")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                    Spacer()
                                    Text(formatBG(detail.maxBG))
                                        .font(.subheadline)
                                }
                            }
                        } else if detail.minBG > 0 {
                            HStack {
                                Text("Minimum")
                                Spacer()
                                Text(formatBG(detail.minBG))
                                    .foregroundColor(.secondary)
                            }
                        } else if detail.maxBG > 0 {
                            HStack {
                                Text("Maximum")
                                Spacer()
                                Text(formatBG(detail.maxBG))
                                    .foregroundColor(.secondary)
                            }
                        }
                    }
                }

                // Active Override
                if let overrideName = detail.overrideName {
                    Section(header: Text("Active Override")) {
                        HStack {
                            Text(overrideName)
                            Spacer()
                        }
                        if let multiplier = detail.overrideMultiplier {
                            HStack {
                                Text("Sensitivity")
                                Spacer()
                                Text(String(format: "%.0f%%", multiplier * 100))
                                    .foregroundColor(.secondary)
                            }
                        }
                        if let targetRange = detail.overrideTargetRange {
                            HStack {
                                Text("Target Range")
                                Spacer()
                                Text("\(formatBG(targetRange.min)) - \(formatBG(targetRange.max))")
                                    .foregroundColor(.secondary)
                            }
                        }
                    }
                }

                // Recommended Bolus
                if let recommendedBolus = detail.recommendedBolus, recommendedBolus > 0 {
                    Section(header: Text("Recommended Bolus")) {
                        HStack {
                            Text(String(format: "%.2f U", recommendedBolus))
                            Spacer()
                        }
                    }
                }
            }
        }
        .navigationTitle(formatNavigationTitle(treatment.date))
        .navigationBarTitleDisplayMode(.inline)
        .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
        .onAppear {
            viewModel.loadDetails(for: treatment)
        }
    }

    private func formatNavigationTitle(_ timeInterval: TimeInterval) -> String {
        let date = Date(timeIntervalSince1970: timeInterval)
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .short
        dateTimeUtils.applyDisplayTimeZone(to: formatter)
        let fullString = formatter.string(from: date)
        // Remove " at " if it exists (some locales use it)
        return fullString.replacingOccurrences(of: " at ", with: " ")
    }
}

struct DeviceStatusData {
    let timestamp: TimeInterval
    let iob: Double?
    let cob: Double?
    let eventualBG: Int?
    let minPredictedBG: Int?
    let maxPredictedBG: Int?
    let overrideName: String?
    let overrideMultiplier: Double?
    let overrideTargetRange: (min: Int, max: Int)?
    let recommendedBolus: Double?
}

struct TreatmentDetailData {
    let glucose: Int
    let iob: Double?
    let cob: Double?
    let eventualBG: Int?
    let minBG: Int
    let maxBG: Int
    let loopStatus: String?
    let overrideName: String?
    let overrideMultiplier: Double?
    let overrideTargetRange: (min: Int, max: Int)?
    let recommendedBolus: Double?
}

class TreatmentDetailViewModel: ObservableObject {
    @Published var detail: TreatmentDetailData?
    @Published var isLoading = false

    func loadDetails(for treatment: Treatment) {
        guard let mainVC = getMainViewController() else { return }

        isLoading = true

        // Find closest BG reading
        let glucose = findNearestBG(at: treatment.date, in: mainVC.bgData)

        // Fetch historical device status from Nightscout
        fetchDeviceStatusHistory(around: treatment.date) { [weak self] deviceStatus in
            guard let self = self else { return }

            DispatchQueue.main.async {
                self.detail = TreatmentDetailData(
                    glucose: glucose,
                    iob: deviceStatus?.iob,
                    cob: deviceStatus?.cob,
                    eventualBG: deviceStatus?.eventualBG,
                    minBG: deviceStatus?.minPredictedBG ?? 0,
                    maxBG: deviceStatus?.maxPredictedBG ?? 0,
                    loopStatus: nil,
                    overrideName: deviceStatus?.overrideName,
                    overrideMultiplier: deviceStatus?.overrideMultiplier,
                    overrideTargetRange: deviceStatus?.overrideTargetRange,
                    recommendedBolus: deviceStatus?.recommendedBolus
                )
                self.isLoading = false
            }
        }
    }

    private func fetchDeviceStatusHistory(around timestamp: TimeInterval, completion: @escaping (DeviceStatusData?) -> Void) {
        // Fetch device status entries around the treatment time
        // We'll get a range of entries to find the closest one
        let targetDate = Date(timeIntervalSince1970: timestamp)
        let startDate = targetDate.addingTimeInterval(-30 * 60) // 30 minutes before
        let endDate = targetDate.addingTimeInterval(30 * 60) // 30 minutes after

        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

        // Build parameters
        let parameters: [String: String] = [
            "find[created_at][$gte]": formatter.string(from: startDate),
            "find[created_at][$lte]": formatter.string(from: endDate),
            "count": "30",
        ]

        NightscoutUtils.executeDynamicRequest(eventType: .deviceStatus, parameters: parameters) { result in
            switch result {
            case let .success(json):
                if let jsonDeviceStatus = json as? [[String: AnyObject]] {
                    // Find the entry closest to our target timestamp
                    let deviceStatus = self.parseClosestDeviceStatus(from: jsonDeviceStatus, targetTimestamp: timestamp)
                    completion(deviceStatus)
                } else {
                    completion(nil)
                }
            case .failure:
                completion(nil)
            }
        }
    }

    private func parseClosestDeviceStatus(from jsonArray: [[String: AnyObject]], targetTimestamp: TimeInterval) -> DeviceStatusData? {
        var closestEntry: DeviceStatusData?
        var smallestDiff: TimeInterval = .infinity

        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withFullDate, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime]

        for entry in jsonArray {
            // Parse timestamp
            var entryTimestamp: TimeInterval = 0

            if let createdAt = entry["created_at"] as? String,
               let date = formatter.date(from: createdAt)
            {
                entryTimestamp = date.timeIntervalSince1970
            } else if let dateString = entry["dateString"] as? String {
                // Try parsing dateString as fallback
                if let date = NightscoutUtils.parseDate(dateString) {
                    entryTimestamp = date.timeIntervalSince1970
                }
            }

            guard entryTimestamp > 0 else { continue }

            let diff = abs(entryTimestamp - targetTimestamp)
            if diff < smallestDiff {
                smallestDiff = diff

                // Extract IOB, COB, and prediction data
                var iob: Double?
                var cob: Double?
                var eventualBG: Int?
                var minPredictedBG: Int?
                var maxPredictedBG: Int?
                var overrideName: String?
                var overrideMultiplier: Double?
                var overrideTargetRange: (min: Int, max: Int)?
                var recommendedBolus: Double?

                // Try Loop format first
                if let loopRecord = entry["loop"] as? [String: AnyObject] {
                    // IOB
                    if let iobDict = loopRecord["iob"] as? [String: AnyObject],
                       let iobValue = iobDict["iob"] as? Double
                    {
                        iob = iobValue
                    }

                    // COB
                    if let cobDict = loopRecord["cob"] as? [String: AnyObject],
                       let cobValue = cobDict["cob"] as? Double
                    {
                        cob = cobValue
                    }

                    // Predictions
                    if let predicted = loopRecord["predicted"] as? [String: AnyObject] {
                        if let values = predicted["values"] as? [Int], !values.isEmpty {
                            eventualBG = values.last
                            minPredictedBG = values.min()
                            maxPredictedBG = values.max()
                        }
                    }

                    // Recommended Bolus
                    if let recBolus = loopRecord["recommendedBolus"] as? Double {
                        recommendedBolus = recBolus
                    }
                }

                // Try OpenAPS format
                if let openapsRecord = entry["openaps"] as? [String: AnyObject] {
                    if let suggested = openapsRecord["suggested"] as? [String: AnyObject] {
                        if let iobValue = suggested["IOB"] as? Double {
                            iob = iobValue
                        }
                        if let cobValue = suggested["COB"] as? Double {
                            cob = cobValue
                        }
                        if let eventualValue = suggested["eventualBG"] as? Int {
                            eventualBG = eventualValue
                        }
                    }

                    if let enacted = openapsRecord["enacted"] as? [String: AnyObject] {
                        if iob == nil, let iobValue = enacted["IOB"] as? Double {
                            iob = iobValue
                        }
                        if cob == nil, let cobValue = enacted["COB"] as? Double {
                            cob = cobValue
                        }
                    }
                }

                // Parse override data
                if let overrideRecord = entry["override"] as? [String: AnyObject],
                   let isActive = overrideRecord["active"] as? Bool,
                   isActive
                {
                    overrideName = overrideRecord["name"] as? String
                    overrideMultiplier = overrideRecord["multiplier"] as? Double

                    if let currentRange = overrideRecord["currentCorrectionRange"] as? [String: AnyObject],
                       let minValue = currentRange["minValue"] as? Double,
                       let maxValue = currentRange["maxValue"] as? Double
                    {
                        overrideTargetRange = (min: Int(minValue), max: Int(maxValue))
                    }
                }

                closestEntry = DeviceStatusData(
                    timestamp: entryTimestamp,
                    iob: iob,
                    cob: cob,
                    eventualBG: eventualBG,
                    minPredictedBG: minPredictedBG,
                    maxPredictedBG: maxPredictedBG,
                    overrideName: overrideName,
                    overrideMultiplier: overrideMultiplier,
                    overrideTargetRange: overrideTargetRange,
                    recommendedBolus: recommendedBolus
                )
            }
        }

        // Only return if we found something within 15 minutes
        if smallestDiff < 15 * 60 {
            return closestEntry
        }

        return nil
    }

    private func findNearestBG(at timestamp: TimeInterval, in bgData: [ShareGlucoseData]) -> Int {
        guard !bgData.isEmpty else { return 0 }

        var closestBG: ShareGlucoseData?
        var smallestDiff: TimeInterval = .infinity

        for bg in bgData {
            let diff = abs(bg.date - timestamp)
            if diff < smallestDiff {
                smallestDiff = diff
                closestBG = bg
            }

            if diff > smallestDiff && smallestDiff < 300 {
                break
            }
        }

        if let bg = closestBG, smallestDiff < 600 {
            return Int(bg.sgv)
        }

        return 0
    }

    private func getMainViewController() -> MainViewController? {
        MainViewController.shared
    }
}

struct TreatmentRow: View {
    let treatment: Treatment

    var body: some View {
        NavigationLink(destination: TreatmentDetailView(treatment: treatment)) {
            HStack {
                Image(systemName: treatment.icon)
                    .foregroundColor(treatment.color)
                    .opacity(treatment.type == .tempBasal ? 0.5 : 1.0)
                    .frame(width: 24)

                VStack(alignment: .leading, spacing: 4) {
                    HStack {
                        Text(treatment.title)
                            .font(.headline)
                        if let subtitle = treatment.subtitle {
                            Text(subtitle)
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                        }
                    }
                    Text(formatTime(treatment.date))
                        .font(.caption)
                        .foregroundColor(.secondary)
                }

                Spacer()
            }
            .padding(.vertical, 4)
        }
    }

    private func formatTime(_ timeInterval: TimeInterval) -> String {
        let date = Date(timeIntervalSince1970: timeInterval)
        let formatter = DateFormatter()
        formatter.timeStyle = .short
        dateTimeUtils.applyDisplayTimeZone(to: formatter)
        return formatter.string(from: date)
    }
}

enum TreatmentType: CaseIterable, Hashable {
    case carb
    case bolusManual
    case bolusAutomatic
    case smb
    case tempBasal
    case override
    case tempTarget

    var displayName: String {
        switch self {
        case .carb: return "Carbs"
        case .bolusManual: return "Bolus"
        case .bolusAutomatic: return "Automatic"
        case .smb: return "SMB"
        case .tempBasal: return "Basal"
        case .override: return "Override"
        case .tempTarget: return "Temp Target"
        }
    }
}

enum TreatmentFilter: Hashable {
    case all
    case carb
    case basal
    case bolusAll
    case bolusAutomatic
    case bolusSMB
    case bolusManual

    func displayName(automaticLabel: String) -> String {
        switch self {
        case .all: return "All"
        case .carb: return "Carbs"
        case .basal: return "Basal"
        case .bolusAll: return "All Boluses"
        case .bolusAutomatic: return automaticLabel
        case .bolusSMB: return "SMB"
        case .bolusManual: return "Manual"
        }
    }

    static var primaryFilters: [TreatmentFilter] {
        [.all, .carb, .basal]
    }

    static func bolusFilters(isLoopDevice: Bool, isTrioDevice: Bool, automaticLabelIsSMB: Bool) -> [TreatmentFilter] {
        var filters: [TreatmentFilter] = [.bolusAll]

        if isTrioDevice {
            filters.append(.bolusAutomatic)
        } else if isLoopDevice {
            filters.append(.bolusAutomatic)
        } else {
            if automaticLabelIsSMB {
                filters.append(.bolusAutomatic)
            } else {
                filters.append(contentsOf: [.bolusAutomatic, .bolusSMB])
            }
        }

        filters.append(.bolusManual)
        return filters
    }
}

struct Treatment: Identifiable {
    let id: String
    let type: TreatmentType
    let date: TimeInterval
    let title: String
    let subtitle: String?
    let icon: String
    let color: Color
    let bgValue: Int

    init(id: String? = nil, type: TreatmentType, date: TimeInterval, title: String, subtitle: String?, icon: String, color: Color, bgValue: Int) {
        self.id = id ?? "\(type)-\(date)-\(title)"
        self.type = type
        self.date = date
        self.title = title
        self.subtitle = subtitle
        self.icon = icon
        self.color = color
        self.bgValue = bgValue
    }

    var hourKey: String {
        let date = Date(timeIntervalSince1970: self.date)
        let calendar = dateTimeUtils.displayCalendar()
        let components = calendar.dateComponents([.year, .month, .day, .hour], from: date)
        return "\(components.year!)-\(components.month!)-\(components.day!)-\(components.hour!)"
    }
}

class TreatmentsViewModel: ObservableObject {
    @Published var groupedTreatments: [String: [Treatment]] = [:]
    @Published var isInitialLoading = false
    @Published var isLoadingMore = false
    @Published var hasMoreData = true
    @Published var hasSMBEntries = false
    @Published var hasAutomaticEntries = false

    private var allTreatments: [Treatment] = []
    private var processedNightscoutIds = Set<String>() // Track which NS entries we've already processed
    private var oldestFetchedDate: Date? // Track the oldest treatment date we've fetched
    private let pageSize = 100
    private var isFetching = false

    func loadInitialTreatments() {
        guard !isInitialLoading, !isFetching else {
            return
        }

        isInitialLoading = true
        isFetching = true
        allTreatments.removeAll()
        processedNightscoutIds.removeAll()
        oldestFetchedDate = nil
        hasMoreData = true
        hasSMBEntries = false
        hasAutomaticEntries = false

        // Start from now and go backwards
        fetchTreatments(endDate: Date()) { [weak self] treatments, rawCount in
            guard let self = self else { return }

            DispatchQueue.main.async {
                self.allTreatments = treatments
                self.regroupTreatments()

                // Update oldest date from the last (oldest) treatment
                if let oldest = treatments.last {
                    self.oldestFetchedDate = Date(timeIntervalSince1970: oldest.date)
                }

                // Has more data if we got a full page from Nightscout
                self.hasMoreData = rawCount >= self.pageSize
                self.isInitialLoading = false
                self.isFetching = false
            }
        }
    }

    func refreshTreatments() {
        allTreatments.removeAll()
        processedNightscoutIds.removeAll()
        oldestFetchedDate = nil
        hasMoreData = true
        isFetching = false
        loadInitialTreatments()
    }

    func loadMoreIfNeeded() {
        guard !isLoadingMore, !isFetching, hasMoreData, let oldestDate = oldestFetchedDate else {
            return
        }

        isLoadingMore = true
        isFetching = true

        // Fetch treatments older than the oldest we have
        fetchTreatments(endDate: oldestDate) { [weak self] treatments, rawCount in
            guard let self = self else { return }

            DispatchQueue.main.async {
                self.allTreatments.append(contentsOf: treatments)
                self.regroupTreatments()

                // Update oldest date from the last (oldest) treatment in the new batch
                if let oldest = treatments.last {
                    self.oldestFetchedDate = Date(timeIntervalSince1970: oldest.date)
                }

                // Has more data if we got a full page from Nightscout
                self.hasMoreData = rawCount >= self.pageSize
                self.isLoadingMore = false
                self.isFetching = false
            }
        }
    }

    private func fetchTreatments(endDate: Date, completion: @escaping ([Treatment], Int) -> Void) {
        guard IsNightscoutEnabled() else {
            completion([], 0)
            return
        }

        let baseURL = Storage.shared.url.value
        let token = Storage.shared.token.value

        guard !baseURL.isEmpty else {
            completion([], 0)
            return
        }

        // Format dates for the query
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        formatter.timeZone = TimeZone(abbreviation: "UTC")

        // For pagination: fetch treatments with created_at < endDate
        // Go back up to 365 days from endDate to ensure we get enough data
        let startDate = Calendar.current.date(byAdding: .day, value: -365, to: endDate)!
        let endDateString = formatter.string(from: endDate)
        let startDateString = formatter.string(from: startDate)

        // Build parameters with date filtering
        let parameters: [String: String] = [
            "find[created_at][$gte]": startDateString,
            "find[created_at][$lt]": endDateString,
            "count": "\(pageSize)",
        ]

        // Construct URL
        guard let url = NightscoutUtils.constructURL(
            baseURL: baseURL,
            token: token,
            endpoint: "/api/v1/treatments.json",
            parameters: parameters
        ) else {
            completion([], 0)
            return
        }

        var request = URLRequest(url: url)
        request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
        request.httpMethod = "GET"

        let task = URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
            guard let self = self else { return }

            if let error = error {
                LogManager.shared.log(category: .nightscout, message: "Failed to fetch treatments: \(error.localizedDescription)")
                completion([], 0)
                return
            }

            guard let data = data else {
                completion([], 0)
                return
            }

            do {
                guard let entries = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: AnyObject]] else {
                    completion([], 0)
                    return
                }

                // Parse treatments
                let rawCount = entries.count
                let result = self.parseTreatments(from: entries)

                DispatchQueue.main.async {
                    if result.detectedSMB {
                        self.hasSMBEntries = true
                    }
                    if result.detectedAutomatic {
                        self.hasAutomaticEntries = true
                    }
                }

                completion(result.treatments, rawCount)

            } catch {
                LogManager.shared.log(category: .nightscout, message: "Failed to parse treatments: \(error.localizedDescription)")
                completion([], 0)
            }
        }

        task.resume()
    }

    private func parseTreatments(from entries: [[String: AnyObject]]) -> (treatments: [Treatment], detectedSMB: Bool, detectedAutomatic: Bool) {
        var treatments: [Treatment] = []
        var detectedSMB = false
        var detectedAutomatic = false
        guard let mainVC = getMainViewController() else { return ([], false, false) }

        for entry in entries {
            guard let eventType = entry["eventType"] as? String,
                  let createdAt = entry["created_at"] as? String,
                  let date = NightscoutUtils.parseDate(createdAt)
            else {
                continue
            }

            let timestamp = date.timeIntervalSince1970
            let nsId = entry["_id"] as? String ?? "unknown-\(timestamp)"

            // Skip if we've already processed this Nightscout entry
            if processedNightscoutIds.contains(nsId) {
                continue
            }

            // Mark this entry as processed
            processedNightscoutIds.insert(nsId)

            switch eventType {
            case "Carb Correction", "Meal Bolus":
                if let carbs = entry["carbs"] as? Double, carbs > 0 {
                    let actualBG = findNearestBG(at: timestamp, in: mainVC.bgData)
                    let treatment = Treatment(
                        id: "\(nsId)-carb",
                        type: .carb,
                        date: timestamp,
                        title: "\(Int(carbs))g",
                        subtitle: "Carbs",
                        icon: "circle.fill",
                        color: .orange,
                        bgValue: actualBG
                    )
                    treatments.append(treatment)
                }

                // Also process bolus from Meal Bolus
                if eventType == "Meal Bolus",
                   let insulin = entry["insulin"] as? Double, insulin > 0
                {
                    let actualBG = findNearestBG(at: timestamp, in: mainVC.bgData)
                    let treatment = Treatment(
                        id: "\(nsId)-bolus",
                        type: .bolusManual,
                        date: timestamp,
                        title: String(format: "%.2f U", insulin),
                        subtitle: "Bolus",
                        icon: "circle.fill",
                        color: .blue,
                        bgValue: actualBG
                    )
                    treatments.append(treatment)
                }

            case "Correction Bolus", "Bolus", "External Insulin":
                if let insulin = entry["insulin"] as? Double, insulin > 0 {
                    let isAutomatic = entry["automatic"] as? Bool ?? false
                    let isSMB = entry["isSMB"] as? Bool ?? false
                    let typeString = (entry["type"] as? String)?.lowercased()
                    let isTrioSMBType = typeString == "smb"
                    let treatAsAutomatic = isAutomatic || isSMB || isTrioSMBType
                    let actualBG = findNearestBG(at: timestamp, in: mainVC.bgData)

                    if treatAsAutomatic {
                        if isSMB || isTrioSMBType {
                            detectedSMB = true
                        }
                        if isAutomatic {
                            detectedAutomatic = true
                        }
                        let treatment = Treatment(
                            id: "\(nsId)-smb",
                            type: .bolusAutomatic,
                            date: timestamp,
                            title: String(format: "%.2f U", insulin),
                            subtitle: isSMB || isTrioSMBType ? "SMB" : "Automatic Bolus",
                            icon: "arrowtriangle.down.fill",
                            color: .blue,
                            bgValue: actualBG
                        )
                        treatments.append(treatment)
                    } else {
                        let treatment = Treatment(
                            id: "\(nsId)-bolus",
                            type: .bolusManual,
                            date: timestamp,
                            title: String(format: "%.2f U", insulin),
                            subtitle: "Bolus",
                            icon: "circle.fill",
                            color: .blue,
                            bgValue: actualBG
                        )
                        treatments.append(treatment)
                    }
                }

            case "SMB":
                if let insulin = entry["insulin"] as? Double, insulin > 0 {
                    detectedSMB = true
                    let actualBG = findNearestBG(at: timestamp, in: mainVC.bgData)
                    let treatment = Treatment(
                        id: "\(nsId)-smb",
                        type: .smb,
                        date: timestamp,
                        title: String(format: "%.2f U", insulin),
                        subtitle: "SMB",
                        icon: "arrowtriangle.down.fill",
                        color: .blue,
                        bgValue: actualBG
                    )
                    treatments.append(treatment)
                }

            case "Temp Basal":
                if let rate = entry["rate"] as? Double {
                    let treatment = Treatment(
                        id: "\(nsId)-basal",
                        type: .tempBasal,
                        date: timestamp,
                        title: String(format: "%.2f U/hr", rate),
                        subtitle: "Temp Basal",
                        icon: "chart.xyaxis.line",
                        color: .blue,
                        bgValue: 0
                    )
                    treatments.append(treatment)
                }

            case "Temporary Override", "Exercise":
                let durationMinutes = (entry["duration"] as? Double) ?? Double(entry["duration"] as? Int ?? 0)
                if durationMinutes == 0 {
                    continue
                }

                let reason = (entry["notes"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
                    ?? (entry["reason"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
                    ?? ""

                let subtitleParts = ["Override", durationMinutes > 0 ? "\(Int(durationMinutes))m" : nil].compactMap { $0 }
                let subtitle = subtitleParts.joined(separator: " • ")
                let title = reason.isEmpty ? "Temporary Override" : reason

                let treatment = Treatment(
                    id: "\(nsId)-override",
                    type: .override,
                    date: timestamp,
                    title: title,
                    subtitle: subtitle,
                    icon: "slider.horizontal.3",
                    color: .purple,
                    bgValue: 0
                )
                treatments.append(treatment)

            case "Temporary Target":
                let durationMinutes = (entry["duration"] as? Double) ?? Double(entry["duration"] as? Int ?? 0)
                if durationMinutes == 0 {
                    continue
                }

                let targetBottom = (entry["targetBottom"] as? Double) ?? Double(entry["targetBottom"] as? Int ?? 0)
                let targetTop = (entry["targetTop"] as? Double) ?? Double(entry["targetTop"] as? Int ?? 0)
                let targetValue = targetBottom > 0 ? targetBottom : targetTop

                guard targetValue > 0 else {
                    continue
                }

                let title: String
                if targetBottom > 0, targetTop > 0, abs(targetTop - targetBottom) >= 0.1 {
                    title = "\(formatBG(targetBottom)) - \(formatBG(targetTop))"
                } else {
                    title = formatBG(targetValue)
                }

                let reason = (entry["reason"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
                let subtitleParts = ["Temp Target", durationMinutes > 0 ? "\(Int(durationMinutes))m" : nil, reason?.isEmpty == false ? reason : nil].compactMap { $0 }
                let subtitle = subtitleParts.joined(separator: " • ")

                let treatment = Treatment(
                    id: "\(nsId)-temp-target",
                    type: .tempTarget,
                    date: timestamp,
                    title: title,
                    subtitle: subtitle,
                    icon: "scope",
                    color: .green,
                    bgValue: 0
                )
                treatments.append(treatment)

            default:
                break
            }
        }

        // Sort by date descending (most recent first)
        return (treatments.sorted { $0.date > $1.date }, detectedSMB, detectedAutomatic)
    }

    private func regroupTreatments() {
        var grouped: [String: [Treatment]] = [:]

        for treatment in allTreatments {
            let key = treatment.hourKey
            if grouped[key] == nil {
                grouped[key] = []
            }
            grouped[key]?.append(treatment)
        }

        // Sort treatments within each hour
        for key in grouped.keys {
            grouped[key]?.sort { $0.date > $1.date }
        }

        groupedTreatments = grouped
    }

    private func findNearestBG(at timestamp: TimeInterval, in bgData: [ShareGlucoseData]) -> Int {
        // Find the closest BG reading to the treatment time
        guard !bgData.isEmpty else { return 0 }

        var closestBG: ShareGlucoseData?
        var smallestDiff: TimeInterval = .infinity

        for bg in bgData {
            let diff = abs(bg.date - timestamp)
            if diff < smallestDiff {
                smallestDiff = diff
                closestBG = bg
            }

            // If we're getting further away, we can stop (data is sorted)
            if diff > smallestDiff && smallestDiff < 300 { // Within 5 minutes
                break
            }
        }

        // Only return BG if it's within 10 minutes of the treatment
        if let bg = closestBG, smallestDiff < 600 {
            return Int(bg.sgv)
        }

        return 0
    }

    private func getMainViewController() -> MainViewController? {
        MainViewController.shared
    }
}

struct TreatmentsView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            TreatmentsView()
        }
    }
}
