//
//  NetworkMonitor.swift
//  SportsTime
//
//  Monitors network connectivity using NWPathMonitor and triggers
//  sync when connectivity is restored after being offline.
//

import Foundation
import Network
import os

/// Monitors network state and triggers sync on connectivity restoration.
@MainActor
final class NetworkMonitor {

    // MARK: - Properties

    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue(label: "com.sportstime.networkmonitor")
    private let logger = Logger(subsystem: "com.sportstime.app", category: "NetworkMonitor")

    /// Current network status
    private(set) var isConnected: Bool = false

    /// Whether the network has ever been in a disconnected state during this session
    private var hasBeenOffline: Bool = false

    /// Debounce timer to avoid rapid sync triggers during network flapping
    private var debounceTask: Task<Void, Never>?

    /// Debounce delay in seconds (2.5s to handle WiFi↔cellular handoffs)
    private let debounceDelay: TimeInterval = 2.5

    /// Callback for when sync should be triggered
    var onSyncNeeded: (() async -> Void)?

    // MARK: - Singleton

    static let shared = NetworkMonitor()

    private init() {}

    // MARK: - Lifecycle

    /// Start monitoring network state.
    /// Call this during app initialization.
    func startMonitoring() {
        monitor.pathUpdateHandler = { [weak self] path in
            Task { @MainActor [weak self] in
                await self?.handlePathUpdate(path)
            }
        }
        monitor.start(queue: queue)
        logger.info("Network monitoring started")
    }

    /// Stop monitoring network state.
    /// Call this during app termination.
    func stopMonitoring() {
        monitor.cancel()
        debounceTask?.cancel()
        debounceTask = nil
        logger.info("Network monitoring stopped")
    }

    // MARK: - Path Update Handler

    private func handlePathUpdate(_ path: NWPath) async {
        let wasConnected = isConnected
        isConnected = path.status == .satisfied

        logger.debug("Network path update: \(path.status == .satisfied ? "connected" : "disconnected")")

        if !isConnected {
            // Mark that we've been offline
            hasBeenOffline = true
            // Cancel any pending sync trigger
            debounceTask?.cancel()
            debounceTask = nil
            logger.info("Network disconnected - sync will trigger on reconnection")
        } else if !wasConnected && hasBeenOffline {
            // We just reconnected after being offline
            logger.info("Network restored - scheduling debounced sync")
            scheduleDebouncedSync()
        }
    }

    // MARK: - Debounced Sync

    private func scheduleDebouncedSync() {
        // Cancel any existing debounce task
        debounceTask?.cancel()

        debounceTask = Task { @MainActor [weak self] in
            guard let self = self else { return }

            // Wait for debounce delay
            do {
                try await Task.sleep(for: .seconds(self.debounceDelay))
            } catch {
                // Task was cancelled (network went offline again or app terminating)
                return
            }

            // Double-check we're still connected
            guard self.isConnected else {
                self.logger.debug("Network disconnected during debounce - cancelling sync")
                return
            }

            self.logger.info("Triggering sync after network restoration")

            // Reset the offline flag since we're now syncing
            self.hasBeenOffline = false

            // Trigger the sync callback
            if let onSyncNeeded = self.onSyncNeeded {
                await onSyncNeeded()
            }
        }
    }
}
