//
//  DiagnosticsView.swift
//  Workouts
//
//  Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
//  On-demand iCloud sync diagnostics, built on the copied indie-diag reference
//  types. Reached from Settings › iCloud Sync › Diagnostics. Generates a fresh
//  snapshot on appear and on pull-to-refresh — never on a timer (see the
//  indie-diag skill's anti-patterns).
//

import SwiftUI

@MainActor
@Observable
final class DiagnosticsViewModel {
    /// One coordinator for the screen's lifetime; it's cheap (no I/O until
    /// `generate()` runs) and each of its pieces tears itself down after each call.
    /// The schema scanner resolves the forward-gate ceiling per file, because
    /// Workouts has two document types with independent version counters — a
    /// `Splits/` file is gated by `RoutineDocument.currentSchemaVersion`, a
    /// `Workouts/` file by `WorkoutDocument.currentSchemaVersion`, and a `Stubs/`
    /// tombstone carries no versioned payload so it's never counted as skipped.
    private let coordinator = DiagnosticsCoordinator(
        containerIdentifier: SyncEngine.containerIdentifier,
        schemaScanner: SchemaSkipScanner(
            ceilingForURL: { url in
                let path = url.path
                // PINNED: routine documents live under the on-disk "Splits/" directory.
                if path.contains("/Splits/") { return RoutineDocument.currentSchemaVersion }
                if path.contains("/Workouts/") { return WorkoutDocument.currentSchemaVersion }
                return .max
            },
            representativeCeiling: max(
                RoutineDocument.currentSchemaVersion,
                WorkoutDocument.currentSchemaVersion
            )
        )
    )

    private(set) var report: DiagnosticsReport?
    private(set) var isRefreshing = false

    func refresh() async {
        isRefreshing = true
        report = await coordinator.generate()
        isRefreshing = false
    }
}

struct DiagnosticsView: View {
    @State private var viewModel = DiagnosticsViewModel()

    var body: some View {
        List {
            if let report = viewModel.report {
                overallSection(report)
                iCloudSection(report)
                documentsSection(report)
                networkSection(report)
                copySection(report)
            } else if viewModel.isRefreshing {
                Section { HStack { ProgressView(); Text("Gathering…").foregroundStyle(.secondary) } }
            }
        }
        .navigationTitle("Diagnostics")
        .navigationBarTitleDisplayMode(.inline)
        .task { await viewModel.refresh() }
        .refreshable { await viewModel.refresh() }
    }

    // MARK: - Sections

    private func overallSection(_ report: DiagnosticsReport) -> some View {
        Section("Overall") {
            HStack {
                Image(systemName: healthSymbol(report.overallHealth))
                    .foregroundStyle(healthColor(report.overallHealth))
                Text(report.overallHealth.rawValue)
                Spacer()
                if viewModel.isRefreshing { ProgressView() }
            }
        }
    }

    private func iCloudSection(_ report: DiagnosticsReport) -> some View {
        Section("iCloud") {
            LabeledContent("Account", value: report.containerStatus.hasSignedInAccount ? "Signed in" : "Signed out")
            LabeledContent("Container", value: report.containerStatus.isAvailable ? "Available" : "Unavailable")
        }
    }

    @ViewBuilder
    private func documentsSection(_ report: DiagnosticsReport) -> some View {
        Section("Documents") {
            LabeledContent("Current") {
                Text("\(report.documentSync.currentCount) / \(report.documentSync.totalCount)")
            }
            if report.documentSync.isSyncing {
                Label("Syncing…", systemImage: "arrow.triangle.2.circlepath")
                    .foregroundStyle(.secondary)
            }

            // Skipped and evicted are presented as a pair: evicted documents are
            // never probed, so the skipped count is a floor. Informational styling,
            // not a warning color — a skipped document is an expected, recoverable
            // state (update the app and it opens).
            if let skips = report.schemaSkips,
               skips.skippedCount > 0 || report.documentSync.evictedCount > 0 {
                LabeledContent("Skipped · Evicted") {
                    Text("\(skips.skippedCount) · \(report.documentSync.evictedCount)")
                        .foregroundStyle(.secondary)
                }
                if skips.skippedCount > 0 {
                    Text("Skipped documents were saved by a newer version of the app — update to open them.")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            } else if report.documentSync.evictedCount > 0 {
                LabeledContent("Evicted", value: "\(report.documentSync.evictedCount)")
            }

            if report.documentSync.conflictCount > 0 {
                LabeledContent("Conflicts", value: "\(report.documentSync.conflictCount)")
                    .foregroundStyle(.orange)
            }
            if report.documentSync.errorCount > 0 {
                LabeledContent("Errors", value: "\(report.documentSync.errorCount)")
                    .foregroundStyle(.orange)
            }
        }
    }

    private func networkSection(_ report: DiagnosticsReport) -> some View {
        Section("Network") {
            LabeledContent("Connection", value: report.networkStatus.connectionType.rawValue)
            LabeledContent("Quality", value: report.networkStatus.quality.rawValue)
            if report.networkStatus.isConstrained {
                Label("Low Data Mode", systemImage: "exclamationmark.circle").foregroundStyle(.secondary)
            }
        }
    }

    private func copySection(_ report: DiagnosticsReport) -> some View {
        Section {
            Button {
                UIPasteboard.general.string = report.summary
            } label: {
                Label("Copy Diagnostics", systemImage: "doc.on.doc")
            }
        } footer: {
            Text("Copies a plain-text summary you can paste into a support message.")
        }
    }

    // MARK: - Health styling

    private func healthSymbol(_ health: DiagnosticHealth) -> String {
        switch health {
        case .healthy: "checkmark.circle.fill"
        case .syncing: "arrow.triangle.2.circlepath"
        case .warning: "exclamationmark.triangle.fill"
        case .critical: "xmark.octagon.fill"
        }
    }

    private func healthColor(_ health: DiagnosticHealth) -> Color {
        switch health {
        case .healthy: .green
        case .syncing: .secondary
        case .warning, .critical: .orange
        }
    }
}
