import Foundation
import UserNotifications

enum PingService {
    private static let stateStore = PingStateStore()

    static func suppressChecks(for hostname: String, duration: TimeInterval) async {
        await stateStore.suppressChecks(for: hostname, duration: duration)
    }

    static func ping(hostname: String, apiKey: String, notificationsEnabled: Bool = true) async -> Bool {
        if await stateStore.shouldSkipPing(for: hostname) {
            return false
        }

        guard let url = URL(string: "https://\(hostname)/api/v2/ping") else {
            print("❌ [PingService] Invalid URL for \(hostname)")
            return false
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
        request.timeoutInterval = 10

        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 {
                await handlePingFailure(for: hostname, notificationsEnabled: notificationsEnabled)
                return false
            }

            if let result = try? JSONDecoder().decode([String: String].self, from: data), result["response"] == "pong" {
                await handlePingSuccess(for: hostname, notificationsEnabled: notificationsEnabled)
                return true
            } else {
                await handlePingFailure(for: hostname, notificationsEnabled: notificationsEnabled)
                return false
            }
        } catch {
            await handlePingFailure(for: hostname, notificationsEnabled: notificationsEnabled)
            return false
        }
    }

    private static func handlePingSuccess(for hostname: String, notificationsEnabled: Bool) async {
        if let notification = await stateStore.recordSuccess(for: hostname, notificationsEnabled: notificationsEnabled) {
            sendNotification(title: notification.title, body: notification.body)
        }
    }

    private static func handlePingFailure(for hostname: String, notificationsEnabled: Bool) async {
        let alertGracePeriod = UserDefaults.standard.integer(forKey: "alertGracePeriod")
        if let notification = await stateStore.recordFailure(
            for: hostname,
            notificationsEnabled: notificationsEnabled,
            gracePeriod: TimeInterval(alertGracePeriod)
        ) {
            sendNotification(title: notification.title, body: notification.body)
        }
    }

    private static func sendNotification(title: String, body: String) {
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default

        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request)
    }
}

private actor PingStateStore {
    private var previousPingStates: [String: Bool] = [:]
    private var suppressedUntil: [String: Date] = [:]
    private var failureStartedAt: [String: Date] = [:]

    func suppressChecks(for hostname: String, duration: TimeInterval) {
        suppressedUntil[hostname] = Date().addingTimeInterval(duration)
        previousPingStates[hostname] = false
        failureStartedAt.removeValue(forKey: hostname)
    }

    func shouldSkipPing(for hostname: String) -> Bool {
        if let suppressedUntil = suppressedUntil[hostname], suppressedUntil > Date() {
            return true
        }

        suppressedUntil.removeValue(forKey: hostname)
        return false
    }

    func recordSuccess(for hostname: String, notificationsEnabled: Bool) -> PingNotification? {
        let wasPreviouslyDown = previousPingStates[hostname] == false
        previousPingStates[hostname] = true
        failureStartedAt.removeValue(forKey: hostname)

        guard wasPreviouslyDown, notificationsEnabled else {
            return nil
        }

        return PingNotification(
            title: "Server Online",
            body: "\(hostname) is now online"
        )
    }

    func recordFailure(for hostname: String, notificationsEnabled: Bool, gracePeriod: TimeInterval) -> PingNotification? {
        let now = Date()
        let startedAt = failureStartedAt[hostname] ?? now
        if failureStartedAt[hostname] == nil {
            failureStartedAt[hostname] = startedAt
        }

        guard now.timeIntervalSince(startedAt) >= gracePeriod else {
            return nil
        }

        let wasPreviouslyUp = previousPingStates[hostname] != false
        previousPingStates[hostname] = false
        failureStartedAt.removeValue(forKey: hostname)

        guard wasPreviouslyUp, notificationsEnabled else {
            return nil
        }

        return PingNotification(
            title: "Server Offline",
            body: "\(hostname) is offline"
        )
    }
}

private struct PingNotification {
    let title: String
    let body: String
}
