//
//  BrizziSamHelper.swift
//  Emoney Info
//
//  Created by Wira Irawan on 27/07/24.
//

import Foundation
import CommonCrypto

class BrizziSamHelper {
    public var encryptedKey: String?
    public var authKey = "0000030080000000"
    public var keyCard: String?
    public var random = ""

    static func encryptDeSeDe(_ str: String, _ str2: String, _ str3: String) -> Data? {
        var key = str2
        if key.count != 48 {
            if key.count == 32 {
                key += key.prefix(16)
            } else if key.count == 16 {
                key += key + key
            } else {
                key = "00000000000000000000000000000000"
            }
        }
        let keyData = key.hex2byte()
        let ivData = str3.hex2byte()
        
        return crypt(input: str.hex2byte(), keyData: keyData, ivData: ivData, operation: CCOperation(kCCEncrypt))
    }

    static func decryptDeSeDe(_ datas: Data) -> Data? {
        let keyData = ("C152153D5807784C721A433B5B59636D" + "C152153D5807784C").hex2byte()
        let ivData = ("0000000000000000").hex2byte()
        
        return crypt(input: datas, keyData: keyData, ivData: ivData, operation: CCOperation(kCCDecrypt))
    }

    static func mix(_ bArr: [UInt8], _ bArr2: [UInt8]) -> [UInt8] {
        guard !bArr2.isEmpty else {
            return bArr
        }
        
        var bArr3 = [UInt8](repeating: 0, count: bArr.count)
        var i = 0
        for y in 0..<bArr.count {
            bArr3[y] = bArr[y] ^ bArr2[i]
            i += 1
            if i >= bArr2.count {
                i = 0
            }
        }
        return bArr3
    }

    static func decrypt(_ data: String, _ key: String) -> Data? {
        let keyData = key.hex2byte()
        
        return crypt(input: data.hex2byte(), keyData: keyData, ivData: nil, operation: CCOperation(kCCDecrypt))
    }

    static func encrypt(_ str: String, _ key: String) -> Data? {
        let substring = String(key.prefix(16))
        guard let decryptedData = decrypt(str, substring) else {
            return ("").hex2byte()
        }
        
        let a9 = decryptedData.hexEncodedString()
        let keyData = String(key.dropFirst(16).prefix(16)).hex2byte()
        
        guard let encryptedData = crypt(input: a9.hex2byte(), keyData: keyData, ivData: nil, operation: CCOperation(kCCEncrypt)) else {
            return ("").hex2byte()
        }
        
        guard let finalDecryptedData = decrypt(encryptedData.hexEncodedString(), substring) else {
            return ("").hex2byte()
        }
        
        return finalDecryptedData
    }

    func generateSamRandom() -> String {
        guard
            let keyCard = self.keyCard,
            let encryptedCard = BrizziSamHelper.encrypt(keyCard, self.random)?.hexEncodedString()
        else {
            return ""
        }
        let sam = BrizziSamHelper.mix(encryptedCard.hex2byte().bytes, ("0000000000000000").hex2byte().bytes).hexString().subString(from: 0, to: 16)
        guard sam.count == 16 else {
            return ""
        }
        let sams = sam[sam.index(sam.startIndex, offsetBy: 2)..<sam.index(sam.startIndex, offsetBy: 16)] + sam[sam.startIndex..<sam.index(sam.startIndex, offsetBy: 2)]
        guard let encryptedResult = BrizziSamHelper.encrypt((BrizziSamHelper.mix(("1122334455667788").hex2byte().bytes, keyCard.hex2byte().bytes).hexString()), self.random)?.hexEncodedString() else {
            return ""
        }
        let result = encryptedResult.subString(from: 0, to: 16)
        
        guard let finalResult = BrizziSamHelper.encrypt((BrizziSamHelper.mix(String(sams).hex2byte().bytes, result.hex2byte().bytes)).hexString(), self.random)?.hexEncodedString() else {
            return result
        }
        return result + finalResult
    }

    private static func crypt(input: Data, keyData: Data, ivData: Data?, operation: CCOperation) -> Data? {
        var outLength = Int(0)
        var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSize3DES)
        var status: CCCryptorStatus
        
        if let ivData = ivData {
            status = CCCrypt(operation, CCAlgorithm(kCCAlgorithm3DES), CCOptions(kCCOptionPKCS7Padding), keyData.bytes, kCCKeySize3DES, ivData.bytes, input.bytes, input.count, &outBytes, outBytes.count, &outLength)
        } else {
            status = CCCrypt(operation, CCAlgorithm(kCCAlgorithmDES), CCOptions(kCCOptionPKCS7Padding), keyData.bytes, kCCKeySizeDES, nil, input.bytes, input.count, &outBytes, outBytes.count, &outLength)
        }
        
        guard status == kCCSuccess else {
            return nil
        }
        
        return Data(bytes: outBytes, count: outLength)
    }
}
