//
//  SocketIOHelper.swift
//  WoWonder
//
//  Created by Mac on 20/10/22.
//  Copyright © 2022 ScriptSun. All rights reserved.
//

import Foundation
import SocketIO
import WowonderMessengerSDK
import UIKit

class SocketHelper {
    
    static let shared = SocketHelper()
    private let manager: SocketManager
    var socket: SocketIOClient
    private var lastSeenTimer: Timer?
    
    private init() {
        guard let url = URL(string: "\(API.baseURL):449") else {
            fatalError("Invalid socket URL")
        }
        manager = SocketManager(socketURL: url, config: [
            .secure(true),
            .selfSigned(false),
            .forceWebsockets(true),
            .forceNew(true),
            .path("/socket.io"),
            .version(.three),
            .reconnects(true),
            .reconnectAttempts(Int.max),
            .reconnectWait(1000),
            .reconnectWaitMax(5000),
            .randomizationFactor(0.5),
            .log(true)
        ])
        socket = manager.defaultSocket
        print(manager.socketURL.description)
        setupSocketEvents()
    }
    
    private func setupSocketEvents() {
        
        //self.socket.disconnect()
        
        self.socket.on(clientEvent: .connect) { data, ack in
            print("Socket connected")
            // Handle your logic on socket connection
            if AppInstance.instance.sessionId != "" && AppInstance.instance.sessionId != nil {
                self.emit_join(username: AppInstance.instance.userProfile?.username ?? "", user_id: AppInstance.instance._sessionId)
            }
        }
        
        // Handle status change event
        self.socket.on(clientEvent: .statusChange) { data, _ in
            if self.socket.status == .connected {
                print("Socket Status >>>>> connected")
            }
            if self.socket.status == .connecting {
                print("Socket Status >>>>> connecting")
            }
            if self.socket.status == .notConnected || self.socket.status == .disconnected {
                self.connect()
            }
        }
        
        self.socket.on(clientEvent: .disconnect) { data, _ in
            print("Disconnect")
            self.connect()
        }
        
        self.socket.on(clientEvent: .reconnect) { _, _ in
            print("Reconnected")
            // Handle reconnect event
        }
        
        self.socket.on(clientEvent: .reconnectAttempt) { _, _ in
            print("Reconnect attempt")
            // Handle reconnect attempt event
        }
        
        self.socket.on(clientEvent: .websocketUpgrade) { _, _ in
            print("Websocket upgraded")
            // Handle websocket upgrade event
        }
        
        self.socket.on(clientEvent: .error) { data, ack in
            print("error >>>>>", data)
            // Handle your logic on socket connection
        }
        
        /*self.socket.onAny { event in
            print("event name >>>>> ", event.event)
        }*/
        
        self.connect()
    }
    
    func connect() {
        if self.socket.status != .connected {
            self.socket.connect()
        }
    }
    
    func disconnect() {
        self.socket.off("join")
        self.socket.off("private_message")
        self.socket.off("page_message")
        self.socket.off("group_message")
        self.socket.off("seen_messages")
        self.socket.off("lastseen")
        self.socket.off("user_status_change")
        self.socket.off("recording")
        self.socket.off("typing")
        self.socket.off("typing_done")
        self.socket.off("new_video_call")
        self.socket.off("user_notification")
        self.socket.off("on_user_loggedin")
        self.socket.off("on_user_loggedoff")
        self.socket.off("ping_for_lastseen")
        self.socket.off("register_reaction")
        self.socket.off("group_message_page")
        self.socket.removeAllHandlers()
        self.socket.disconnect()
        self.lastSeenTimer?.invalidate()
        self.lastSeenTimer = nil
        print("socket Disconnected")
    }
    
    func emit(event: String, data: [String: String], completion: (() -> ())? = nil) {
        if self.socket.status == .connected {
            self.socket.emit(event, data, completion: completion)
        } else {
            self.connect()
        }
    }
    
    func responseSocket(event: String, completion: @escaping([Any]) -> ()) {
        if self.socket.status == .connected {
            self.socket.on(event) { data, emitter in
                completion(data)
            }
        } else {
            self.connect()
        }
    }
    
}

// MARK: - Extensions

// MARK: Emit Event
extension SocketHelper {
    
    func emit_join(username: String, user_id: String) {
        let data = [
            "username": username,
            "user_id" : user_id
        ]
        self.emit(event: "join", data: data) {
            self.lastSeenTimer?.invalidate()
            self.lastSeenTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { _ in
                self.emit_ping_for_lastseen(user_id: user_id)
            }
            self.emit_on_user_loggedin(from_id: user_id)
        }
    }
    
    func emit_recording(recipient_id: String, user_id: String) {
        let data = [
            "recipient_id": recipient_id,
            "user_id": user_id
        ]
        self.emit(event: "recording", data: data)
    }
    
    func emit_typing(recipient_id: String, user_id: String) {
        let data = [
            "recipient_id": recipient_id,
            "user_id": user_id
        ]
        self.emit(event: "typing", data: data)
    }
    
    func emit_typing_done(recipient_id: String, user_id: String) {
        let data = [
            "recipient_id": recipient_id,
            "user_id": user_id
        ]
        self.emit(event: "typing_done", data: data)
    }
    
    func emit_seen_messages(recipient_id: String, user_id: String, current_user_id: String) {
        let data = [
            "recipient_id": recipient_id,
            "user_id": user_id,
            "current_user_id": current_user_id
        ]
        self.emit(event: "seen_messages", data: data)
    }
    
    func emit_private_message(to_id: String, from_id: String, username: String, msg: String, color: String, message_reply_id: String, story_id: String, lat: String, lng: String, completion: (() -> ())? = nil) {
        let data = [
            "to_id": to_id,
            "from_id": from_id,
            "username": username,
            "msg": msg,
            "color": color,
            "message_reply_id": message_reply_id,
            "story_id": story_id,
            "lat": lat,
            "lng": lng,
            "isSticker": "false"
        ]
        if self.socket.status == .connected {
            self.socket.emit("private_message", with: [data], completion: completion)
        } else {
            self.connect()
        }
    }
    
    func emit_page_message(page_id: String, to_id: String, from_id: String, username: String, msg: String, message_reply_id: String) {
        let data = [
            "page_id": page_id,
            "to_id": to_id,
            "from_id": from_id,
            "username": username,
            "msg": msg,
            "message_reply_id": message_reply_id,
            "isSticker": "false"
        ]
        self.emit(event: "page_message", data: data)
    }
    
    func emit_group_message(groupId: String, accessToken: String, username: String, msg: String, messageReplyId: String, completion: (() -> ())? = nil) {
        let data = [
            "group_id": groupId,
            "from_id": accessToken,
            "username": username,
            "msg": msg,
            "message_reply_id": messageReplyId,
            "isSticker": "false"
        ]
        if self.socket.status == .connected {
            self.socket.emit("group_message", with: [data], completion: completion)
        } else {
            self.connect()
        }
    }
    
    func emit_user_notification(recipient_id: String) {
        let data = [
            "to_id": recipient_id,
            "type": "create_video"
        ]
        self.emit(event: "user_notification", data: data)
    }
    
    func emit_register_reaction(id: String, reaction: String, user_id: String, completion: (() -> ())? = nil) {
        let data = [
            "type": "messages",
            "id": id,
            "reaction": reaction,
            "user_id": user_id,
        ]
        self.emit(event: "register_reaction", data: data, completion: completion)
    }
    
    func emit_on_user_loggedin(from_id: String) {
        let data = [
            "from_id": from_id
        ]
        self.emit(event: "on_user_loggedin", data: data)
    }
    
    func emit_on_user_loggedoff(from_id: String) {
        let data = [
            "from_id": from_id
        ]
        self.emit(event: "on_user_loggedoff", data: data)
    }
    
    func emit_ping_for_lastseen(user_id: String) {
        let data = [
            "user_id": user_id
        ]
        self.emit(event: "ping_for_lastseen", data: data)
    }
    
}

// MARK: On Event
extension SocketHelper {
    
    func on_private_message(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "private_message", completion: completion)
    }
    
    func on_private_message_page(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "private_message_page", completion: completion)
    }
    
    func on_page_message(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "page_message", completion: completion)
    }
    
    func on_group_message(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "group_message", completion: completion)
    }
    
    func on_user_status_change(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "user_status_change", completion: completion)
    }
    
    func on_recording(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "recording", completion: completion)
    }
    
    func on_typing(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "typing", completion: completion)
    }
    
    func on_typing_done(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "typing_done", completion: completion)
    }
    
    func on_seen_messages(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "seen_messages", completion: completion)
    }
    
    func on_lastseen(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "lastseen", completion: completion)
    }
    
    func on_new_video_call(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "new_video_call", completion: completion)
    }
    
    func on_on_user_loggedin(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "on_user_loggedin", completion: completion)
    }
    
    func on_on_user_loggedoff(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "on_user_loggedoff", completion: completion)
    }
    
    func on_register_reaction(completion: @escaping([Any]) -> ()) {
        self.responseSocket(event: "register_reaction", completion: completion)
    }
    
}
