import SwiftUI
import SwiftData
import UIKit

struct SnapshotsView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(AppSettings.self) private var appSettings
    @Query(sort: \HealthSnapshot.timestamp, order: .reverse) private var allSnapshots: [HealthSnapshot]
    @Query private var allDeltas: [SnapshotDelta]
    @Query private var deviceProfiles: [DeviceProfile]
    @State private var viewModel = SnapshotsViewModel()

    private var profileMap: [String: DeviceProfile] {
        Dictionary(uniqueKeysWithValues: deviceProfiles.compactMap {
            $0.deviceID.isEmpty ? nil : ($0.deviceID, $0)
        })
    }

    private var displayedSnapshots: [HealthSnapshot] {
        let selected = appSettings.selectedDeviceIDs
        guard !selected.isEmpty else { return [] }
        return allSnapshots.filter { selected.contains($0.deviceID) }
    }

    private var snapshotItems: [SnapshotListItem] {
        let baselines = viewModel.baselines(for: displayedSnapshots)
        let deltaSummaryBySnapshotID = allDeltas.reduce(into: [UUID: SnapshotDeltaListSummary]()) { partial, delta in
            partial[delta.toSnapshotID] = delta.listSummary
        }

        return displayedSnapshots.map { snapshot in
            SnapshotListItem(
                snapshot: snapshot,
                baseline: baselines[snapshot.id] ?? nil,
                deltaSummary: deltaSummaryBySnapshotID[snapshot.id],
                showsDeltaSummary: viewModel.comparisonMode == .previous
            )
        }
    }

    private var knownDevices: [DeviceEntry] {
        let currentID = AppSettings.currentDeviceID
        var ids = Set(allSnapshots.map { $0.deviceID })
        ids.insert(currentID)
        return ids.map { id in
            let profile = profileMap[id]
            let name: String
            if let n = profile?.name, !n.isEmpty { name = n }
            else if id.isEmpty { name = "Unidentified" }
            else { name = "Unknown Device" }
            let color = DeviceColor(rawValue: profile?.colorTag ?? "")?.color ?? .neutralGray
            return DeviceEntry(id: id, displayName: name, color: color, isCurrent: id == currentID)
        }
        .sorted {
            if $0.isCurrent != $1.isCurrent { return $0.isCurrent }
            return $0.displayName < $1.displayName
        }
    }

    var body: some View {
        NavigationStack {
            Group {
                if displayedSnapshots.isEmpty {
                    EmptyStateView(
                        icon: "clock.arrow.circlepath",
                        title: appSettings.selectedDeviceIDs.isEmpty ? "No Devices Selected" : "No Snapshots",
                        message: appSettings.selectedDeviceIDs.isEmpty
                            ? "Select at least one device using the filter above."
                            : "Use the Dashboard to create your first snapshot."
                    )
                } else {
                    snapshotList
                }
            }
            .navigationTitle("Snapshots")
            .toolbar { toolbarContent }
            .task(id: allDeltas.count) {
                repairDeltaListSummariesIfNeeded()
            }
            .onChange(of: appSettings.selectedDeviceIDs) {
                if let baseline = viewModel.selectedBaseline,
                   !displayedSnapshots.contains(where: { $0.id == baseline.id }) {
                    viewModel.selectedBaseline = nil
                    viewModel.comparisonMode = .previous
                }
            }
        }
    }

    // MARK: - List

    private var snapshotList: some View {
        List(snapshotItems) { item in
            NavigationLink {
                SnapshotDetailView(
                    snapshot: item.snapshot,
                    baseline: item.baseline,
                    profile: profileMap[item.snapshot.deviceID]
                )
            } label: {
                SnapshotRow(
                    snapshot: item.snapshot,
                    baseline: item.baseline,
                    deltaSummary: item.deltaSummary,
                    showsDeltaSummary: item.showsDeltaSummary,
                    isSelectedBaseline: viewModel.selectedBaseline?.id == item.snapshot.id,
                    profile: profileMap[item.snapshot.deviceID]
                )
            }
            .swipeActions(edge: .leading) {
                Button {
                    viewModel.toggleBaseline(item.snapshot)
                    viewModel.comparisonMode = .selected
                } label: {
                    Label(
                        viewModel.selectedBaseline?.id == item.snapshot.id ? "Unset Baseline" : "Set as Baseline",
                        systemImage: viewModel.selectedBaseline?.id == item.snapshot.id ? "pin.slash" : "pin"
                    )
                }
                .tint(.indigo)
            }
            .swipeActions(edge: .trailing) {
                Button(role: .destructive) {
                    do {
                        try SnapshotLifecycleService.delete(item.snapshot, context: modelContext)
                    } catch {
                        // Failure is surfaced via the navigation stack; no silent discard
                    }
                } label: {
                    Label("Delete", systemImage: "trash")
                }
            }
        }
    }

    // MARK: - Toolbar

    @ToolbarContentBuilder
    private var toolbarContent: some ToolbarContent {
        ToolbarItem(placement: .topBarLeading) {
            devicePickerMenu
        }
        ToolbarItem(placement: .navigationBarTrailing) {
            Menu {
                Picker("Compare Against", selection: $viewModel.comparisonMode) {
                    Text("Previous").tag(ComparisonMode.previous)
                    ForEach(ComparisonMode.relativeOptions, id: \.interval) { opt in
                        Text(opt.label).tag(ComparisonMode.relativeTime(opt.interval))
                    }
                    if viewModel.selectedBaseline != nil {
                        Text("Selected Baseline").tag(ComparisonMode.selected)
                    }
                }
            } label: {
                Label(viewModel.comparisonMode.label, systemImage: "arrow.left.arrow.right")
                    .labelStyle(.titleAndIcon)
            }
        }
    }

    private var devicePickerMenu: some View {
        let selected = appSettings.selectedDeviceIDs
        let isMulti  = selected.count > 1
        return Menu {
            ForEach(knownDevices) { entry in
                Button {
                    appSettings.toggleDevice(entry.id)
                } label: {
                    Label {
                        Text(entry.isCurrent
                             ? "\(entry.displayName) (This Device)"
                             : entry.displayName)
                    } icon: {
                        Image(systemName: selected.contains(entry.id)
                              ? "checkmark.circle.fill" : "circle.fill")
                            .foregroundStyle(entry.color)
                    }
                }
            }
        } label: {
            Image(systemName: "iphone")
        }
        .tint(isMulti ? .orange : .accentColor)
        .accessibilityLabel("Select devices – \(selected.count) selected")
    }

    private func repairDeltaListSummariesIfNeeded() {
        do {
            _ = try DeltaService.rebuildMissingListSummaries(context: modelContext, maxCount: 64)
        } catch {
            // Keep the list responsive even if summary repair fails.
        }
    }
}

private struct SnapshotListItem: Identifiable {
    let snapshot: HealthSnapshot
    let baseline: HealthSnapshot?
    let deltaSummary: SnapshotDeltaListSummary?
    let showsDeltaSummary: Bool

    var id: UUID { snapshot.id }
}

// MARK: - Row

private struct SnapshotRow: View {
    let snapshot: HealthSnapshot
    let baseline: HealthSnapshot?
    let deltaSummary: SnapshotDeltaListSummary?
    let showsDeltaSummary: Bool
    let isSelectedBaseline: Bool
    let profile: DeviceProfile?

    private static let dateFormatter: DateFormatter = {
        let f = DateFormatter()
        f.dateStyle = .medium
        f.timeStyle = .short
        return f
    }()

    private var deviceDisplayName: String {
        if let name = profile?.name, !name.isEmpty { return name }
        return snapshot.deviceName.isEmpty ? "Unknown device" : snapshot.deviceName
    }

    private var deviceColor: Color {
        DeviceColor(rawValue: profile?.colorTag ?? "")?.color ?? .neutralGray
    }

    private var metricCountLabel: String? {
        guard snapshot.hasCurrentCachedSummary else { return nil }
        return snapshot.cachedTypeCount == 1 ? "1 metric" : "\(snapshot.cachedTypeCount) metrics"
    }

    private var deltaSummaryText: String? {
        guard let deltaSummary else { return nil }

        var parts: [String] = []

        if deltaSummary.absoluteRecordChangeCount > 0 {
            parts.append("\(deltaSummary.absoluteRecordChangeCount) record change\(deltaSummary.absoluteRecordChangeCount == 1 ? "" : "s")")
        }

        if deltaSummary.changedMetricCount > 0 {
            parts.append("\(deltaSummary.changedMetricCount) metric change\(deltaSummary.changedMetricCount == 1 ? "" : "s")")
        }

        if deltaSummary.appearedMetricCount > 0 {
            parts.append("\(deltaSummary.appearedMetricCount) metric appeared")
        }

        if deltaSummary.disappearedMetricCount > 0 {
            parts.append("\(deltaSummary.disappearedMetricCount) metric disappeared")
        }

        if parts.isEmpty {
            return "No changes"
        }

        return parts.joined(separator: " • ")
    }

    private var deltaSummaryColor: Color {
        guard let deltaSummary else { return .secondary }
        if deltaSummary.disappearedMetricCount > 0 { return Color.criticalRed }
        if deltaSummary.hasChanges { return Color.warningAmber }
        return Color.healthyGreen
    }

    private var hasOSVersionChange: Bool {
        guard let baseline else { return false }
        let currentVersion = snapshot.osVersion.trimmingCharacters(in: .whitespacesAndNewlines)
        let baselineVersion = baseline.osVersion.trimmingCharacters(in: .whitespacesAndNewlines)
        return !currentVersion.isEmpty && !baselineVersion.isEmpty && currentVersion != baselineVersion
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            HStack {
                Text(Self.dateFormatter.string(from: snapshot.timestamp))
                    .font(.subheadline.weight(.semibold))
                Spacer()
                if isSelectedBaseline {
                    Image(systemName: "pin.fill")
                        .foregroundStyle(.indigo)
                        .font(.caption)
                        .accessibilityLabel("Selected as comparison baseline")
                }
                if !snapshot.anomalyFlags.isEmpty {
                    Image(systemName: "exclamationmark.triangle.fill")
                        .foregroundStyle(Color.warningAmber)
                        .font(.caption)
                        .accessibilityLabel("Has anomalies")
                }
            }

            HStack(spacing: 6) {
                Circle()
                    .fill(deviceColor)
                    .frame(width: 8, height: 8)
                Text(deviceDisplayName)
                    .font(.caption)
                    .foregroundStyle(.secondary)
                if let metricCountLabel {
                    Label(metricCountLabel, systemImage: "list.bullet.rectangle")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                if hasOSVersionChange {
                    Label("OS \(snapshot.osVersion)", systemImage: "gearshape.fill")
                        .font(.caption)
                        .foregroundStyle(Color.warningAmber)
                        .accessibilityLabel("OS version changed to \(snapshot.osVersion)")
                }
            }

            // Chain indicators
            chainIndicators

            if snapshot.snapshotQuality != SnapshotQuality.complete {
                Label("Incomplete snapshot", systemImage: "exclamationmark.triangle")
                    .font(.caption)
                    .foregroundStyle(Color.warningAmber)
            }

            if showsDeltaSummary,
               let deltaSummaryText {
                HStack(spacing: 4) {
                    Image(systemName: deltaSummary == nil || deltaSummary?.hasChanges == false
                          ? "checkmark.circle"
                          : "arrow.triangle.2.circlepath")
                    Text(deltaSummaryText)
                }
                .font(.caption)
                .foregroundStyle(deltaSummaryColor)
            }
        }
        .padding(.vertical, 2)
        .accessibilityElement(children: .combine)
    }

    @ViewBuilder
    private var chainIndicators: some View {
        if snapshot.isChainStart && snapshot.recoveredDeviceID {
            Label("DB reset / recovered device ID", systemImage: "arrow.clockwise.icloud")
                .font(.caption)
                .foregroundStyle(.secondary)
        } else if snapshot.isChainStart {
            Label("Chain start", systemImage: "link.badge.plus")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        if snapshot.isPostRestore && !snapshot.isPostRestoreInferred {
            Label("Post-restore baseline", systemImage: "clock.arrow.circlepath")
                .font(.caption)
                .foregroundStyle(.secondary)
        } else if snapshot.isPostRestore && snapshot.isPostRestoreInferred {
            Label("Post-restore baseline (inferred)", systemImage: "clock.arrow.circlepath")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        if snapshot.triggerReason == "observerCallback" {
            Label("Observer-triggered snapshot", systemImage: "waveform")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
    }
}

#Preview {
    SnapshotsView()
    .modelContainer(for: [HealthSnapshot.self, SnapshotDelta.self, TypeDelta.self, TypeCount.self, HealthRecord.self, TypeDistributionBin.self, DeviceProfile.self], inMemory: true)
        .environment(AppSettings())
}
