// ── Broadcast Client ─────────────────────────────────────
// In-app broadcast message client for iOS/watchOS/macOS.
// Part of ByteLystPlatformSDK.

import Foundation

/// In-app message priority levels.
public enum BLBroadcastPriority: String, Codable, Sendable {
    case low = "low"
    case normal = "normal"
    case high = "high"
    case urgent = "urgent"
}

/// In-app message display styles.
public enum BLBroadcastStyle: String, Codable, Sendable {
    case banner = "banner"
    case modal = "modal"
    case toast = "toast"
    case fullscreen = "fullscreen"
}

/// In-app message status.
public enum BLBroadcastStatus: String, Codable, Sendable {
    case unread = "unread"
    case read = "read"
    case dismissed = "dismissed"
}

/// Represents an in-app broadcast message.
public struct BLInAppMessage: Codable, Sendable, Identifiable {
    public let id: String
    public let userId: String
    public let productId: String
    public let broadcastId: String
    public let title: String
    public let body: String
    public let bodyMarkdown: String?
    public let ctaText: String?
    public let ctaUrl: String?
    public let priority: BLBroadcastPriority
    public let style: BLBroadcastStyle
    public let dismissible: Bool
    public let expiresAt: String?
    public let status: BLBroadcastStatus
    public let createdAt: String
    public let updatedAt: String
}

/// Broadcast client for fetching and managing in-app messages.
@available(iOS 15.0, macOS 12.0, watchOS 8.0, *)
public class BLBroadcastClient: ObservableObject {
    private let platformClient: BLPlatformClient
    private var pollTask: Task<Void, Never>?
    
    public init(platformClient: BLPlatformClient) {
        self.platformClient = platformClient
    }
    
    /// List active in-app messages for the current user.
    public func listMessages() async throws -> [BLInAppMessage] {
        let request = try platformClient.buildRequest(path: "/broadcasts")
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed(String(data: data, encoding: .utf8) ?? "Unknown error")
        }
        
        let result = try JSONDecoder().decode(MessagesResponse.self, from: data)
        return result.messages
    }
    
    /// Mark a message as read.
    public func markRead(messageId: String) async throws {
        let request = try platformClient.buildRequest(
            path: "/broadcasts/\(messageId)/read",
            method: "POST"
        )
        let (_, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to mark message as read")
        }
    }
    
    /// Mark a message as dismissed.
    public func markDismissed(messageId: String) async throws {
        let request = try platformClient.buildRequest(
            path: "/broadcasts/\(messageId)/dismiss",
            method: "POST"
        )
        let (_, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to dismiss message")
        }
    }
    
    /// Track a CTA click and get the redirect URL.
    public func trackClick(messageId: String) async throws -> String? {
        let request = try platformClient.buildRequest(
            path: "/broadcasts/\(messageId)/click",
            method: "POST"
        )
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to track click")
        }
        
        let result = try JSONDecoder().decode(ClickResponse.self, from: data)
        return result.redirectUrl
    }
    
    /// Start polling for new messages.
    public func startPolling(interval: TimeInterval = 60, onUpdate: @escaping ([BLInAppMessage]) -> Void) {
        stopPolling()
        
        pollTask = Task {
            while !Task.isCancelled {
                do {
                    let messages = try await listMessages()
                    onUpdate(messages)
                } catch {
                    // Silently ignore polling errors
                }
                
                try? await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000))
            }
        }
    }
    
    /// Stop polling for messages.
    public func stopPolling() {
        pollTask?.cancel()
        pollTask = nil
    }
}

// MARK: - Response Types

private struct MessagesResponse: Codable {
    let messages: [BLInAppMessage]
}

private struct ClickResponse: Codable {
    let success: Bool
    let redirectUrl: String?
}
