import Combine
import CoreData
import SwiftUI
import OSLog

private let logger = Logger(subsystem: "nahbar", category: "CloudSync")

// MARK: - SyncState

enum SyncState: Equatable {
    case disabled               // iCloud-Sync ist ausgeschaltet
    case accountNotAvailable    // Kein iCloud-Account angemeldet
    case idle                   // Sync aktiv, keine laufende Operation
    case syncing                // Import, Export oder Setup läuft gerade
    case succeeded(Date)        // Letzter Sync erfolgreich
    case failed(String)         // Fehler beim letzten Sync-Versuch

    var statusText: String {
        switch self {
        case .disabled:            return "Deaktiviert"
        case .accountNotAvailable: return "Kein iCloud-Account"
        case .idle:                return "Bereit"
        case .syncing:             return "Synchronisiere…"
        case .succeeded(let date): return "Zuletzt: \(date.relativeDescription)"
        case .failed(let msg):     return "Fehler: \(msg)"
        }
    }

    var systemImage: String {
        switch self {
        case .disabled:            return "icloud.slash"
        case .accountNotAvailable: return "person.crop.circle.badge.exclamationmark"
        case .idle:                return "icloud"
        case .syncing:             return "arrow.triangle.2.circlepath.icloud"
        case .succeeded:           return "checkmark.icloud"
        case .failed:              return "exclamationmark.icloud"
        }
    }

    var isError: Bool {
        if case .failed = self { return true }
        if case .accountNotAvailable = self { return true }
        return false
    }
}

// MARK: - CloudSyncMonitor

/// Beobachtet NSPersistentCloudKitContainer-Events und publiziert den aktuellen
/// Sync-Status. UI-Updates werden explizit auf den Main Thread dispatched.
final class CloudSyncMonitor: ObservableObject {

    @Published private(set) var state: SyncState = .disabled
    @Published private(set) var lastSuccessfulSync: Date?

    private var eventObserver: NSObjectProtocol?
    private var accountObserver: NSObjectProtocol?

    // MARK: - Public API

    func startMonitoring(iCloudEnabled: Bool) {
        stopMonitoring()

        guard iCloudEnabled else {
            setStateOnMain(.disabled)
            logger.info("CloudSyncMonitor: iCloud deaktiviert")
            return
        }

        updateAccountState()
        setupEventObserver()
        setupAccountObserver()
        logger.info("CloudSyncMonitor: Monitoring gestartet")
    }

    func stopMonitoring() {
        if let obs = eventObserver {
            NotificationCenter.default.removeObserver(obs)
            eventObserver = nil
        }
        if let obs = accountObserver {
            NotificationCenter.default.removeObserver(obs)
            accountObserver = nil
        }
    }

    // MARK: - Private

    private func updateAccountState() {
        if FileManager.default.ubiquityIdentityToken == nil {
            setStateOnMain(.accountNotAvailable)
            logger.warning("CloudSyncMonitor: Kein iCloud-Account verfügbar")
            AppEventLog.shared.record("Kein iCloud-Account angemeldet", level: .warning, category: "iCloud")
        } else {
            // Nur aus inaktiven States in .idle wechseln
            DispatchQueue.main.async { [weak self] in
                guard let self else { return }
                if case .accountNotAvailable = self.state { self.state = .idle }
                if case .disabled = self.state { self.state = .idle }
            }
        }
    }

    private func setupEventObserver() {
        eventObserver = NotificationCenter.default.addObserver(
            forName: NSPersistentCloudKitContainer.eventChangedNotification,
            object: nil,
            queue: nil   // Handler selbst dispatcht auf Main
        ) { [weak self] notification in
            self?.handleCloudKitEvent(notification)
        }
    }

    private func setupAccountObserver() {
        accountObserver = NotificationCenter.default.addObserver(
            forName: .NSUbiquityIdentityDidChange,
            object: nil,
            queue: nil
        ) { [weak self] _ in
            self?.updateAccountState()
        }
    }

    private func handleCloudKitEvent(_ notification: Notification) {
        guard let event = notification.userInfo?[
            NSPersistentCloudKitContainer.eventNotificationUserInfoKey
        ] as? NSPersistentCloudKitContainer.Event else { return }

        switch event.type {
        case .setup, .import, .export:
            if event.endDate == nil {
                setStateOnMain(.syncing)
                logger.debug("CloudSync: \(String(describing: event.type)) gestartet")
            } else if event.succeeded {
                let end = event.endDate ?? Date()
                setStateOnMain(.succeeded(end))
                DispatchQueue.main.async { [weak self] in
                    self?.lastSuccessfulSync = end
                }
                logger.info("CloudSync: \(String(describing: event.type)) erfolgreich um \(end)")
                AppEventLog.shared.record(
                    "\(String(describing: event.type).capitalized) erfolgreich",
                    level: .success, category: "iCloud"
                )
            } else if let error = event.error {
                let msg = (error as NSError).localizedDescription
                setStateOnMain(.failed(msg))
                logger.error("CloudSync: \(String(describing: event.type)) fehlgeschlagen: \(msg)")
                AppEventLog.shared.record(
                    "\(String(describing: event.type).capitalized) fehlgeschlagen: \(msg)",
                    level: .error, category: "iCloud"
                )
            }
        @unknown default:
            logger.warning("CloudSync: Unbekannter Event-Typ empfangen")
        }
    }

    private func setStateOnMain(_ newState: SyncState) {
        DispatchQueue.main.async { [weak self] in
            self?.state = newState
        }
    }

    deinit {
        if let obs = eventObserver  { NotificationCenter.default.removeObserver(obs) }
        if let obs = accountObserver { NotificationCenter.default.removeObserver(obs) }
    }
}

// MARK: - Date Extension

private extension Date {
    var relativeDescription: String {
        let seconds = Date().timeIntervalSince(self)
        if seconds < 60    { return "gerade eben" }
        if seconds < 3600  { return "vor \(Int(seconds / 60)) Min." }
        if seconds < 86400 { return "vor \(Int(seconds / 3600)) Std." }
        let fmt = DateFormatter()
        fmt.locale = Locale(identifier: "de_DE")
        fmt.dateStyle = .short
        fmt.timeStyle = .short
        return fmt.string(from: self)
    }
}
