//
//  API.swift
//  MyTatvaCare
//
//  Created by Nirav Ramani on 16/05/25.
//

import Foundation
import CommonCrypto

let PROD_API_KEY = "lChjFRJce3bxmoS3TSQk5w=="
let STAGE_API_KEY = "lChjFRJce3bxmoS3TSQk5w=="

let PROD_BASE_URL = "https://api.mytatva.in/api/v8"
let STAGE_BASE_URL = "https://api-uat.mytatva.in/api/v8"


let PROD_ENC_KEY = "9Ddyaf6rfywpiTvTiax2iq6ykKpaxgJ6"
let STAGE_ENC_KEY = "9Ddyaf6rfywpiTvTiax2iq6ykKpaxgJ6"

let PROD_ENC_IV = "9Ddyaf6rfywpiTvT"
let STAGE_ENC_IV = "9Ddyaf6rfywpiTvT"


let defaultToken = "ILtFcw+xtbuy8IgsBCSyD6nSpgZd5AOz7T+g3N8Tef/INZi+dxwPJhnBc2kfdq2e8Kw2yayjaKjuji64coUJsK7e4QcF7sqXCp3Cy5S2OVE+hONwCipkrn9d1yjsBWIDqfLK9ModJCWdigDj5ZeSwkWYSSeTEVoN3Dc2Y8cuqKUQvN9ZcNGmdRrXz2oo3rm4EHQrXPo1Ijufm0HfcqzSH/Gh0TC5gFKjde/GEXIm5Z2Ju1/TTXVSJg/zxNuOF4iUKNEh4UIFUYxYpnqlALRCdF75J+9WAZ7/LBGyB1Wrx+D7bsrzRiQ5sCTwxE3TCM3s66lYJizX22VIzYKOIrJ6GTOYGTINHeIJKEPEvFceR+cG54ez15V1r77ErQElevItD3xIGe0uDezfnixJYPxPhSEOPOyIpxUFInXBRdGjllHPIevzBqqHeP76nz+1U7mFNs1kofugN+5huT3/tCifTQqFyWuxD+VIhl0UJxhxPMbBgoryANgKCToWrK2FqdJwBtZKwCj3S+yp0rff0fwrbMXXsYytJyVLLY2oNviHtzDa/kih0AnMA3vEbqYkf1PQ6LZv+AmtpNYHKh7kMDrhVE3wVxH4+ga0+sfuxYgfiZXCoQW7yY0wOZTiFbgvL2TQflDZR6ttK5abtyKYV4gl5KqbFLZW+d4pKLQoZb02NbJc+5h5gAH2VclTJc6fbkd0qIWuXyqGIBEc2uYrNl2v3vmVvbcGevSqb900ozo6SbFccQLjLcEon+okmgBIDPvBEZFj8fZgQV4z8/yzayDcfqZQPc9Wrw8JNrFLCd8emvo7QHFfGttnIsAtmR8Yp3x49sjEn3hFW6nEKl9FRtRT1r3LzHmIdokL5tz3nO/T3FzLn5sqfp5QBJKKK2dgJdmAz0KC6vKtJ0PtVR4shL7pqjoZuJimFObYKyQbQudW7gLH8WjqORNG2dsplOLzDQ0y7nQdQ0tWrLJtnAch0p5PyTXtunlkNWeuzrldkqVMTUPaAtGcEnmS4RFncD3C6sq9V6lpBKLdrh99vnH3EX81DTH2Hrqm0bN5Tl3UqzUJ7n/JQCR3phDmOo+Uk81y3z3ikiGZZpZikN7c3DtSP34Vc+cNUosWrChqj1gLL4Eg/jMDxE4+bdAGr03ALxEAj2uLGEsDaZRuio6e8/iMcA0z3oqhtAxQ0nU1ivAK7cHeddjRFtvrNb0l+TgrcQI9UVffMOoGDEfAQNUpT2+R5dIysw=="
let TOKEN = UserDefaults.standard.string(forKey: "authToken") ?? defaultToken
let envType = UserDefaults.standard.string(forKey: "envType") ?? "uat"

/*TatvaEncryptionConfig {
 const val STAGE_BASE_URL = "https://api-feature2.mytatva.in/api/v8"
 const val PROD_BASE_URL = "https://api.mytatva.in/api/v8"
 
 const val PROD_ENC_KEY = "9Ddyaf6rfywpiTvTiax2iq6ykKpaxgJ6"
 const val STAGE_ENC_KEY = "9Ddyaf6rfywpiTvTiax2iq6ykKpaxgJ6"
 
 const val PROD_ENC_IV = "9Ddyaf6rfywpiTvT"
 const val STAGE_ENC_IV = "9Ddyaf6rfywpiTvT"
 
 const val PROD_API_KEY = "lChjFRJce3bxmoS3TSQk5w=="
 const val STAGE_API_KEY = "lChjFRJce3bxmoS3TSQk5w=="
 }*/

struct CGMLog: Codable {
    let timeInMillis: Double
    let countdownMinutes: Int
    let countdownDays: Int
    let hypoglycemiaEarlyWarnMinutes: Int
    let showGlucoseMG: Int
    let glucoseId: Int
    let name: String
    let bytes: [Int]
    let showGlucose: Float
    let Ib: Float
    let Iw: Float
    let countdownHours: Int
    let T: Float
    let year: Int32
    let month: Int32
    let day: Int32
    let hour: Int32
    let minute: Int32
    let trendObject: TrendObject
    let glucoseStatusObject: GlucoseStatusObject
    let errorObject: ErrorObject
}

struct TrendObject: Codable {
    let trendId: Int
    let drawableId: Int
    let widgetImg: Int
    let apsChangeRate: String
}

struct GlucoseStatusObject: Codable {
    let statusId: Int
}

struct ErrorObject: Codable {
    let errorId: Int
    let sound: String
}

struct Payload: Codable {
    var vendor = "GoodFlip"
    let logs: [CGMLog]
}

enum TatvaEnvironment {
    case prod
    case stage
}

class API {
    
    static let shared = API()
    
    private init() {}
    
    func postCGMData(
        data: Payload,
        environment: TatvaEnvironment,
        token: String = TOKEN,
        encryptionKey: String = PROD_ENC_KEY,
        encryptionIv: String = PROD_ENC_IV,
        onSuccess: @escaping () -> Void,
        onFailure: @escaping (Error?) -> Void
    ) {// environment: TatvaEnvironment,
        
        print("ios token:", token)
        Task {
            do {
                let jsonData = try JSONEncoder().encode(data)
                
                // Convert to String
                guard let jsonString = String(data: jsonData, encoding: .utf8) else { return }
                print("===>JSON Payload String:\n\(jsonString)")
                
                print("===>Raw JSON: \(data)")
                
                // Attach sensorId at top level for /cgm/logs, using same behavior as Android
                let activeSensorId = UserDefaults.standard.string(forKey: "sensorId") ?? ""
                var finalJsonString = jsonString
                if !activeSensorId.isEmpty,
                   let baseObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
                    var updatedObject = baseObject
                    updatedObject["sensorId"] = activeSensorId
                    if let updatedData = try? JSONSerialization.data(withJSONObject: updatedObject, options: []),
                       let updatedString = String(data: updatedData, encoding: .utf8) {
                        finalJsonString = updatedString
                    }
                }
                
                guard let encrypted = Crypto.shared.getEncryptedText(data: finalJsonString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) else { return }
                print("===>Encrypted: \(encrypted)")
                
                if let decrypted = Crypto.shared.getDecryptedData(cipherText: encrypted, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
                    print("===>Decrypted (for verification): \(decrypted)")
                }
                
                let urlString = (envType.lowercased() == "uat" ?  STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/logs"
              //  let urlString = (environment == .stage ?  STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/logs"
                guard let url = URL(string: urlString) else {
                    throw URLError(.badURL)
                }
                
                var request = URLRequest(url: url)
                request.httpMethod = "POST"
                request.timeoutInterval = 30
                request.setValue(envType.lowercased() == "uat" ?  STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
               // request.setValue(environment == .stage ?  STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
                request.setValue(token, forHTTPHeaderField: "token")
                request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
                request.httpBody = encrypted.data(using: .utf8)
                print("===>url:", urlString)
                
                // Use regular URLSession for foreground/background compatibility
                // Background URLSession requires delegates, not completion handlers
                let session = URLSession.shared
                
                let task = session.uploadTask(with: request, from: encrypted.data(using: .utf8)) { data, response, error in
                    if let error = error {
                        print("===>Upload failed: \(error.localizedDescription)")
                        DispatchQueue.main.async {
                            onFailure(error)
                        }
                        return
                    }
                    
                    if let httpResponse = response as? HTTPURLResponse {
                        print("HTTP Status: \(httpResponse.statusCode)")
                    }
                    
                    if let data = data, let responseStr = String(data: data, encoding: .utf8) {
                        print("===>Server Response: \(responseStr)")
                        if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseStr, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
                            print("===>Decrypted post cgm data response (for verification): \(decrypted)")
                        }
                    }
                    
                    DispatchQueue.main.async {
                        onSuccess()
                    }
                }
                
                task.resume()
                print("===>Upload task started")
                
            } catch {
                print("===>Request failed: \(error.localizedDescription)")
                DispatchQueue.main.async {
                    onFailure(error)
                }
            }
        }
    }
    //these 2 events will give glucose data
    //addCT3GlucoseDataWithReceiveData
    //updatePOCAlgoBeforeRef
    func sendStatus(sensorId: String = UserDefaults.standard.string(forKey: "sensorId") ?? "",
                    environment: TatvaEnvironment,
                    status: CGMConnectionStatus,
                    transmitterName: String = KLTBluetoothManager.shared().currentDevice?.advertise?.localName ?? "",
                    encryptionKey: String = PROD_ENC_KEY,
                    encryptionIv: String = PROD_ENC_IV) {
        if sensorId.isEmpty { return }
       // let envType = UserDefaults.standard.string(forKey: "envType") ?? "uat"
        print("envType: \(envType.lowercased())")
        let urlString = (envType.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/connection"
        //let urlString = (environment == .stage ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/connection"
        let url = URL(string: urlString)!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.timeoutInterval = 30
        
        // Set headers
        request.addValue("text/plain", forHTTPHeaderField: "Content-Type")
        request.setValue(envType.lowercased() == "uat" ?  STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
        //request.setValue(environment == .stage ?  STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
        request.addValue(TOKEN, forHTTPHeaderField: "token")
        print("===>url:", urlString)
        print("===>api-key:", PROD_API_KEY)
        print("===>httpmMethod:","POST")
        print("===>ios token:", TOKEN)
        // Set request body
        let json: [String: Any] = [
            "sensorId": sensorId,
            "status": status.rawValue,
            "rawData": [
                "transmitterName": transmitterName,
                "SensorId": sensorId,
                "R": UserDefaults.standard.double(forKey: "algo_r"),
                "K": UserDefaults.standard.double(forKey: "algo_k"),
                "Sensor": sensorId,
                "timeInMillis": Date().timeIntervalSince1970 * 1000
            ]
        ]
        print("===>json data:", json)
        guard let jsonData = try? JSONSerialization.data(withJSONObject: json) else { return }
        
        // Convert to String
        guard let jsonString = String(data: jsonData, encoding: .utf8) else { return }
        
        guard let encrypted = Crypto.shared.getEncryptedText(data: jsonString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) else { return }
        print("===>Encrypted: \(encrypted)")
        
        request.httpBody = encrypted.data(using: .utf8)
        
        // Use regular URLSession for foreground/background compatibility
        let session = URLSession.shared
        
        let task = session.uploadTask(with: request, from: encrypted.data(using: .utf8)) { data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }
            guard let data = data else {
                print("No data received")
                return
            }
            if let responseString = String(data: data, encoding: .utf8) {
                print("===>Server Response: \(responseString)")
                if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
                    print("===>Decrypted connection api response (for verification): \(decrypted)")
                }
            }
        }
        task.resume()
        print("===>Status upload started")
    }
    
    /*func getStatus(encryptionKey: String = PROD_ENC_KEY,
                   encryptionIv: String = PROD_ENC_IV) {
        print("ios token:", TOKEN)
        let url = URL(string: "https://api-feature2.mytatva.in/api/v8/cgm/status")!
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.timeoutInterval = 30
        
        // Set headers
        //request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue(PROD_API_KEY, forHTTPHeaderField: "api-key")
        request.addValue(TOKEN, forHTTPHeaderField: "token")
        
      
        // Set request body
        
        // Perform request
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }
            guard let data = data else {
                print("No data received")
                return
            }
            if let responseString = String(data: data, encoding: .utf8) {
                print("===>Server Response: \(responseString)")
                if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
                    print("===>Decrypted response (for verification): \(decrypted)")
                    
                    if let decrptedData = decrypted.data(using: .utf8) {
                        let decoded = try? JSONDecoder().decode(CGMStatusResponse.self, from: decrptedData)
                        
                        guard let sensor = decoded?.data.first,
                              !sensor.startDate.isEmpty,
                              !sensor.endDate.isEmpty else {
                            print("No valid sensor data")
                            return
                        }
                        
                        let startDateStr = sensor.startDate
                        let endDateStr = sensor.endDate
                        
                        print("Start Date: \(startDateStr)")
                        print("End Date: \(endDateStr)")
                        
                        if self.isCurrentDateInRange(startDate: startDateStr, endDate: endDateStr) {
                            print("Current date is in range")
    
                            //if KLTBluetoothManager.shared().currentDevice.initialEndDate != nil  {
                                //self.sendStatus(status: .transmitterDisconnect)
                           // }
                        }
                    }
                }
            }
        }
        task.resume()
        
    }*/
    
    func verifyDevice(
        sensorId: String,
        encryptionKey: String = PROD_ENC_KEY,
        encryptionIv: String = PROD_ENC_IV,
        onSuccess: @escaping (Int, Bool, String) -> Void,
        onFailure: @escaping () -> Void
    ) {
        let urlString = (envType.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/helper/device_verification?deviceId=\(sensorId)&deviceType=CGM&vendor=diasens"
        
        guard let url = URL(string: urlString) else {
            print("===>verifyDevice: Invalid URL")
            onFailure()
            return
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue(envType.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
        request.setValue(TOKEN, forHTTPHeaderField: "token")
        
        print("===>verifyDevice URL:", urlString)
        print("===>verifyDevice token:", TOKEN)
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                print("===>verifyDevice Error: \(error.localizedDescription)")
                DispatchQueue.main.async { onFailure() }
                return
            }
            
            guard let data = data, let responseString = String(data: data, encoding: .utf8) else {
                print("===>verifyDevice: No data received")
                DispatchQueue.main.async { onFailure() }
                return
            }
            
            print("===>verifyDevice Server Response: \(responseString)")
            
            guard let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) else {
                print("===>verifyDevice: Decryption failed")
                DispatchQueue.main.async { onFailure() }
                return
            }
            
            print("===>verifyDevice Decrypted: \(decrypted)")
            
            guard let jsonData = decrypted.data(using: .utf8),
                  let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
                print("===>verifyDevice: JSON parsing failed")
                DispatchQueue.main.async { onFailure() }
                return
            }
            
            var code = 0
            if let intCode = json["code"] as? Int {
                code = intCode
            } else if let strCode = json["code"] as? String, let parsed = Int(strCode) {
                code = parsed
            }
            let msg = json["msg"] as? String ?? ""
            let dataValue = json["data"] as? Bool ?? false
            
            DispatchQueue.main.async {
                onSuccess(code, dataValue, msg)
            }
        }
        task.resume()
    }
    
    // Example date range check function (assumes yyyy-MM-dd format)
    func isCurrentDateInRange(startDate: String, endDate: String) -> Bool {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        guard let start = formatter.date(from: startDate),
              let end = formatter.date(from: endDate) else {
            return false
        }
        
        let now = Date()
        return (now >= start) && (now <= end)
    }
}

class Crypto {
    
    static let shared = Crypto()
    
    private init() {
        
    }
    
    private func sha256(_ input: String) -> String {
        let inputData = Data(input.utf8)
        var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        inputData.withUnsafeBytes {
            _ = CC_SHA256($0.baseAddress, CC_LONG(inputData.count), &hash)
        }
        return hash.map { String(format: "%02x", $0) }.joined()
    }
    
    private func truncateKey(_ key: String) -> String {
        return String(key.prefix(32)) // Use only first 32 chars
    }
    
    func getEncryptedText(data: String, encryptionKey: String, encryptionIv: String) -> String? {
        let key = truncateKey(sha256(encryptionKey))
        guard let keyData = key.data(using: .utf8),
              let ivData = encryptionIv.data(using: .utf8),
              let dataToEncrypt = data.data(using: .utf8) else {
            return nil
        }
        
        let bufferSize = dataToEncrypt.count + kCCBlockSizeAES128
        var buffer = Data(count: bufferSize)
        
        var numBytesEncrypted: size_t = 0
        
        let cryptStatus = buffer.withUnsafeMutableBytes { bufferPtr in
            dataToEncrypt.withUnsafeBytes { dataPtr in
                keyData.withUnsafeBytes { keyPtr in
                    ivData.withUnsafeBytes { ivPtr in
                        CCCrypt(
                            CCOperation(kCCEncrypt),
                            CCAlgorithm(kCCAlgorithmAES),
                            CCOptions(kCCOptionPKCS7Padding),
                            keyPtr.baseAddress, kCCKeySizeAES256,
                            ivPtr.baseAddress,
                            dataPtr.baseAddress, dataToEncrypt.count,
                            bufferPtr.baseAddress, bufferSize,
                            &numBytesEncrypted
                        )
                    }
                }
            }
        }
        
        if cryptStatus == kCCSuccess {
            let encryptedData = buffer.prefix(numBytesEncrypted)
            return encryptedData.base64EncodedString()
        }
        return nil
    }
    
    func getDecryptedData(cipherText: String, encryptionKey: String, encryptionIv: String) -> String? {
        let key = truncateKey(sha256(encryptionKey))
        guard let keyData = key.data(using: .utf8),
              let ivData = encryptionIv.data(using: .utf8),
              let encryptedData = Data(base64Encoded: cipherText) else {
            return nil
        }
        
        let bufferSize = encryptedData.count + kCCBlockSizeAES128
        var buffer = Data(count: bufferSize)
        
        var numBytesDecrypted: size_t = 0
        
        let cryptStatus = buffer.withUnsafeMutableBytes { bufferPtr in
            encryptedData.withUnsafeBytes { encryptedPtr in
                keyData.withUnsafeBytes { keyPtr in
                    ivData.withUnsafeBytes { ivPtr in
                        CCCrypt(
                            CCOperation(kCCDecrypt),
                            CCAlgorithm(kCCAlgorithmAES),
                            CCOptions(kCCOptionPKCS7Padding),
                            keyPtr.baseAddress, kCCKeySizeAES256,
                            ivPtr.baseAddress,
                            encryptedPtr.baseAddress, encryptedData.count,
                            bufferPtr.baseAddress, bufferSize,
                            &numBytesDecrypted
                        )
                    }
                }
            }
        }
        
        if cryptStatus == kCCSuccess {
            let decryptedData = buffer.prefix(numBytesDecrypted)
            return String(data: decryptedData, encoding: .utf8)
        }
        return nil
    }
    
}

struct CGMStatusResponse: Codable {
    let code: Int
    let status: String
    let msg: String
    let data: [CGMStatusItem]
}

struct CGMStatusItem: Codable {
    let status: String
    let startDate: String
    let endDate: String
    let sensorId: String
    let daysToExpire: Int
}
