import ComposableArchitecture
import Foundation

/// Manages Orpheus chat session lifecycle and real-time event subscriptions.
///
/// This is a singleton that manages creating sessions, subscribing to real-time events,
/// and emitting events to subscribers. Sessions are created synchronously and real-time
/// subscriptions happen in the background.
public actor OrpheusSessionManager {
    @Dependency(\.date) private var date
    private let realtimeClient: OrpheusRealtimeClient
    private var sessionScope: Task<Void, Never>?
    private var realtimeSubscriptionTask: Task<Void, Never>?
    
    private let _sessionFlow = AsyncCurrentValueSubject<OrpheusSession?>(
        initialValue: nil
    )
    
    /// Current session flow. Emits the current session, or nil if no session exists.
    public var sessionFlow: AsyncCurrentValueSubject<OrpheusSession?> {
        _sessionFlow
    }
    
    private let _eventsFlow = AsyncCurrentValueSubject<OrpheusRealtimeEvent?>(
        initialValue: nil
    )
    
    /// Events flow. Emits real-time events from the current session subscription.
    public var eventsFlow: AsyncCurrentValueSubject<OrpheusRealtimeEvent?> {
        _eventsFlow
    }
    
    public init(realtimeClient: OrpheusRealtimeClient) {
        self.realtimeClient = realtimeClient
    }
    
    /// Starts a new session, cancelling any existing session.
    ///
    /// This creates the session, starts the real-time subscription in the background,
    /// and returns the newly created session.
    public func startNewSession() -> OrpheusSession {
        sessionScope?.cancel()
        realtimeSubscriptionTask?.cancel()
        
        let newSession = OrpheusSession(
            id: UUID().uuidString,
            createdAt: date.now.timeIntervalSince1970,
//            lastMessageAt: 0,
            workspaceId: nil
        )
        
        _sessionFlow.value = newSession
        
        // Start real-time subscription in background
        realtimeSubscriptionTask = Task { [weak self] in
            guard let self = self else { return }
            await self.startRealtimeSubscription(sessionId: newSession.id)
        }
        
        return newSession
    }
    
    /// Updates the workspace ID for the current session.
    public func updateWorkspaceId(_ workspaceId: String) {
        if let currentSession = _sessionFlow.value {
            _sessionFlow.value = currentSession.copy(workspaceId: workspaceId)
            print("📡 Updated session workspace: \(workspaceId)")
        }
    }
    
    private func startRealtimeSubscription(sessionId: String) async {
        print("📡 Starting realtime subscription for session: \(sessionId)")
        
        do {
            let eventStream = await realtimeClient.subscribe(sessionId: sessionId)
            
            for try await event in eventStream {
                print("📡 Received realtime event: \(event)")
                _eventsFlow.value = event
                
                // Check if we should continue
                if Task.isCancelled {
                    break
                }
            }
            
            print("📡 Realtime subscription ended for session: \(sessionId)")
            _eventsFlow.value = .disconnected
        } catch is CancellationError {
            print("📡 Realtime subscription cancelled for session: \(sessionId)")
        } catch {
            print("❌ Error in realtime subscription for session: \(sessionId), error: \(error)")
            _eventsFlow.value = .error(error)
        }
    }
    
    /// Closes the session manager, cleaning up resources.
    public func close() {
        print("📡 Cleaning up session manager")
        
        _sessionFlow.value = nil
        _eventsFlow.value = nil
        
        sessionScope?.cancel()
        sessionScope = nil
        realtimeSubscriptionTask?.cancel()
        realtimeSubscriptionTask = nil
        
        Task {
            await realtimeClient.cancel()
        }
    }
}

/// AsyncCurrentValueSubject - A simple async sequence that holds and emits a current value
public final class AsyncCurrentValueSubject<T>: @unchecked Sendable {
    private let lock = NSLock()
    private var _currentValue: T
    private var continuations: [ObjectIdentifier: AsyncStream<T>.Continuation] = [:]
    private var continuationCounter: Int = 0
    
    public init(initialValue: T) {
        self._currentValue = initialValue
    }
    
    public var value: T {
        get {
            lock.lock()
            defer { lock.unlock() }
            return _currentValue
        }
        set {
            lock.lock()
            _currentValue = newValue
            let conts = Array(continuations.values)
            lock.unlock()
            
            // Notify all continuations
            for continuation in conts {
                continuation.yield(newValue)
            }
        }
    }
    
    /// Creates an async stream that emits the current value and subsequent updates
    public func stream() -> AsyncStream<T> {
        AsyncStream { continuation in
            lock.lock()
            // Emit current value immediately
            let current = _currentValue
            let id = ObjectIdentifier(continuation as AnyObject)
            continuations[id] = continuation
            lock.unlock()
            
            continuation.yield(current)
            
            continuation.onTermination = { [weak self] _ in
                self?.removeContinuation(id: id)
            }
        }
    }
    
    private func removeContinuation(id: ObjectIdentifier) {
        lock.lock()
        defer { lock.unlock() }
        continuations.removeValue(forKey: id)
    }
}

