import Foundation
import Combine
import WebRTC

class CallViewModel: ObservableObject {
    
    // MARK: - Published State
    @Published var currentUser: User?
    @Published var callState: CallState = CallState(status: .idle)
    @Published var availableInterpreters: [String] = []
    @Published var interpreterState: PresenceState = .unavailable // Default to unavailable locally
    
    // Notification State
    @Published var remotePeerSeesMe: Bool = false
    @Published var showVideoVisibleToast: Bool = false
    
    // Stats State
    @Published var onlineInterpretersCount: Int = 0
    @Published var onlineCallersCount: Int = 0
    
    @Published var errorMessage: String?
    
    // Abuse Reporting
    @Published var lastCallId: String?
    @Published var lastRemoteUser: String?
    
    // Services
    private let signaling: SignalingService
    private let webRTC: WebRTCService
    private var cancellables = Set<AnyCancellable>()
    
    // WebRTC Data
    // In a real app, these would be exposed via a VideoView struct dependent on the WebRTC library's rendering
    @Published var localVideoTrack: RTCVideoTrack?
    @Published var remoteVideoTrack: RTCVideoTrack?
    
    init() {
        self.signaling = SignalingService()
        self.webRTC = WebRTCService()
        
        setupBindings()
    }
    
    // MARK: - Setup
    private func setupBindings() {
        // Signaling - Connection State
        signaling.isConnected
            .dropFirst() // Ignore initial 'false' state
            .receive(on: DispatchQueue.main)
            .sink { [weak self] isConnected in
                if !isConnected {
                    self?.errorMessage = "Disconnected from server"
                    // Handle disconnect state
                }
            }
            .store(in: &cancellables)
        
        // Signaling - Messages
        signaling.messageReceived
            .receive(on: DispatchQueue.main)
            .sink { [weak self] msg in
                self?.handleSignalingMessage(msg)
            }
            .store(in: &cancellables)
            
        // WebRTC Delegate
        webRTC.delegate = self
    }
    
    // MARK: - Authentication / Registration
    func login(username: String, role: Role) {
        // 1. Optimistically set user so UI transitions immediately
        self.currentUser = User(username: username, role: role)
        // Reset interpreter state to unavailable on login/registration to match server default
        if role == .interpreter {
            self.interpreterState = .unavailable
        }
        
        // 2. Subscribe to connection status to send REGISTER only when actually connected
        signaling.isConnected
            .filter { $0 } // Wait for 'true'
            .first()       // Do this only once per login call
            .sink { [weak self] _ in
                print("Connected! Sending REGISTER for \(username)")
                let msg = SignalingMessage(type: "REGISTER", callId: nil, from: nil, to: nil, payload: AnyCodable(["username": username, "role": role.rawValue]))
                self?.signaling.send(msg)
            }
            .store(in: &cancellables)
        
        // 3. Start connection
        signaling.connect()
    }
    
    func logout() {
        signaling.disconnect()
        currentUser = nil
        callState = CallState(status: .idle)
        availableInterpreters = []
        localVideoTrack = nil
        remoteVideoTrack = nil
        errorMessage = nil
        
        // Cancel bindings to prevent receiving messages after logout
        cancellables.removeAll()
        // Re-setup to be ready for next login
        setupBindings()
    }
    
    // MARK: - Call Logic (Caller)
    func callInterpreter(username: String) {
        guard let myUser = currentUser, myUser.role == .caller else { return }
        
        callState = CallState(remoteUser: username, status: .calling)
        
        let msg = SignalingMessage(type: "CALL_REQUEST", callId: nil, from: myUser.username, to: username, payload: nil)
        signaling.send(msg)
    }
    
    // MARK: - Call Logic (Interpreter)
    func acceptCall() {
        guard let myUser = currentUser, myUser.role == .interpreter, let callId = callState.callId, let remoteUser = callState.remoteUser else { return }
        
        let msg = SignalingMessage(type: "CALL_ACCEPT", callId: callId, from: myUser.username, to: remoteUser, payload: nil)
        signaling.send(msg)
        
        callState.status = .inCall // Transition to In Call
        startWebRTC(isCaller: false)
    }
    
    func declineCall() {
        guard let myUser = currentUser, myUser.role == .interpreter, let callId = callState.callId, let remoteUser = callState.remoteUser else { return }
        
        let msg = SignalingMessage(type: "CALL_DECLINE", callId: callId, from: myUser.username, to: remoteUser, payload: nil)
        signaling.send(msg)
        
        resetCallState()
        // Revert to AVAILABLE after declining (or should match server behavior)
        self.interpreterState = .available
    }
    
    // MARK: - Presence Management
    func togglePresence() {
        guard let myUser = currentUser, myUser.role == .interpreter else { return }
        
        let newState: PresenceState = (interpreterState == .available) ? .unavailable : .available
        
        // Optimistic update
        self.interpreterState = newState
        
        let msg = SignalingMessage(type: "SET_PRESENCE", callId: nil, from: myUser.username, to: nil, payload: AnyCodable(["state": newState.rawValue]))
        signaling.send(msg)
    }
    
    func reportAbuse(message: String) {
        guard let myUser = currentUser, myUser.role == .interpreter, let callId = lastCallId, let remoteUser = lastRemoteUser else { return }
        
        let msg = SignalingMessage(type: "REPORT_ABUSE", callId: callId, from: myUser.username, to: nil, payload: AnyCodable(["message": message, "offender": remoteUser]))
        signaling.send(msg)
        
        // Clear last call info so button disappears
        self.lastCallId = nil
        self.lastRemoteUser = nil
    }
    
    // MARK: - Call Logic (Shared)
    func hangup() {
        guard let myUser = currentUser, let callId = callState.callId, let remoteUser = callState.remoteUser else { return }
        
        let msg = SignalingMessage(type: "HANGUP", callId: callId, from: myUser.username, to: remoteUser, payload: nil)
        signaling.send(msg)
        
        endCall()
    }
    
    private func endCall() {
        webRTC.disconnect()
        resetCallState()
        remoteVideoTrack = nil
    }
    
    private func resetCallState() {
        // Save last call info for abuse reporting
        if let callId = callState.callId, let remoteUser = callState.remoteUser {
            self.lastCallId = callId
            self.lastRemoteUser = remoteUser
        }
        
        callState = CallState(status: .idle)
        remotePeerSeesMe = false
        showVideoVisibleToast = false
    }
    
    // MARK: - Video Visibility
    func notifyVideoSeen() {
        // Send signal to remote peer that we see them
        guard let callId = callState.callId, let remoteUser = callState.remoteUser, let myUser = currentUser else { return }
        
        // Prevent spamming
        // In a real app we might want to allow this multiple times if video cuts out, but for now once per call is fine
        // However, this method might be called multiple times by VideoView if resolution changes.
        // We can debounce or just send it. Let's send it, it's cheap.
        
        let msg = SignalingMessage(type: "VIDEO_VISIBLE", callId: callId, from: myUser.username, to: remoteUser, payload: nil)
        signaling.send(msg)
    }
    
    // MARK: - Signaling Handling
    private func handleSignalingMessage(_ msg: SignalingMessage) {
        switch msg.type {
        case "REGISTER_SUCCESS":
            break // Already handled optimistically
            
        case "PRESENCE_UPDATE":
             if let payload = msg.payload?.value as? [[String: Any]] {
                 self.availableInterpreters = payload.compactMap { $0["username"] as? String }
             }
             
        case "STATS_UPDATE":
             if let payload = msg.payload?.value as? [String: Any],
                let numInterpreters = payload["interpreters"] as? Int,
                let numCallers = payload["callers"] as? Int {
                 DispatchQueue.main.async {
                     self.onlineInterpretersCount = numInterpreters
                     self.onlineCallersCount = numCallers
                 }
             }
        // Interpreter Actions
        case "PRESENCE_CHANGED":
            if let payload = msg.payload?.value as? [String: Any],
               let stateStr = payload["state"] as? String,
               let state = PresenceState(rawValue: stateStr) {
                self.interpreterState = state
            }
            
        case "CALL_REQUEST":
            guard currentUser?.role == .interpreter else { return }
            if let callId = msg.callId, let from = msg.from {
                self.callState = CallState(callId: callId, remoteUser: from, status: .incoming)
            }
            
        // Caller Actions
        case "CALL_ACCEPT":
            if let callId = msg.callId {
                self.callState.callId = callId
                self.callState.status = .inCall
                
                // Extract TURN config if present
                if let payload = msg.payload?.value as? [String: Any],
                   let turn = payload["turnServer"] as? [String: Any] {
                   // Configure WebRTC with these credentials
                     let _ = turn // Placeholder usage
                }
                
                startWebRTC(isCaller: true)
            }
            
        case "CALL_DECLINE":
            resetCallState()
            errorMessage = "Call declined. Please try another available interpreter."
            
        case "BUSY", "TIMEOUT":
            resetCallState()
            errorMessage = "\(msg.type): \(msg.payload?.value ?? "")"
            
        case "VIDEO_VISIBLE":
             // Remote peer sees us!
             DispatchQueue.main.async {
                 self.remotePeerSeesMe = true
                 self.showVideoVisibleToast = true
                 
                 // Hide toast after 3 seconds
                 DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                     self.showVideoVisibleToast = false
                 }
             }
             
        case "HANGUP":
            endCall()
            
        // WebRTC Signaling
        case "OFFER":
            if let sdpStr = msg.payload?.value as? String {
                let sdp = RTCSessionDescription(type: .offer, sdp: sdpStr)
                webRTC.setRemoteDescription(sdp: sdp) { error in
                    if error == nil {
                        self.webRTC.createAnswer { answerSdp in
                            self.webRTC.setLocalDescription(sdp: answerSdp) { _ in
                                self.sendSDP(answerSdp, type: "ANSWER")
                            }
                        }
                    }
                }
            }
            
        case "ANSWER":
             if let sdpStr = msg.payload?.value as? String {
                let sdp = RTCSessionDescription(type: .answer, sdp: sdpStr)
                webRTC.setRemoteDescription(sdp: sdp) { _ in }
             }
             
        case "ICE":
             if let candidateDict = msg.payload?.value as? [String: Any],
                let sdp = candidateDict["sdp"] as? String,
                let sdpMLineIndexInt = candidateDict["sdpMLineIndex"] as? Int {
                 
                 let sdpMLineIndex = Int32(sdpMLineIndexInt)
                 let sdpMid = candidateDict["sdpMid"] as? String ?? "" // sdpMid is optional sometimes, good to safely unwrap or default
                 
                 let candidate = RTCIceCandidate(sdp: sdp, sdpMLineIndex: sdpMLineIndex, sdpMid: sdpMid)
                 webRTC.addCandidate(candidate)
             }
            
        default:
            print("Unhandled message: \(msg.type)")
        }
    }
    
    // MARK: - WebRTC Orchestration
    private func startWebRTC(isCaller: Bool) {
        // In production, pass TURN creds here
        webRTC.setup(turnServers: ["turn:turn.jaredlog.com:3478"], username: "user", credential: "password")
        
        if isCaller {
            webRTC.createOffer { sdp in
                self.webRTC.setLocalDescription(sdp: sdp) { error in
                    if error == nil {
                        self.sendSDP(sdp, type: "OFFER")
                    }
                }
            }
        }
    }
    
    private func sendSDP(_ sdp: RTCSessionDescription, type: String) {
        guard let callId = callState.callId, let remoteUser = callState.remoteUser, let myUser = currentUser else { return }
        let msg = SignalingMessage(type: type, callId: callId, from: myUser.username, to: remoteUser, payload: AnyCodable(sdp.sdp))
        signaling.send(msg)
    }
}

// MARK: - WebRTC Delegate
extension CallViewModel: WebRTCClientDelegate {
    func webRTCClient(_ client: WebRTCService, didDiscoverLocalCandidate candidate: RTCIceCandidate) {
        guard let callId = callState.callId, let remoteUser = callState.remoteUser, let myUser = currentUser else { return }
        
        let candidateDict: [String: Any] = [
            "sdp": candidate.sdp,
            "sdpMLineIndex": candidate.sdpMLineIndex,
            "sdpMid": candidate.sdpMid ?? ""
        ]
        
        let msg = SignalingMessage(type: "ICE", callId: callId, from: myUser.username, to: remoteUser, payload: AnyCodable(candidateDict))
        signaling.send(msg)
    }
    
    func webRTCClient(_ client: WebRTCService, didChangeConnectionState state: RTCIceConnectionState) {
        // Handle connection state changes (connecting, connected, disconnected, failed)
    }
    
    func webRTCClient(_ client: WebRTCService, didReceiveData data: Data) {
        // Handle data channel
    }
    
    func webRTCClient(_ client: WebRTCService, didReceiveRemoteVideoTrack rtcVideoTrack: RTCVideoTrack) {
        DispatchQueue.main.async {
            self.remoteVideoTrack = rtcVideoTrack
        }
    }
    
    func webRTCClient(_ client: WebRTCService, didCreateLocalVideoTrack rtcVideoTrack: RTCVideoTrack) {
        DispatchQueue.main.async {
            self.localVideoTrack = rtcVideoTrack
        }
    }
}
