import Foundation
import SwiftUI

enum ConnectionStatus: Equatable {
    case disconnected
    case connecting
    case connected
    case reconnecting
}

@Observable
final class AppState {
    var isLoggedIn = false
    var currentUser: User?
    var connectionStatus: ConnectionStatus = .disconnected
    var email: String = ""

    let chatClient = ChatClient()

    private var reconnectTask: Task<Void, Never>?
    private var notificationTask: Task<Void, Never>?
    private var isReconnecting = false
    private var backgroundedAt: Date?

    /// Start listening for connection state changes (call after login)
    func startConnectionMonitor() {
        notificationTask?.cancel()
        notificationTask = Task { [weak self] in
            guard let self else { return }
            let stream = await chatClient.makeNotificationStream()
            for await notification in stream {
                guard !Task.isCancelled else { break }
                if case .connectionStateChanged(let connected) = notification {
                    await MainActor.run {
                        if connected {
                            self.connectionStatus = .connected
                            self.isReconnecting = false
                            self.reconnectTask?.cancel()
                            self.reconnectTask = nil
                        } else if self.isLoggedIn, !self.isReconnecting {
                            // Only start reconnect if not already reconnecting
                            // (reconnect() internally calls disconnect() which fires this)
                            self.connectionStatus = .disconnected
                            self.attemptReconnect()
                        }
                    }
                }
            }
        }
    }

    /// Attempt reconnect with exponential backoff; immediate logout on auth failure
    @MainActor
    private func attemptReconnect() {
        reconnectTask?.cancel()
        isReconnecting = true
        reconnectTask = Task { [weak self] in
            guard let self else { return }
            let maxAttempts = 5
            var delay: TimeInterval = Constants.reconnectBaseDelay

            for attempt in 1...maxAttempts {
                guard !Task.isCancelled, self.isLoggedIn else { return }

                self.connectionStatus = .reconnecting
                #if DEBUG
                print("DEBUG AppState: reconnect attempt \(attempt)/\(maxAttempts), delay=\(delay)s")
                #endif

                try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                guard !Task.isCancelled, self.isLoggedIn else { return }

                let result = await self.chatClient.reconnect()
                switch result {
                case .success:
                    self.connectionStatus = .connected
                    self.isReconnecting = false
                    #if DEBUG
                    print("DEBUG AppState: reconnected on attempt \(attempt)")
                    #endif
                    return
                case .authFailed:
                    // Keys rotated or invalid — logout immediately, don't retry
                    self.isReconnecting = false
                    #if DEBUG
                    print("DEBUG AppState: auth failed (keys likely rotated), logging out immediately")
                    #endif
                    await self.logout()
                    return
                case .networkError:
                    // Network issue — retry with backoff
                    delay = min(delay * 2, Constants.reconnectMaxDelay)
                }
            }

            // All network retries exhausted → force logout
            self.isReconnecting = false
            guard !Task.isCancelled, self.isLoggedIn else { return }
            #if DEBUG
            print("DEBUG AppState: reconnect failed after \(maxAttempts) attempts, logging out")
            #endif
            await self.logout()
        }
    }

    // MARK: - App Lifecycle

    func handleEnteredBackground() {
        backgroundedAt = Date()
    }

    @MainActor
    func handleBecameActive() {
        guard isLoggedIn, !isReconnecting else { return }
        let wasInBackground = backgroundedAt.map { Date().timeIntervalSince($0) } ?? 0
        backgroundedAt = nil

        Task {
            let alive = await chatClient.isConnectionAlive()
            if !alive {
                #if DEBUG
                print("DEBUG AppState: foreground — connection dead, reconnecting")
                #endif
                await MainActor.run {
                    guard !self.isReconnecting else { return }
                    self.connectionStatus = .reconnecting
                    self.attemptReconnect()
                }
            } else if wasInBackground > 30 {
                // Connection appears alive but was backgrounded a long time —
                // force reconnect to ensure fresh state
                #if DEBUG
                print("DEBUG AppState: foreground — stale connection (\(Int(wasInBackground))s), reconnecting")
                #endif
                await MainActor.run {
                    guard !self.isReconnecting else { return }
                    self.connectionStatus = .reconnecting
                    self.attemptReconnect()
                }
            } else {
                #if DEBUG
                print("DEBUG AppState: foreground — connection alive (\(Int(wasInBackground))s in bg)")
                #endif
            }
        }
    }

    func logout() async {
        isReconnecting = false
        reconnectTask?.cancel()
        reconnectTask = nil
        notificationTask?.cancel()
        notificationTask = nil
        await chatClient.disconnect()
        KeychainService.deleteCredentials()
        isLoggedIn = false
        currentUser = nil
        connectionStatus = .disconnected
        email = ""
    }
}
