import SwiftUI

struct SettingsView: View {
    @Environment(AppSettings.self) private var appSettings
    @AppStorage("checkFrequencyHours") private var checkFrequencyHours: Int = 6
    @State private var showDeleteCacheConfirm = false
    @State private var showResetDataConfirm = false
    @State private var dataMaintenanceMessage: String?
    @State private var archiveObservationCount: Int?
    @State private var timeoutProfiles: [LocalMetricTimeoutProfile] = []
    @State private var currentDeviceProfile: LocalDeviceProfile?
    @State private var resetScheduled = PrototypeStoreResetPolicy.isResetScheduled(appSupportURL: .applicationSupportDirectory)
    @State private var diagnosticReports: [DiagnosticReportFile] = []
    @State private var diagnosticReportText: DiagnosticReportText?

    private var currentDeviceID: String {
        AppSettings.currentDeviceID
    }

    var body: some View {
        NavigationStack {
            List {
                deviceSection
                interfaceSection
                monitoringSection
                timeoutCalibrationSection
                typeSelectionSections
                dataSection
                diagnosticReportsSection
                aboutSection
            }
            .navigationTitle("Settings")
            .onAppear {
                loadCurrentDeviceProfile()
                loadTimeoutProfiles()
                loadArchiveCacheStatus()
                loadDiagnosticReports()
            }
            .sheet(item: $diagnosticReportText) { report in
                DiagnosticReportSheet(reportText: report.text)
            }
            .confirmationDialog(
                "Delete Rebuildable UI Cache",
                isPresented: $showDeleteCacheConfirm,
                titleVisibility: .visible
            ) {
                Button("Delete UI Cache", role: .destructive) { deleteArchiveCache() }
                Button("Cancel", role: .cancel) { }
            } message: {
                Text("This deletes only derived UI/report cache rows. The SQLite archive remains untouched and can rebuild the cache.")
            }
            .confirmationDialog(
                "Reset Test Databases on Next Launch",
                isPresented: $showResetDataConfirm,
                titleVisibility: .visible
            ) {
                Button("Schedule Reset", role: .destructive) { scheduleTestDataReset() }
                Button("Cancel", role: .cancel) { }
            } message: {
                Text("This schedules deletion of the archive, rebuildable cache, and legacy prototype stores on the next app launch. Force close and reopen HealthProbe after scheduling the reset.")
            }
        }
    }

    // MARK: - Sections

    private var deviceSection: some View {
        Section("This Device") {
            if let profile = currentDeviceProfile {
                HStack {
                    Text("Name")
                    Spacer()
                    TextField("Device name", text: Binding(
                        get: { profile.name },
                        set: { newName in
                            updateCurrentDeviceProfile { profile in
                                profile.name = newName
                            }
                        }
                    ))
                    .multilineTextAlignment(.trailing)
                    .foregroundStyle(.secondary)
                }

                HStack {
                    Text("Color")
                    Spacer()
                    HStack(spacing: 10) {
                        ForEach(DeviceColor.allCases) { dc in
                            ZStack {
                                Circle()
                                    .fill(dc.color)
                                    .frame(width: 24, height: 24)
                                if profile.colorTag == dc.rawValue {
                                    Circle()
                                        .strokeBorder(.primary.opacity(0.75), lineWidth: 2.5)
                                        .frame(width: 24, height: 24)
                                }
                            }
                            .onTapGesture {
                                updateCurrentDeviceProfile { profile in
                                    profile.colorTag = dc.rawValue
                                }
                            }
                            .accessibilityLabel(dc.rawValue.capitalized)
                            .accessibilityAddTraits(profile.colorTag == dc.rawValue ? .isSelected : [])
                        }
                    }
                }
            } else {
                Text("Loading…")
                    .foregroundStyle(.secondary)
            }
        }
    }

    private var monitoringSection: some View {
        Section("Monitoring") {
            Picker("Check Frequency", selection: $checkFrequencyHours) {
                Text("Every 2 hours").tag(2)
                Text("Every 6 hours").tag(6)
                Text("Every 12 hours").tag(12)
                Text("Every 24 hours").tag(24)
            }
        }
    }

    private var interfaceSection: some View {
        Section("Interface") {
            Toggle("Simplified Detail Views", isOn: Binding(
                get: { appSettings.simplifiedUIModeEnabled },
                set: { appSettings.simplifiedUIModeEnabled = $0 }
            ))
        }
    }

    private var timeoutCalibrationSection: some View {
        Section("Timeout Calibration") {
            Toggle("Adaptive Timeouts", isOn: Binding(
                get: { appSettings.adaptiveTimeoutsEnabled },
                set: { appSettings.adaptiveTimeoutsEnabled = $0 }
            ))

            InfoRow(label: "Default Initial Timeout") {
                Text(formatDuration(LocalMetricTimeoutProfile.defaultInitialTimeout))
                    .foregroundStyle(.secondary)
            }

            InfoRow(label: "Maximum Timeout") {
                Text(formatDuration(LocalMetricTimeoutProfile.maximumTimeout))
                    .foregroundStyle(.secondary)
            }

            ForEach(timeoutProfiles) { profile in
                TimeoutProfileRow(
                    profile: profile,
                    onManualTimeoutChanged: { setManualTimeout(profile.metricIdentifier, value: $0) },
                    onSetManual: { setManualTimeout(profile.metricIdentifier, value: profile.effectiveTimeout) },
                    onAutomatic: { returnTimeoutProfileToAutomatic(profile.metricIdentifier) },
                    onReset: { resetTimeoutProfile(profile.metricIdentifier) }
                )
            }

            Button(role: .destructive) {
                resetAllTimeoutProfiles()
            } label: {
                Label("Reset All Learned Timeouts", systemImage: "arrow.counterclockwise")
            }
            .disabled(timeoutProfiles.isEmpty)
        }
    }

    @ViewBuilder
    private var typeSelectionSections: some View {
        Section("Capture Profile") {
            InfoRow(label: "Profile") {
                Text(appSettings.captureProfileDescription)
                    .foregroundStyle(.secondary)
            }

            InfoRow(label: "Selected Types") {
                Text("\(appSettings.selectedTypeCount) of \(appSettings.availableTypeCount) available")
                    .foregroundStyle(.secondary)
            }

            Button {
                appSettings.selectAllAvailableTypes()
            } label: {
                Label("Select All Available Types", systemImage: "checkmark.circle")
            }
            .disabled(appSettings.usesAllAvailableTypeProfile)

            Button {
                appSettings.selectDefaultTypes()
            } label: {
                Label("Select Core Profile", systemImage: "list.bullet")
            }
            .disabled(appSettings.usesDefaultTypeProfile)

            Button(role: .destructive) {
                appSettings.clearSelectedTypes()
            } label: {
                Label("Clear Selection", systemImage: "xmark.circle")
            }
            .disabled(appSettings.selectedTypeIDs.isEmpty)
        }

        ForEach(TypeCategory.allCases, id: \.self) { category in
            Section(category.rawValue) {
                ForEach(HealthKitService.allTypes.filter { $0.category == category }) { type in
                    TypeToggleRow(type: type, appSettings: appSettings)
                }
            }
        }
    }

    private var dataSection: some View {
        Section("Data") {
            InfoRow(label: "Cached Observations") {
                Text(archiveObservationCount.map(String.init) ?? "Unavailable")
                    .foregroundStyle(.secondary)
            }

            Button {
                rebuildArchiveCache()
            } label: {
                Label("Rebuild UI Cache", systemImage: "arrow.triangle.2.circlepath")
            }

            Button(role: .destructive) {
                showDeleteCacheConfirm = true
            } label: {
                Label("Delete UI Cache", systemImage: "trash")
            }
            .disabled((archiveObservationCount ?? 0) == 0)

            Button(role: .destructive) {
                showResetDataConfirm = true
            } label: {
                Label("Reset Test Databases on Next Launch", systemImage: "trash.slash")
            }

            if resetScheduled {
                Text("Reset scheduled. Force close and reopen HealthProbe to recreate the archive and cache from scratch.")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            if let dataMaintenanceMessage {
                Text(dataMaintenanceMessage)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        }
    }

    private var diagnosticReportsSection: some View {
        Section("Diagnostic Reports") {
            if diagnosticReports.isEmpty {
                Text("No saved reports yet")
                    .foregroundStyle(.secondary)
            } else {
                ForEach(diagnosticReports) { report in
                    Button {
                        openDiagnosticReport(report)
                    } label: {
                        VStack(alignment: .leading, spacing: 3) {
                            Text(report.displayTitle)
                                .font(.subheadline.weight(.semibold))
                                .foregroundStyle(.primary)
                                .lineLimit(2)
                            Text(report.displaySubtitle)
                                .font(.caption)
                                .foregroundStyle(.secondary)
                        }
                    }
                    .swipeActions {
                        Button(role: .destructive) {
                            deleteDiagnosticReport(report)
                        } label: {
                            Label("Delete", systemImage: "trash")
                        }
                    }
                }
            }

            Button {
                loadDiagnosticReports()
            } label: {
                Label("Refresh Reports", systemImage: "arrow.clockwise")
            }
        }
    }

    private var aboutSection: some View {
        Section("About") {
            InfoRow(label: "Version") {
                Text(Bundle.main.appVersionString)
                    .foregroundStyle(.secondary)
            }
            InfoRow(label: "Purpose") {
                Text("Read-only HealthKit audit tool")
                    .foregroundStyle(.secondary)
            }
        }
    }

    // MARK: - Actions

    private func loadCurrentDeviceProfile() {
        currentDeviceProfile = LocalDeviceProfileStore.profile(for: currentDeviceID)
        if let currentDeviceProfile {
            LocalDeviceProfileStore.save(currentDeviceProfile)
        }
    }

    private func updateCurrentDeviceProfile(_ mutate: (inout LocalDeviceProfile) -> Void) {
        var profile = currentDeviceProfile ?? LocalDeviceProfileStore.profile(for: currentDeviceID)
        mutate(&profile)
        currentDeviceProfile = profile
        LocalDeviceProfileStore.save(profile)
    }

    private func loadTimeoutProfiles() {
        timeoutProfiles = LocalMetricTimeoutProfileStore.allProfiles()
    }

    private func resetAllTimeoutProfiles() {
        LocalMetricTimeoutProfileStore.resetAll()
        loadTimeoutProfiles()
    }

    private func setManualTimeout(_ metricIdentifier: String, value: TimeInterval) {
        updateTimeoutProfile(metricIdentifier) { profile in
            profile.setManualTimeout(value)
        }
    }

    private func returnTimeoutProfileToAutomatic(_ metricIdentifier: String) {
        updateTimeoutProfile(metricIdentifier) { profile in
            profile.returnToAutomatic()
        }
    }

    private func resetTimeoutProfile(_ metricIdentifier: String) {
        updateTimeoutProfile(metricIdentifier) { profile in
            profile.resetLearning()
        }
    }

    private func updateTimeoutProfile(
        _ metricIdentifier: String,
        mutate: (inout LocalMetricTimeoutProfile) -> Void
    ) {
        guard let index = timeoutProfiles.firstIndex(where: { $0.metricIdentifier == metricIdentifier }) else {
            return
        }
        mutate(&timeoutProfiles[index])
        LocalMetricTimeoutProfileStore.save(timeoutProfiles[index])
        loadTimeoutProfiles()
    }

    private func loadArchiveCacheStatus() {
        resetScheduled = PrototypeStoreResetPolicy.isResetScheduled(appSupportURL: .applicationSupportDirectory)
        do {
            archiveObservationCount = try CoreDataArchiveCacheStore().observationCount()
        } catch {
            archiveObservationCount = nil
            dataMaintenanceMessage = "Cache status failed: \(error.localizedDescription)"
        }
    }

    private func loadDiagnosticReports() {
        do {
            diagnosticReports = try DiagnosticReportStore.list(limit: 20)
        } catch {
            dataMaintenanceMessage = "Diagnostic report list failed: \(error.localizedDescription)"
            diagnosticReports = []
        }
    }

    private func openDiagnosticReport(_ report: DiagnosticReportFile) {
        do {
            diagnosticReportText = DiagnosticReportText(text: try DiagnosticReportStore.read(report))
        } catch {
            dataMaintenanceMessage = "Diagnostic report open failed: \(error.localizedDescription)"
            loadDiagnosticReports()
        }
    }

    private func deleteDiagnosticReport(_ report: DiagnosticReportFile) {
        do {
            try DiagnosticReportStore.delete(report)
            loadDiagnosticReports()
        } catch {
            dataMaintenanceMessage = "Diagnostic report delete failed: \(error.localizedDescription)"
            loadDiagnosticReports()
        }
    }

    private func rebuildArchiveCache() {
        do {
            let summary = try CoreDataArchiveCacheStore().rebuild(fromArchiveAt: SQLiteHealthArchiveStore.defaultDatabaseURL)
            archiveObservationCount = summary.observationRows
            dataMaintenanceMessage = "UI cache rebuilt: \(summary.observationRows) observations."
        } catch {
            dataMaintenanceMessage = "Cache rebuild failed: \(error.localizedDescription)"
            loadArchiveCacheStatus()
        }
    }

    private func deleteArchiveCache() {
        do {
            try CoreDataArchiveCacheStore().deleteCache()
            archiveObservationCount = 0
            dataMaintenanceMessage = "UI cache deleted. Archive data was preserved."
        } catch {
            dataMaintenanceMessage = "Cache delete failed: \(error.localizedDescription)"
            loadArchiveCacheStatus()
        }
    }

    private func scheduleTestDataReset() {
        PrototypeStoreResetPolicy.requestResetOnNextLaunch()
        resetScheduled = true
        dataMaintenanceMessage = "Reset scheduled. Force close and reopen HealthProbe to wipe test databases and start from a clean archive timeline."
    }
}

// MARK: - Subviews

private struct DiagnosticReportText: Identifiable {
    let id = UUID()
    let text: String
}

private struct TypeToggleRow: View {
    let type: MonitoredType
    let appSettings: AppSettings

    var body: some View {
        Toggle(isOn: Binding(
            get: { appSettings.isEnabled(type) },
            set: { _ in appSettings.toggle(type) }
        )) {
            VStack(alignment: .leading, spacing: 2) {
                Text(type.displayName)
                if type.objectType == nil {
                    Text("Unavailable on this iOS")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
        }
        .disabled(type.objectType == nil)
        .accessibilityLabel("\(type.displayName), \(appSettings.isEnabled(type) ? "enabled" : "disabled")")
    }
}

private struct TimeoutProfileRow: View {
    let profile: LocalMetricTimeoutProfile
    let onManualTimeoutChanged: (TimeInterval) -> Void
    let onSetManual: () -> Void
    let onAutomatic: () -> Void
    let onReset: () -> Void

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(alignment: .firstTextBaseline) {
                Text(profile.displayName)
                    .font(.subheadline.weight(.semibold))
                Spacer()
                Text(formatDuration(profile.effectiveTimeout))
                    .font(.subheadline.weight(.semibold))
                    .foregroundStyle(.secondary)
            }

            HStack {
                Text(profile.timeoutMode.capitalized)
                Spacer()
                Text("Success \(profile.successCount)")
                Text("Timeouts \(profile.timeoutCount)")
            }
            .font(.caption)
            .foregroundStyle(.secondary)

            HStack {
                Text("Last success")
                Spacer()
                Text(profile.lastSuccessfulElapsed > 0 ? formatDuration(profile.lastSuccessfulElapsed) : "none")
            }
            .font(.caption)
            .foregroundStyle(.secondary)

            HStack {
                Text("Last timeout")
                Spacer()
                Text(profile.lastTimeoutElapsed > 0 ? formatDuration(profile.lastTimeoutElapsed) : "none")
            }
            .font(.caption)
            .foregroundStyle(.secondary)

            HStack {
                Text("p95 success")
                Spacer()
                Text(profile.p95SuccessfulElapsedIfAvailable.map(formatDuration) ?? "collecting")
            }
            .font(.caption)
            .foregroundStyle(.secondary)

            if profile.isManuallyOverridden {
                Stepper(
                    "Manual \(formatDuration(profile.manualTimeoutValue))",
                    value: Binding(
                        get: { profile.manualTimeoutValue },
                        set: onManualTimeoutChanged
                    ),
                    in: LocalMetricTimeoutProfile.defaultInitialTimeout...LocalMetricTimeoutProfile.maximumTimeout,
                    step: 5
                )
                .font(.caption)
            }

            HStack {
                Button("Set Manual") {
                    onSetManual()
                }
                .buttonStyle(.borderless)

                Button("Automatic") {
                    onAutomatic()
                }
                .buttonStyle(.borderless)
                .disabled(!profile.isManuallyOverridden)

                Spacer()

                Button("Reset", role: .destructive) {
                    onReset()
                }
                .buttonStyle(.borderless)
            }
            .font(.caption)
        }
        .padding(.vertical, 4)
    }
}

private struct InfoRow<Content: View>: View {
    let label: String
    @ViewBuilder let content: () -> Content
    var body: some View {
        HStack { Text(label); Spacer(); content() }
    }
}

private extension Bundle {
    var appVersionString: String {
        let version = infoDictionary?["CFBundleShortVersionString"] as? String ?? "–"
        let build   = infoDictionary?["CFBundleVersion"] as? String ?? "–"
        return "\(version) (\(build))"
    }
}

private func formatDuration(_ seconds: TimeInterval) -> String {
    if seconds <= 0 { return "none" }
    if seconds < 60 { return String(format: "%.0fs", seconds) }
    return "\(Int(seconds) / 60)m \(Int(seconds) % 60)s"
}

#Preview {
    SettingsView()
        .environment(AppSettings())
}
