import SwiftUI
import SwiftData
import UIKit

struct SnapshotDetailView: View {
    let snapshot: HealthSnapshot
    let baseline: HealthSnapshot?
    let profile: LocalDeviceProfile?

    @Query(sort: \HealthSnapshot.timestamp) private var allSnapshots: [HealthSnapshot]
    @State private var displayedSnapshot: HealthSnapshot?
    @State private var archiveTypeRows: [SnapshotArchiveTypeRow]?
    @State private var archiveTypeError: String?

    private var currentSnapshot: HealthSnapshot {
        displayedSnapshot ?? snapshot
    }

    private var archiveReloadID: String {
        [
            currentSnapshot.id.uuidString,
            String(currentSnapshot.archiveObservationID ?? -1),
            String(baseline?.archiveObservationID ?? -1)
        ].joined(separator: "|")
    }

    private var summaryTypeCount: Int? {
        if let archiveTypeRows {
            return archiveTypeRows.count
        }
        guard currentSnapshot.hasCurrentCachedSummary else { return nil }
        return currentSnapshot.cachedTypeCount
    }

    private var summaryRecordCount: Int? {
        if let archiveTypeRows {
            return archiveTypeRows.reduce(0) { $0 + $1.currentCount }
        }
        guard currentSnapshot.hasCurrentCachedSummary else { return nil }
        return currentSnapshot.cachedRecordCount
    }

    private var summaryEarliestRecordDate: Date? {
        archiveTypeRows?.compactMap(\.earliestStartDate).min() ?? currentSnapshot.cachedEarliestRecordDate
    }

    private var summaryLatestRecordDate: Date? {
        archiveTypeRows?.compactMap(\.latestEndDate).max() ?? currentSnapshot.cachedLatestRecordDate
    }

    private var archiveRecordChangeCount: Int? {
        archiveTypeRows?.reduce(0) { $0 + $1.recordChangeCount }
    }

    private var archiveAffectedMetricCount: Int? {
        archiveTypeRows?.filter(\.hasChanges).count
    }

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

    private var timelineSnapshots: [HealthSnapshot] {
        allSnapshots.filter { candidate in
            if currentSnapshot.deviceID.isEmpty {
                return candidate.deviceID.isEmpty
            }
            return candidate.deviceID == currentSnapshot.deviceID
        }
    }

    @State private var showShareSheet = false
    @State private var pdfExportURL: URL?
    @State private var isExporting = false
    @State private var showMetadataSheet = false

    var body: some View {
        List {
            evolutionSection
        }
        .navigationTitle("Snapshot")
        .navigationBarTitleDisplayMode(.inline)
        .safeAreaInset(edge: .top, spacing: 0) {
            SnapshotNavigationHeader(
                snapshots: timelineSnapshots,
                currentSnapshot: currentSnapshot,
                onSnapshotSelected: { displayedSnapshot = $0 }
            )
                .frame(height: 64)
        }
        .toolbar {
            ToolbarItem(placement: .principal) {
                snapshotToolbarTitle
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                HStack(spacing: 12) {
                    Button {
                        showMetadataSheet = true
                    } label: {
                        Image(systemName: "info.circle")
                    }
                    .accessibilityLabel("View snapshot details")

                    if isExporting {
                        ProgressView()
                            .accessibilityLabel("Generating PDF")
                    } else {
                        Button {
                            exportAsPDF()
                        } label: {
                            Image(systemName: "square.and.arrow.up")
                        }
                        .accessibilityLabel("Export snapshot as PDF")
                    }
                }
            }
        }
        .sheet(isPresented: $showMetadataSheet) {
            metadataSheetContent
        }
        .sheet(isPresented: $showShareSheet) {
            if let url = pdfExportURL {
                ShareSheet(items: [url])
                    .ignoresSafeArea()
            }
        }
        .task(id: archiveReloadID) {
            await loadArchiveTypeRows()
        }
    }

    private func exportAsPDF() {
        isExporting = true
        let reportData = SnapshotPDFExporter.extractReportData(
            snapshot: currentSnapshot,
            baseline: baseline,
            profile: profile
        )
        let timestamp = currentSnapshot.timestamp
        Task(priority: .userInitiated) {
            let pdfData = SnapshotPDFExporter.generatePDF(from: reportData)
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd-HH-mm"
            let name = "HealthProbe-Snapshot-\(formatter.string(from: timestamp)).pdf"
            let url = FileManager.default.temporaryDirectory.appendingPathComponent(name)
            try? pdfData.write(to: url)
            isExporting = false
            pdfExportURL = url
            showShareSheet = true
        }
    }

    @MainActor
    private func loadArchiveTypeRows() async {
        guard let currentObservationID = currentSnapshot.archiveObservationID else {
            archiveTypeRows = nil
            archiveTypeError = nil
            return
        }

        do {
            let cache = try CoreDataArchiveCacheStore()
            let currentSummaries = try cache.typeSummaries(observationID: currentObservationID)
            let baselineObservationID = baseline?.archiveObservationID
            let baselineSummaries = try baselineObservationID.map {
                try cache.typeSummaries(observationID: $0)
            } ?? []

            let currentByType = Dictionary(uniqueKeysWithValues: currentSummaries.map { ($0.sampleTypeIdentifier, $0) })
            let baselineByType = Dictionary(uniqueKeysWithValues: baselineSummaries.map { ($0.sampleTypeIdentifier, $0) })
            let allTypeIdentifiers = Set(currentByType.keys).union(baselineByType.keys)

            var rows: [SnapshotArchiveTypeRow] = []
            rows.reserveCapacity(allTypeIdentifiers.count)

            for typeIdentifier in allTypeIdentifiers {
                let current = currentByType[typeIdentifier]
                let baselineSummary = baselineByType[typeIdentifier]
                let diff: HealthArchiveDiffSummary
                if let baselineObservationID {
                    diff = try await SQLiteHealthArchiveStore.shared.diffSummary(HealthArchiveDiffRequest(
                        fromObservationID: baselineObservationID,
                        toObservationID: currentObservationID,
                        sampleTypeIdentifier: typeIdentifier
                    ))
                } else {
                    diff = HealthArchiveDiffSummary(
                        fromObservationID: currentObservationID,
                        toObservationID: currentObservationID,
                        sampleTypeIdentifier: typeIdentifier,
                        appearedCount: 0,
                        disappearedCount: 0,
                        representationChangedCount: 0
                    )
                }

                rows.append(SnapshotArchiveTypeRow(
                    typeIdentifier: typeIdentifier,
                    displayName: current?.displayName ?? baselineSummary?.displayName ?? typeIdentifier,
                    currentCount: current?.visibleRecordCount ?? 0,
                    previousCount: baselineSummary?.visibleRecordCount,
                    appearedCount: diff.appearedCount,
                    disappearedCount: diff.disappearedCount,
                    representationChangedCount: diff.representationChangedCount,
                    earliestStartDate: current?.earliestStartDate,
                    latestEndDate: current?.latestEndDate
                ))
            }

            archiveTypeRows = rows.sorted {
                $0.displayName.localizedCompare($1.displayName) == .orderedAscending
            }
            archiveTypeError = nil
        } catch {
            archiveTypeRows = nil
            archiveTypeError = error.localizedDescription
        }
    }

    @ViewBuilder
    private var snapshotToolbarTitle: some View {
        if #available(iOS 26.0, *) {
            Text("Snapshot")
                .font(.headline.weight(.semibold))
                .padding(.horizontal, 18)
                .frame(height: 36)
                .background(Color(.systemBackground).opacity(0.08), in: Capsule())
                .glassEffect(
                    .regular.tint(Color(.systemBackground).opacity(0.12)),
                    in: Capsule()
                )
        } else {
            Text(currentSnapshot.timestamp, format: .dateTime.month().day().hour().minute())
                .font(.headline.weight(.semibold))
                .padding(.horizontal, 18)
                .frame(height: 36)
                .background(.ultraThinMaterial, in: Capsule())
        }
    }

    @ViewBuilder
    private var metadataSheetContent: some View {
        NavigationStack {
            ScrollView {
                VStack(alignment: .leading, spacing: 12) {
                    // Title with Date
                    VStack(spacing: 4) {
                        Text("Snapshot")
                            .font(.headline.weight(.semibold))
                        Text(currentSnapshot.timestamp, format: .dateTime.month().day().hour().minute())
                            .font(.subheadline)
                            .foregroundStyle(.secondary)
                    }
                    .frame(maxWidth: .infinity, alignment: .center)
                    .padding(12)
                    .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))

                    // Data Range
                    SnapshotDataRangeIndicator(
                        oldestRecordDate: summaryEarliestRecordDate,
                        newestRecordDate: summaryLatestRecordDate,
                        quality: currentSnapshot.snapshotQuality
                    )

                    // Summary Stats (compact)
                    VStack(spacing: 12) {
                        if let summaryTypeCount,
                           let summaryRecordCount {
                            HStack(spacing: 16) {
                                statCompact(label: "Types", value: "\(summaryTypeCount)")
                                Divider()
                                statCompact(label: "Records", value: "\(summaryRecordCount)")
                            }
                            .font(.caption)
                            .foregroundStyle(.secondary)
                        } else {
                            Text("Snapshot summary unavailable")
                                .font(.caption)
                                .foregroundStyle(.secondary)
                                .frame(maxWidth: .infinity, alignment: .center)
                        }
                    }
                    .padding(12)
                    .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))

                    // Device (collapsible)
                    DisclosureGroup {
                        VStack(alignment: .leading, spacing: 12) {
                            DetailRow(label: "Version") {
                                Text(extractOSVersion(currentSnapshot.osVersion))
                                    .foregroundStyle(.secondary)
                                    .font(.caption.monospacedDigit())
                            }
                            Divider()
                            DetailRow(label: "Build") {
                                Text(extractBuildNumber(currentSnapshot.osVersion))
                                    .foregroundStyle(.secondary)
                                    .font(.caption.monospacedDigit())
                            }
                        }
                        .padding(.top, 8)
                    } label: {
                        HStack(spacing: 8) {
                            Image(systemName: "iphone")
                                .font(.system(size: 16, weight: .semibold))
                            Text(deviceDisplayName)
                                .font(.subheadline.weight(.semibold))
                        }
                    }
                    .padding(12)
                    .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))

                    // Comparison (if baseline exists)
                    if let baseline {
                        comparisonSection(baseline: baseline)
                    }

                    Spacer()
                }
                .padding(16)
            }
            .navigationTitle("Snapshot")
            .navigationBarTitleDisplayMode(.inline)
        }
    }

    @ViewBuilder
    private func comparisonSection(baseline: HealthSnapshot) -> some View {
        let delta = archiveRecordChangeCount ?? 0
        let deltaPercent = computeDeltaPercent(delta: delta, baseline: baseline)
        let affectedMetricCount = archiveAffectedMetricCount ?? 0
        let isSignificant = delta > 0 || affectedMetricCount > 0 || (deltaPercent > 10)

        DisclosureGroup {
            VStack(alignment: .leading, spacing: 12) {
                DetailRow(label: "Baseline") {
                    Text(baseline.timestamp, format: .dateTime.month().day().hour().minute())
                        .foregroundStyle(.secondary)
                }
                Divider()
                DetailRow(label: "Time Span") {
                    let days = Calendar.current.dateComponents([.day], from: baseline.timestamp, to: currentSnapshot.timestamp).day ?? 0
                    Text(days == 0 ? "Same day" : "\(days) days")
                        .foregroundStyle(.secondary)
                }
                if archiveTypeRows != nil {
                    Divider()
                    DetailRow(label: "Changed Metrics") {
                        Text("\(affectedMetricCount)")
                            .foregroundStyle(.secondary)
                    }
                    Divider()
                    DetailRow(label: "Record Changes") {
                        Text("\(delta)")
                            .foregroundStyle(.secondary)
                    }
                }
            }
            .padding(.top, 8)
        } label: {
            HStack(spacing: 8) {
                Image(systemName: "arrow.left.and.right.square")
                    .font(.system(size: 16, weight: .semibold))
                Text("Comparison")
                    .font(.subheadline.weight(.semibold))
                Spacer()
                if isSignificant {
                    SeverityBadge(delta: delta)
                        .frame(height: 24)
                } else {
                    Text("–")
                        .font(.caption2.weight(.semibold))
                        .foregroundStyle(.secondary)
                }
            }
        }
        .padding(12)
        .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))
    }

    private func shortOSVersion(_ full: String) -> Text {
        if full.hasPrefix("iOS ") {
            let version = full.dropFirst(4).prefix(while: { $0 != " " })
            return Text("iOS \(version)")
        }
        return Text(full)
    }

    private func extractOSVersion(_ full: String) -> String {
        if full.hasPrefix("iOS ") {
            let versionPart = full.dropFirst(4).prefix(while: { $0 != " " && $0 != "(" })
            return String(versionPart)
        }
        return full
    }
    
    private func extractBuildNumber(_ full: String) -> String {
        if let start = full.firstIndex(of: "("), let end = full.firstIndex(of: ")") {
            let buildPart = String(full[full.index(after: start)..<end])
            return buildPart.hasPrefix("Build ") ? String(buildPart.dropFirst(6)) : buildPart
        }
        return full
    }

    private func computeDeltaPercent(delta: Int, baseline: HealthSnapshot) -> Double {
        if let archiveTypeRows {
            let baselineTotal = archiveTypeRows.reduce(0) { $0 + ($1.previousCount ?? 0) }
            return baselineTotal > 0 ? Double(delta) / Double(baselineTotal) * 100 : 0
        }

        let baselineTotal = baseline.hasCurrentCachedSummary ? baseline.cachedRecordCount : 0
        return baselineTotal > 0 ? Double(delta) / Double(baselineTotal) * 100 : 0
    }

    private func statCompact(label: String, value: String) -> some View {
        VStack(alignment: .center, spacing: 2) {
            Text(label)
                .font(.caption2.weight(.medium))
            Text(value)
                .font(.subheadline.weight(.semibold).monospacedDigit())
                .foregroundStyle(.primary)
        }
        .frame(maxWidth: .infinity)
    }

    private var evolutionSection: some View {
        Section("Data Types") {
            if let archiveTypeRows {
                if archiveTypeRows.isEmpty {
                    Text("No data types are available for this snapshot.")
                        .foregroundStyle(.secondary)
                } else {
                    ForEach(archiveTypeRows) { row in
                        NavigationLink {
                            DataTypeSnapshotDetailView(
                                snapshot: currentSnapshot,
                                typeIdentifier: row.typeIdentifier,
                                displayName: row.displayName
                            )
                        } label: {
                            SnapshotArchiveTypeRowView(row: row, hasBaseline: baseline != nil)
                        }
                    }
                }
            } else if baseline == nil {
                Text("This snapshot starts the chain, so no baseline comparison is available.")
                    .foregroundStyle(.secondary)
            } else {
                Text("Cached metric summary unavailable for this snapshot.")
                    .foregroundStyle(.secondary)
            }
        }
    }
}

private struct SnapshotArchiveTypeRow: Identifiable {
    let typeIdentifier: String
    let displayName: String
    let currentCount: Int
    let previousCount: Int?
    let appearedCount: Int
    let disappearedCount: Int
    let representationChangedCount: Int
    let earliestStartDate: Date?
    let latestEndDate: Date?

    var id: String { typeIdentifier }

    var recordChangeCount: Int {
        appearedCount + disappearedCount + representationChangedCount
    }

    var hasChanges: Bool {
        currentDelta != 0 || recordChangeCount > 0
    }

    var currentDelta: Int {
        guard let previousCount else { return currentCount }
        return currentCount - previousCount
    }
}

private struct SnapshotArchiveTypeRowView: View {
    let row: SnapshotArchiveTypeRow
    let hasBaseline: Bool

    private var countText: String {
        "\(row.currentCount)"
    }

    private var changeLabel: String {
        guard hasBaseline else { return "Stored" }
        if row.disappearedCount > 0 {
            return "\(row.disappearedCount) missing"
        }
        if row.appearedCount > 0 {
            return "\(row.appearedCount) new"
        }
        if row.representationChangedCount > 0 {
            return "\(row.representationChangedCount) changed"
        }
        if row.currentDelta != 0 {
            let prefix = row.currentDelta > 0 ? "+" : ""
            return "\(prefix)\(row.currentDelta) records"
        }
        return "No changes"
    }

    private var changeColor: Color {
        guard hasBaseline else { return .secondary }
        if row.disappearedCount > 0 { return .criticalRed }
        if row.hasChanges { return .warningAmber }
        return .secondary
    }

    var body: some View {
        HStack(spacing: 12) {
            VStack(alignment: .leading, spacing: 3) {
                Text(row.displayName)
                    .font(.subheadline)
                Text(row.typeIdentifier)
                    .font(.caption2)
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
                    .truncationMode(.middle)
            }

            Spacer()

            VStack(alignment: .trailing, spacing: 4) {
                Text(countText)
                    .font(.subheadline.monospacedDigit())
                    .foregroundStyle(.primary)
                Text(changeLabel)
                    .font(.caption.weight(.semibold))
                    .foregroundStyle(changeColor)
            }
        }
        .accessibilityElement(children: .combine)
    }
}

private struct SnapshotDataRangeIndicator: View {
    let oldestRecordDate: Date?
    let newestRecordDate: Date?
    let quality: SnapshotQuality

    private var hasDateRange: Bool {
        oldestRecordDate != nil && newestRecordDate != nil
    }

    private var daySpan: Int? {
        guard let oldest = oldestRecordDate, let newest = newestRecordDate else { return nil }
        return Calendar.current.dateComponents([.day], from: oldest, to: newest).day ?? 0
    }

    var body: some View {
        VStack(spacing: 12) {
            HStack(spacing: 8) {
                Text("Data Range")
                    .font(.headline.weight(.semibold))

                Spacer()

                qualityBadge
            }

            if hasDateRange {
                dateRangeVisualization
            } else {
                Text("No dated records available")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .frame(maxWidth: .infinity, alignment: .center)
                    .padding(.vertical, 16)
            }
        }
        .padding(16)
        .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))
    }

    @ViewBuilder
    private var qualityBadge: some View {
        if quality != .complete {
            Label("Incomplete", systemImage: "exclamationmark.triangle.fill")
                .font(.caption.weight(.medium))
                .foregroundStyle(Color.warningAmber)
                .padding(.horizontal, 8)
                .padding(.vertical, 4)
                .background(Color.warningAmber.opacity(0.12), in: Capsule())
        }
    }

    @ViewBuilder
    private var dateRangeVisualization: some View {
        if let oldest = oldestRecordDate, let newest = newestRecordDate, let span = daySpan {
            VStack(spacing: 12) {
                HStack(alignment: .top, spacing: 12) {
                    VStack(alignment: .center, spacing: 4) {
                        Image(systemName: "calendar.badge.clock")
                            .font(.system(size: 16, weight: .semibold))
                            .foregroundStyle(Color.healthyGreen)

                        VStack(alignment: .center, spacing: 2) {
                            Text("Oldest record")
                                .font(.caption2.weight(.medium))
                                .foregroundStyle(.secondary)
                            Text(oldest, format: .dateTime.month().day().year())
                                .font(.caption.weight(.semibold))
                        }
                    }
                    .frame(maxWidth: .infinity)

                    VStack(alignment: .center, spacing: 4) {
                        Text("\(span)")
                            .font(.system(size: 18, weight: .semibold).monospacedDigit())
                            .foregroundStyle(.primary)

                        Text("days")
                            .font(.caption2.weight(.medium))
                            .foregroundStyle(.secondary)
                    }

                    VStack(alignment: .center, spacing: 4) {
                        Image(systemName: "calendar.badge.clock")
                            .font(.system(size: 16, weight: .semibold))
                            .foregroundStyle(Color.accentColor)

                        VStack(alignment: .center, spacing: 2) {
                            Text("Newest record")
                                .font(.caption2.weight(.medium))
                                .foregroundStyle(.secondary)
                            Text(newest, format: .dateTime.month().day().year())
                                .font(.caption.weight(.semibold))
                        }
                    }
                    .frame(maxWidth: .infinity)
                }

                timelineBar
            }
        }
    }

    @ViewBuilder
    private var timelineBar: some View {
        if oldestRecordDate != nil, newestRecordDate != nil {
            ZStack(alignment: .leading) {
                RoundedRectangle(cornerRadius: 3)
                    .fill(Color(.systemGray5))

                RoundedRectangle(cornerRadius: 3)
                    .fill(
                        LinearGradient(
                            gradient: Gradient(colors: [Color.healthyGreen, Color.accentColor]),
                            startPoint: .leading,
                            endPoint: .trailing
                        )
                    )
                    .opacity(0.7)
            }
            .frame(height: 4)
        }
    }
}

private struct DetailRow<Content: View>: View {
    let label: String
    @ViewBuilder let content: () -> Content

    var body: some View {
        HStack {
            Text(label)
            Spacer()
            content()
        }
    }
}

private struct ShareSheet: UIViewControllerRepresentable {
    let items: [Any]

    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: items, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

#Preview {
    NavigationStack {
        SnapshotDetailView(
            snapshot: HealthSnapshot(
                timestamp: .now,
                osVersion: "iOS 26.4",
                deviceName: "Preview iPhone",
                deviceID: "preview-device"
            ),
            baseline: nil,
            profile: LocalDeviceProfile(deviceID: "preview-device")
        )
    }
    .modelContainer(for: [HealthSnapshot.self], inMemory: true)
}
