import Dispatch
import Foundation
import SiliconPulseCore

@MainActor
final class RefreshCoordinator {
    private let reader: SystemSnapshotReader
    private let interval: Duration
    private let onSnapshot: @MainActor (SystemSnapshot) -> Void
    private var timerTask: Task<Void, Never>?
    private var thermalObserver: NSObjectProtocol?
    private var memoryPressureSource: DispatchSourceMemoryPressure?

    init(
        reader: SystemSnapshotReader = SystemSnapshotReader(),
        interval: Duration = .seconds(2),
        onSnapshot: @escaping @MainActor (SystemSnapshot) -> Void)
    {
        self.reader = reader
        self.interval = interval
        self.onSnapshot = onSnapshot
    }

    func start() {
        self.stop()
        self.installNotificationRefreshes()
        self.refreshNow()
        self.timerTask = Task { [weak self] in
            while !Task.isCancelled {
                guard let self else { return }
                try? await Task.sleep(for: self.interval)
                self.refreshNow()
            }
        }
    }

    func stop() {
        self.timerTask?.cancel()
        self.timerTask = nil

        if let thermalObserver {
            NotificationCenter.default.removeObserver(thermalObserver)
            self.thermalObserver = nil
        }

        self.memoryPressureSource?.cancel()
        self.memoryPressureSource = nil
    }

    func refreshNow() {
        let reader = self.reader
        Task { [weak self] in
            let snapshot = await Task.detached(priority: .utility) {
                reader.read()
            }.value
            await MainActor.run {
                self?.onSnapshot(snapshot)
            }
        }
    }

    private func installNotificationRefreshes() {
        self.thermalObserver = NotificationCenter.default.addObserver(
            forName: ProcessInfo.thermalStateDidChangeNotification,
            object: nil,
            queue: .main)
        { [weak self] _ in
            Task { @MainActor in self?.refreshNow() }
        }

        let source = DispatchSource.makeMemoryPressureSource(
            eventMask: [.normal, .warning, .critical],
            queue: .main)
        source.setEventHandler { [weak self] in
            Task { @MainActor in self?.refreshNow() }
        }
        source.resume()
        self.memoryPressureSource = source
    }
}
