
import UIKit
import Toast_Swift
import JGProgressHUD
import ContactsUI
import Async
import OneSignalFramework
import OneSignalUser
import WowonderMessengerSDK
import Photos
import GoogleMobileAds
import CommonCrypto
import SwiftUI

struct FetchedContact {
    var name: String
    var phoneNumber: String
}

class BaseVC: UIViewController {
    
    let tempMsg = ["Hi","Hello", "Hello there can we talk?","How you doing", "You there?"]
    var hud: JGProgressHUD?
    var userId: String? = nil
    var sessionId: String? = nil
    var contacts: [FetchedContact] = []
    
    // For imagePicker
    var selectedAssets = [PHAsset]()
    var photoArray = [UIImage]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.dismissKeyboard()
    }
    
    func generateThumbnail(from videoURL: URL, completion: @escaping (UIImage?) -> Void) {
        let asset = AVAsset(url: videoURL)
        let assetImageGenerator = AVAssetImageGenerator(asset: asset)
        assetImageGenerator.appliesPreferredTrackTransform = true
        let time = CMTime(seconds: 1, preferredTimescale: 1) // Get thumbnail at 1 second into the video
        do {
            let thumbnailCGImage = try assetImageGenerator.copyCGImage(at: time, actualTime: nil)
            let thumbnailImage = UIImage(cgImage: thumbnailCGImage)
            completion(thumbnailImage)
        } catch {
            print("Error generating thumbnail: \(error)")
            completion(nil)
        }
    }
    
    // Set Reaction Emoji
    func setReactionEmoji(type: String) -> UIImage? {
        switch type {
        case "1":
            return UIImage(named: "emoji_like")
        case "2":
            return UIImage(named: "emoji_love")
        case "3":
            return UIImage(named: "emoji_haha")
        case "4":
            return UIImage(named: "emoji_wow")
        case "5":
            return UIImage(named: "emoji_sad")
        case "6":
            return UIImage(named: "emoji_angry")
        default:
            return nil
        }
    }
    
    func setCallLog(id: String?, user_id: String?, avatar: String?, name: String?, from_id: String?, active: String?, time: String?, status: String?, room_name: String?, call_type: String?, type: String?) {
        let callLog = CallLogModel(id: id, user_id: user_id, avatar: avatar, name: name, from_id: from_id, active: active, time: time, status: status, room_name: room_name, call_type: call_type, type: type)
        let objectToEncode = callLog
        let data = try? PropertyListEncoder().encode(objectToEncode)
        var getCallLogsData = UserDefaults.standard.getCallLogs(Key: Local.CALL_LOGS.CallLogs)
        getCallLogsData.append(data!)
        UserDefaults.standard.setCallLogs(value: getCallLogsData, ForKey: Local.CALL_LOGS.CallLogs)
    }
    
}

// MARK: - CUSTOM FUNCTIONS

extension BaseVC {
    
    func decryptionAESModeECB(messageData: String, key: String) -> String? {
        let dataKey = Data(messageData.utf8)
        guard let messageString = String(data: dataKey, encoding: .utf8) else { return nil }
        guard let data = Data(base64Encoded: messageString, options: .ignoreUnknownCharacters) else { return nil }
        guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
        guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
        let keyLength = size_t(kCCKeySizeAES128)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
        let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
        let iv: String = ""
        var numBytesEncrypted: size_t = 0
        let cryptStatus = CCCrypt(operation, algoritm, options, (keyData as NSData).bytes, keyLength, iv, (data as NSData).bytes, data.count, cryptData.mutableBytes, cryptData.length, &numBytesEncrypted)
        if cryptStatus < 0 {
            return nil
        } else {
            if UInt32(cryptStatus) == UInt32(kCCSuccess) {
                cryptData.length = Int(numBytesEncrypted)
                var str = String(decoding : cryptData as Data, as: UTF8.self)
                if str.isEmpty {
                    return messageData
                } else {
                    str = str.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)
                    return str
                }
            } else {
                return messageData
            }
        }
    }
    
    func decryptMessage(message: String, time: String) -> String? {
        guard let encryptedData = Data(base64Encoded: message) else {
            print("Invalid base64 input")
            return nil
        }
        let keyData = time.data(using: .utf8)!
        let blockSize = kCCBlockSizeAES128
        let keyLength = kCCKeySizeAES128
        let dataLength = encryptedData.count
        var decryptedData = Data(count: dataLength + blockSize)
        var numBytesDecrypted = 0
        let cryptStatus = keyData.withUnsafeBytes { keyBytes in
            encryptedData.withUnsafeBytes { encryptedBytes in
                decryptedData.withUnsafeMutableBytes { decryptedBytes in
                    CCCrypt(
                        CCOperation(kCCDecrypt),
                        CCAlgorithm(kCCAlgorithmAES),
                        CCOptions(kCCOptionECBMode + kCCOptionPKCS7Padding),
                        keyBytes.baseAddress, keyLength,
                        nil,
                        encryptedBytes.baseAddress, dataLength,
                        decryptedBytes.baseAddress, dataLength + blockSize,
                        &numBytesDecrypted
                    )
                }
            }
        }
        guard cryptStatus == kCCSuccess else {
            print("Decryption failed with status \(cryptStatus)")
            return nil
        }
        decryptedData.removeSubrange(numBytesDecrypted..<decryptedData.count)
        print("Decrypted bytes: \(decryptedData as NSData)")
        if let decryptedString = String(data: decryptedData, encoding: .utf8) {
            print("Decrypted certkey: \(decryptedString)")
            return decryptedString
        } else {
            print("Unable to decode decrypted data to UTF-8 string")
            return nil
        }
    }
    
    func showProgressDialog(text: String) {
        hud = JGProgressHUD(style: .dark)
        hud?.textLabel.text = text
        hud?.show(in: self.view)
    }
    
    func dismissProgressDialog(completionBlock: @escaping () ->()) {
        hud?.dismiss()
        completionBlock()
    }
    
    func fetchContacts(completion: @escaping () ->()) {
        print("Attempting to fetch contacts")
        self.contacts = []
        let contactStore = CNContactStore()
        contactStore.requestAccess(for: .contacts) { granted, error in
            if let error = error {
                print(error.localizedDescription)
                completion()
            } else {
                if granted {
                    print("access granted")
                    let keys = [
                        CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
                        CNContactPhoneNumbersKey
                    ] as [Any]
                    let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
                    do {
                        try contactStore.enumerateContacts(with: request) { contact, stop in
                            for phoneNumber in contact.phoneNumbers {
                                if let fullName = CNContactFormatter.string(from: contact, style: .fullName) {
                                    let fetchedContact = FetchedContact(name: fullName, phoneNumber: phoneNumber.value.stringValue)
                                    self.contacts.append(fetchedContact)
                                }
                            }
                        }
                        completion()
                    } catch {
                        print(error.localizedDescription)
                        completion()
                    }
                } else {
                    print("access denied")
                    completion()
                }
            }
        }
    }
    
}

extension BaseVC {
    
    func htmlToString(text: String) -> [String: Any]? {
        if let decodedString = try? NSAttributedString(data: Data(text.utf8), options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil).string {
            if let jsonData = decodedString.data(using: .utf8) {
                do {
                    if let dictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
                        print(dictionary)
                        return dictionary
                    }
                } catch {
                    print("Error decoding JSON: \(error.localizedDescription)")
                }
            }
        }
        return nil
    }
    
}

// MARK: - Extension to show alert
extension UIViewController {
    
    func showBasicAlert(title : String?, message : String) {
        let alertController: UIAlertController = UIAlertController(title: title == nil ? "Title" : title!, message: message, preferredStyle: .alert)
        let actionOk = UIAlertAction(title: "OK", style: .default, handler: { action in
            alertController .dismiss(animated: true, completion: nil)
        })
        alertController.addAction(actionOk)
        self.present(alertController, animated: true, completion: nil)
    }
    
}

func isValidURL(_ urlString: String) -> Bool {
    if let url = URL(string: urlString) {
        return UIApplication.shared.canOpenURL(url)
    }
    return false
}

func decryptionAESModeECB(text: String, key: String) -> [String: Any]? {
    let dataKey = Data(text.utf8)
    guard let messageString = String(data: dataKey, encoding: .utf8) else { return nil }
    guard let data = Data(base64Encoded: messageString, options: .ignoreUnknownCharacters) else { return nil }
    guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
    guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
    let keyLength = size_t(kCCKeySizeAES128)
    let operation: CCOperation = UInt32(kCCDecrypt)
    let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
    let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
    let iv: String = ""
    var numBytesEncrypted: size_t = 0
    let cryptStatus = CCCrypt(operation, algoritm, options, (keyData as NSData).bytes, keyLength, iv, (data as NSData).bytes, data.count, cryptData.mutableBytes, cryptData.length, &numBytesEncrypted)
    if cryptStatus < 0 {
        return nil
    } else {
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            cryptData.length = Int(numBytesEncrypted)
            let finalData = try! JSONSerialization.jsonObject(with: cryptData as Data, options: []) as? [String: Any]
            if let finalData = finalData  {
                return finalData
            } else {
                return nil
            }
        } else {
            return nil
        }
    }
}
