import SwiftUI
import SwiftData

struct DataTypeSnapshotDetailView: View {
    let snapshot: HealthSnapshot
    let typeIdentifier: String
    let displayName: String

    @Environment(AppSettings.self) private var appSettings
    @Environment(\.dynamicTypeSize) private var dynamicTypeSize
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @Query(sort: \HealthSnapshot.timestamp) private var allSnapshots: [HealthSnapshot]

    @State private var displayedSnapshot: HealthSnapshot?
    @State private var diffState: RecordDiffState = .idle
    @State private var showAddedRecords = false
    @State private var showDisappearedRecords = false
    @State private var showTemporalDistribution = false
    @State private var detailCacheDiagnostic: String?
    @State private var currentCachedTypeSummary: CachedArchiveTypeSummary?
    @State private var previousCachedTypeSummary: CachedArchiveTypeSummary?
    @State private var contentWidth: CGFloat = 744

    private var currentSnapshot: HealthSnapshot {
        displayedSnapshot ?? snapshot
    }

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

    private var previousSnapshot: HealthSnapshot? {
        currentSnapshot.previousInTimeline(timelineSnapshots)
    }

    private var currentTypeCount: TypeCount? {
        typeCount(in: currentSnapshot)
    }

    private var previousTypeCount: TypeCount? {
        previousSnapshot.flatMap(typeCount(in:))
    }

    private var isCurrentTypeContentAliasToPrevious: Bool {
        guard let currentTypeCount,
              let previousTypeCount else { return false }
        return currentTypeCount.contentEquivalentTypeCountID == previousTypeCount.contentRepresentativeTypeCountID
    }

    private var hasTemporalDistributionCache: Bool {
        temporalDistributionInput != nil
    }

    private var temporalDistributionInput: TemporalDistributionInput? {
        guard let currentTypeCount,
              let previousSnapshot,
              let cache = currentTypeCount.detailCache,
              cache.matchesBaseline(previousSnapshot.id) else { return nil }

        return TemporalDistributionInput(
            displayName: currentTypeCount.displayName,
            currentCount: currentTypeCount.count,
            previousCount: previousTypeCount?.count ?? 0,
            detailCache: cache
        )
    }

    private var diffTaskID: String {
        [
            currentSnapshot.id.uuidString,
            previousSnapshot?.id.uuidString ?? "none",
            typeIdentifier
        ].joined(separator: "|")
    }

    private var totalDelta: Int? {
        guard previousSnapshot != nil,
              let currentCount = countValue(for: currentTypeCount),
              let previousCount = countValue(for: previousTypeCount) else { return nil }
        return currentCount - previousCount
    }

    private var currentCountText: String {
        countText(for: currentTypeCount)
    }

    private var previousCountText: String {
        countText(for: previousTypeCount)
    }

    private var quickCurrentCountValue: Int {
        max(currentCachedTypeSummary?.visibleRecordCount ?? currentTypeCount?.count ?? 0, 0)
    }

    private var quickPreviousCountValue: Int {
        max(previousCachedTypeSummary?.visibleRecordCount ?? previousTypeCount?.count ?? 0, 0)
    }

    private var quickAddedDisappeared: (added: Int, disappeared: Int, exact: Bool) {
        if let previousSnapshot,
           let cache = currentTypeCount?.detailCache,
           cache.matchesBaseline(previousSnapshot.id) {
            return (cache.addedCount, cache.disappearedCount, true)
        }

        let net = quickCurrentCountValue - quickPreviousCountValue
        return (max(net, 0), max(-net, 0), false)
    }

    private var recordEvolutionSnapshots: [RecordChangeEvolutionSnapshot] {
        timelineSnapshots.map { snapshot in
            let count = max(typeCount(in: snapshot)?.count ?? 0, 0)
            let fallback = recordEvolutionCachedFallback(for: snapshot)
            return RecordChangeEvolutionSnapshot(
                id: snapshot.id,
                timestamp: snapshot.timestamp,
                localSequenceNumber: snapshot.localSequenceNumber,
                previousSnapshotID: snapshot.previousSnapshotID,
                archiveObservationID: snapshot.archiveObservationID,
                count: count,
                fallbackAdded: fallback.added,
                fallbackDisappeared: fallback.disappeared,
                fallbackIsExact: fallback.exact
            )
        }
    }

    private var simplifiedRecordCounts: (added: Int, disappeared: Int, exact: Bool) {
        if case .loaded(let diff) = diffState {
            return (diff.addedCount, diff.disappearedCount, true)
        }
        return quickAddedDisappeared
    }

    private var currentArchiveObservationID: Int64? {
        currentSnapshot.archiveObservationID
    }

    private var previousArchiveObservationID: Int64? {
        previousSnapshot?.archiveObservationID
    }

    private var isTypeTrackedInCurrentContext: Bool {
        currentTypeCount != nil ||
        previousTypeCount != nil ||
        currentCachedTypeSummary != nil ||
        previousCachedTypeSummary != nil ||
        currentArchiveObservationID != nil
    }

    private var usesSimplifiedDetailUI: Bool {
        LegacyUIMode.isEnabled(
            forceEnabled: appSettings.simplifiedUIModeEnabled,
            horizontalSizeClass: horizontalSizeClass,
            dynamicTypeSize: dynamicTypeSize,
            screenWidth: contentWidth
        )
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                if previousSnapshot == nil {
                    emptyStateContent("No baseline available for this device.", icon: "clock.badge.questionmark")
                } else if !isTypeTrackedInCurrentContext {
                    emptyStateContent("Data type not tracked in selected snapshots.", icon: "eye.slash")
                } else {
                    dataRangeSection
                    recordChangeComparisonSection
                    if usesSimplifiedDetailUI {
                        simplifiedDetailSection
                    } else {
                        recordChangeEvolutionSection
                        temporalDistributionSection
                    }
                }
            }
            .padding(16)
        }
        .navigationTitle(displayName)
        .navigationBarTitleDisplayMode(.inline)
        .safeAreaInset(edge: .top, spacing: 0) {
            SnapshotNavigationHeader(
                snapshots: timelineSnapshots,
                currentSnapshot: currentSnapshot,
                onSnapshotSelected: { displayedSnapshot = $0 }
            )
                .frame(height: 64)
        }
        .toolbar {
            ToolbarItem(placement: .principal) {
                snapshotToolbarTitle
            }
        }
        .background(contentWidthReader)
        .task(id: diffTaskID) {
            await loadArchiveTypeSummaries()
            await loadRecordDiff()
        }
        .navigationDestination(isPresented: $showTemporalDistribution) {
            DataTypeTemporalDistributionView(
                input: temporalDistributionInput,
                displayName: displayName
            )
        }
    }

    private var contentWidthReader: some View {
        GeometryReader { proxy in
            Color.clear
                .onAppear {
                    contentWidth = proxy.size.width
                }
                .onChange(of: proxy.size.width) { _, newWidth in
                    contentWidth = newWidth
                }
        }
    }

    private func typeCount(in snapshot: HealthSnapshot) -> TypeCount? {
        snapshot.typeCounts?.first { $0.typeIdentifier == typeIdentifier }
    }

    private func countText(for typeCount: TypeCount?) -> String {
        guard let typeCount else { return "Not tracked" }
        if typeCount.isUnsupported { return "Unsupported" }
        if typeCount.count < 0 { return "Unavailable" }
        return "\(typeCount.count)"
    }

    private func countValue(for typeCount: TypeCount?) -> Int? {
        guard let typeCount else { return 0 }
        guard !typeCount.isUnsupported, typeCount.count >= 0 else { return nil }
        return typeCount.count
    }

    @ViewBuilder
    private var snapshotToolbarTitle: some View {
        if #available(iOS 26.0, *) {
            Text(displayName)
                .font(.headline.weight(.semibold))
                .lineLimit(1)
                .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(displayName)
                .font(.headline.weight(.semibold))
                .lineLimit(1)
                .padding(.horizontal, 18)
                .frame(height: 36)
                .background(.ultraThinMaterial, in: Capsule())
        }
    }

    @ViewBuilder
    private var dataRangeSection: some View {
        if currentTypeCount != nil || currentCachedTypeSummary != nil {
            DataTypeRangeIndicator(
                earliestDate: currentCachedTypeSummary?.earliestStartDate ?? currentTypeCount?.earliestDate,
                latestDate: currentCachedTypeSummary?.latestEndDate ?? currentTypeCount?.latestDate,
                quality: currentTypeCount?.quality ?? .complete
            )
        }
    }

    private var recordChangeComparisonSection: some View {
        Group {
            if previousSnapshot != nil {
                switch diffState {
            case .loaded(let diff):
                RecordChangeComparisonCard(
                    displayName: displayName,
                    currentCount: quickCurrentCountValue,
                    previousCount: previousCachedTypeSummary?.visibleRecordCount ?? previousTypeCount?.count,
                    addedCount: diff.addedCount,
                    disappearedCount: diff.disappearedCount,
                    isCurrentValid: quickCurrentCountValue >= 0,
                    isPreviousTracked: previousTypeCount != nil || previousCachedTypeSummary != nil,
                    onAddedTap: {
                        if diff.addedCount > 0 {
                            showAddedRecords = true
                        }
                    },
                    onDisappearedTap: {
                        if diff.disappearedCount > 0 {
                            showDisappearedRecords = true
                        }
                    }
                )
                .navigationDestination(isPresented: $showAddedRecords) {
                    if let previous = previousSnapshot {
                        DataTypeRecordListView(
                            title: "New Records",
                            displayName: displayName,
                            totalCount: diff.addedCount,
                            mode: addedRecordListMode(previous: previous),
                            previewRecords: diff.addedRecords,
                            tint: Color.healthyGreen
                        )
                    }
                }
                .navigationDestination(isPresented: $showDisappearedRecords) {
                    DataTypeRecordListView(
                        title: "Missing Records",
                        displayName: displayName,
                        totalCount: diff.disappearedCount,
                        mode: disappearedRecordListMode(),
                        previewRecords: diff.disappearedRecords,
                        tint: Color.criticalRed
                    )
                }

            case .idle:
                VStack(alignment: .leading, spacing: 10) {
                    let quick = quickAddedDisappeared

                    VStack(alignment: .leading, spacing: 8) {
                        Text("Quick Counts")
                            .font(.subheadline.weight(.semibold))

                        HStack {
                            quickStat(label: "Current", value: "\(quickCurrentCountValue)")
                            quickStat(label: "Previous", value: "\(quickPreviousCountValue)")
                        }

                        HStack {
                            quickStat(label: "New", value: "\(quick.added)", color: .healthyGreen)
                            quickStat(label: "Missing", value: "\(quick.disappeared)", color: .criticalRed)
                        }

                        if !quick.exact {
                            Text("New/Missing are net values from observation delta. Exact split needs deep record analysis.")
                                .font(.caption2)
                                .foregroundStyle(.secondary)
                        }
                    }
                    .padding(12)
                    .background(Color(.systemBackground).opacity(0.35), in: RoundedRectangle(cornerRadius: 8))
                }
                .padding(12)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 8))

            case .unavailable:
                VStack(alignment: .leading, spacing: 8) {
                    Label(
                        "Record detail cache is unavailable for this snapshot pair.",
                        systemImage: "exclamationmark.triangle.fill"
                    )
                    .font(.subheadline)
                    .foregroundStyle(Color.warningAmber)

                    #if DEBUG
                    if let detailCacheDiagnostic {
                        Text(detailCacheDiagnostic)
                            .font(.caption2.monospaced())
                            .foregroundStyle(.secondary)
                            .textSelection(.enabled)
                    }
                    #endif
                }
                .padding(12)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color.warningAmber.opacity(0.12), in: RoundedRectangle(cornerRadius: 8))

            case .failed(let message):
                Label(message, systemImage: "exclamationmark.triangle.fill")
                    .font(.subheadline)
                    .foregroundStyle(Color.warningAmber)
                    .padding(12)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(Color.warningAmber.opacity(0.12), in: RoundedRectangle(cornerRadius: 8))

            case .loading:
                HStack(spacing: 8) {
                    ProgressView()
                    Text("Analyzing record changes...")
                        .font(.subheadline)
                        .foregroundStyle(.secondary)
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(12)
                .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
                }
            }
        }
    }

    @ViewBuilder
    private var recordChangeEvolutionSection: some View {
        if previousSnapshot != nil, currentTypeCount != nil || currentCachedTypeSummary != nil {
            RecordChangeEvolutionChart(
                snapshots: recordEvolutionSnapshots,
                currentSnapshotID: currentSnapshot.id,
                typeIdentifier: typeIdentifier,
                displayName: displayName
            )
        }
    }

    private var simplifiedDetailSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Summary")
                .font(.headline.weight(.semibold))

            HStack(spacing: 12) {
                quickStat(label: "Current", value: "\(quickCurrentCountValue)")
                quickStat(label: "Previous", value: "\(quickPreviousCountValue)")
            }

            let quick = simplifiedRecordCounts
            HStack(spacing: 12) {
                quickStat(label: "New", value: "\(quick.added)", color: .healthyGreen)
                quickStat(label: "Missing", value: "\(quick.disappeared)", color: .criticalRed)
            }

            if !quick.exact {
                Text("Exact split needs record analysis.")
                    .font(.caption2)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(12)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
        .accessibilityElement(children: .combine)
        .accessibilityLabel("Summary. Current \(quickCurrentCountValue). Previous \(quickPreviousCountValue). New \(simplifiedRecordCounts.added). Missing \(simplifiedRecordCounts.disappeared).")
    }

    @ViewBuilder
    private var temporalDistributionSection: some View {
        if previousSnapshot != nil, currentTypeCount != nil, !isCurrentTypeContentAliasToPrevious {
            if !hasTemporalDistributionCache {
                VStack(alignment: .leading, spacing: 8) {
                    Label(
                        "Temporal distribution is available only when precomputed cache exists for this snapshot pair.",
                        systemImage: "info.circle"
                    )
                    .font(.caption)
                    .foregroundStyle(.secondary)
                }
                .padding(12)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
            } else {
            Button {
                showTemporalDistribution = true
            } label: {
                HStack(spacing: 10) {
                    Image(systemName: "chart.bar.xaxis")
                        .font(.system(size: 16, weight: .semibold))
                        .foregroundStyle(.secondary)

                    VStack(alignment: .leading, spacing: 2) {
                        Text("Temporal Distribution")
                            .font(.headline.weight(.semibold))
                            .foregroundStyle(.primary)
                        Text("New / missing by time bucket")
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }

                    Spacer()

                    Image(systemName: "chevron.right")
                        .font(.system(size: 12, weight: .semibold))
                        .foregroundStyle(.secondary)
                }
                .padding(12)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
            }
            .buttonStyle(.plain)
            }
        }
    }

    private func emptyStateContent(_ message: String, icon: String) -> some View {
        VStack(spacing: 12) {
            Image(systemName: icon)
                .font(.system(size: 32, weight: .semibold))
                .foregroundStyle(.secondary)

            Text(message)
                .font(.subheadline)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)
        }
        .padding(24)
        .frame(maxWidth: .infinity)
        .background(Color(.systemBackground).opacity(0.5), in: RoundedRectangle(cornerRadius: 12))
    }

    private func quickStat(label: String, value: String, color: Color = .primary) -> some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(label)
                .font(.caption2)
                .foregroundStyle(.secondary)
            Text(value)
                .font(.subheadline.weight(.semibold).monospacedDigit())
                .foregroundStyle(color)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    private func recordEvolutionCachedFallback(for snapshot: HealthSnapshot) -> (added: Int, disappeared: Int, exact: Bool) {
        guard let previous = snapshot.previousInTimeline(timelineSnapshots) else {
            return (0, 0, false)
        }

        if let cache = typeCount(in: snapshot)?.detailCache,
           cache.matchesBaseline(previous.id) {
            return (cache.addedCount, cache.disappearedCount, true)
        }

        return (0, 0, false)
    }

    private func addedRecordListMode(previous: HealthSnapshot) -> RecordListMode {
        if let fromObservationID = previous.archiveObservationID,
           let toObservationID = currentArchiveObservationID {
            return .addedDiff(
                typeIdentifier: typeIdentifier,
                afterDate: previous.timestamp,
                beforeDate: currentSnapshot.timestamp,
                fromObservationID: fromObservationID,
                toObservationID: toObservationID
            )
        }

        return .added(
            typeIdentifier: typeIdentifier,
            afterDate: previous.timestamp,
            beforeDate: currentSnapshot.timestamp
        )
    }

    private func disappearedRecordListMode() -> RecordListMode {
        if let fromObservationID = previousArchiveObservationID,
           let toObservationID = currentArchiveObservationID {
            return .disappearedDiff(
                typeIdentifier: typeIdentifier,
                fromObservationID: fromObservationID,
                toObservationID: toObservationID
            )
        }

        return .disappeared(typeIdentifier: typeIdentifier)
    }

    @MainActor
    private func loadRecordDiff() async {
        guard previousSnapshot != nil else {
            detailCacheDiagnostic = nil
            diffState = .loaded(.empty)
            return
        }

        if isCurrentTypeContentAliasToPrevious {
            detailCacheDiagnostic = nil
            diffState = .loaded(.empty)
            return
        }

        let currentCount = currentCachedTypeSummary?.visibleRecordCount ?? currentTypeCount?.count ?? 0
        let previousCount = previousCachedTypeSummary?.visibleRecordCount ?? previousTypeCount?.count ?? 0

        guard currentCount >= 0, previousCount >= 0 else {
            detailCacheDiagnostic = "counts-invalid current=\(currentCount) previous=\(previousCount)"
            diffState = .unavailable
            return
        }

        if let previousArchiveObservationID,
           let currentArchiveObservationID {
            do {
                let cache = try CoreDataArchiveCacheStore()
                if let cached = try cache.diffSummary(
                    fromObservationID: previousArchiveObservationID,
                    toObservationID: currentArchiveObservationID,
                    sampleTypeIdentifier: typeIdentifier
                ) {
                    detailCacheDiagnostic = "resolver-v6 phase=core-data-diff-cache"
                    diffState = .loaded(DataTypeRecordDiff(cached: cached))
                    return
                }
            } catch {
                detailCacheDiagnostic = "core-data-diff-cache-failed \(error.localizedDescription)"
            }

            do {
                let summary = try await SQLiteHealthArchiveStore.shared.diffSummary(HealthArchiveDiffRequest(
                    fromObservationID: previousArchiveObservationID,
                    toObservationID: currentArchiveObservationID,
                    sampleTypeIdentifier: typeIdentifier
                ))
                detailCacheDiagnostic = "resolver-v5 phase=archive-diff"
                diffState = .loaded(DataTypeRecordDiff(summary: summary))
                return
            } catch {
                detailCacheDiagnostic = "archive-diff-failed \(error.localizedDescription)"
            }
        }

        if let cache = currentTypeCount?.detailCache,
           cache.matchesBaseline(previousSnapshot?.id) {
            detailCacheDiagnostic = "resolver-v7 phase=legacy-detail-cache-read-only"
            diffState = .loaded(DataTypeRecordDiff(cache: cache))
            return
        }

        diffState = .unavailable
    }

    @MainActor
    private func loadArchiveTypeSummaries() async {
        guard currentArchiveObservationID != nil || previousArchiveObservationID != nil else {
            currentCachedTypeSummary = nil
            previousCachedTypeSummary = nil
            return
        }

        do {
            let cache = try CoreDataArchiveCacheStore()
            if let currentArchiveObservationID {
                currentCachedTypeSummary = try cache.typeSummaries(observationID: currentArchiveObservationID)
                    .first { $0.sampleTypeIdentifier == typeIdentifier }
            } else {
                currentCachedTypeSummary = nil
            }

            if let previousArchiveObservationID {
                previousCachedTypeSummary = try cache.typeSummaries(observationID: previousArchiveObservationID)
                    .first { $0.sampleTypeIdentifier == typeIdentifier }
            } else {
                previousCachedTypeSummary = nil
            }
        } catch {
            currentCachedTypeSummary = nil
            previousCachedTypeSummary = nil
            detailCacheDiagnostic = "core-data-type-cache-failed \(error.localizedDescription)"
        }
    }

}

private enum RecordDiffState: Equatable {
    case idle
    case loading
    case unavailable
    case failed(String)
    case loaded(DataTypeRecordDiff)
}

private struct DataTypeRecordDiff: Equatable, Sendable {
    static let previewLimit = 1_000
    static let empty = DataTypeRecordDiff(
        addedCount: 0,
        disappearedCount: 0,
        addedRecords: [],
        disappearedRecords: []
    )

    let addedCount: Int
    let disappearedCount: Int
    let addedRecords: [HealthRecordValue]
    let disappearedRecords: [HealthRecordValue]

    init(
        addedCount: Int,
        disappearedCount: Int,
        addedRecords: [HealthRecordValue],
        disappearedRecords: [HealthRecordValue]
    ) {
        self.addedCount = addedCount
        self.disappearedCount = disappearedCount
        self.addedRecords = addedRecords
        self.disappearedRecords = disappearedRecords
    }

    init(cache: TypeCountDetailCache) {
        self.addedCount = cache.addedCount
        self.disappearedCount = cache.disappearedCount
        self.addedRecords = cache.addedPreviewRecords
        self.disappearedRecords = cache.disappearedPreviewRecords
    }

    init(summary: HealthArchiveDiffSummary) {
        self.addedCount = summary.appearedCount
        self.disappearedCount = summary.disappearedCount
        self.addedRecords = []
        self.disappearedRecords = []
    }

    init(cached: CachedArchiveDiffSummary) {
        self.addedCount = cached.appearedCount
        self.disappearedCount = cached.disappearedCount
        self.addedRecords = []
        self.disappearedRecords = []
    }

    var isPreviewLimited: Bool {
        addedCount > Self.previewLimit || disappearedCount > Self.previewLimit
    }
}

#Preview {
    NavigationStack {
        DataTypeSnapshotDetailView(
            snapshot: HealthSnapshot(
                timestamp: .now,
                osVersion: "iOS 26.4",
                deviceName: "Preview iPhone",
                deviceID: "preview-device"
            ),
            typeIdentifier: "HKQuantityTypeIdentifierStepCount",
            displayName: "Step Count"
        )
    }
    .modelContainer(for: [HealthSnapshot.self, TypeCount.self, YearlyCount.self, HealthRecord.self, TypeDistributionBin.self], inMemory: true)
    .environment(AppSettings())
}
