import Foundation
import CryptoKit

struct Client {
    
    let server: URL
    
    private let delegate = NeverCacheDelegate()
    
    init(server: URL) {
        self.server = server
    }

    func deviceStatus(authToken: Data) async -> ClientState {
        await send(path: .getDeviceStatus, data: authToken).state
    }
    
    func send(_ message: Message, authToken: Data) async -> (state: ClientState, response: Message?) {
        let serverMessage = ServerMessage(authToken: authToken, message: message)
        return await send(path: .postMessage, data: serverMessage.encoded)
    }

    private func send(path: RouteAPI, data: Data) async -> (state: ClientState, response: Message?) {
        let url = server.appendingPathComponent(path.rawValue)
        var request = URLRequest(url: url)
        request.httpBody = data
        request.httpMethod = "POST"
        guard let data = await fulfill(request) else {
            return (.deviceNotAvailable(.serverNotReached), nil)
        }
        guard let byte = data.first else {
            return (.internalError("Empty response"), nil)
        }
        guard let status = MessageResult(rawValue: byte) else {
            return (.internalError("Invalid message response: \(byte)"), nil)
        }
        let result = ClientState(keyResult: status)
        guard data.count == Message.length + 1 else {
            return (result, nil)
        }
        let messageData = Array(data.advanced(by: 1))
        let message = Message(decodeFrom: messageData)
        return (result, message)
    }
    
    private func fulfill(_ request: URLRequest) async -> Data? {
        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            guard let code = (response as? HTTPURLResponse)?.statusCode else {
                print("No response from server")
                return nil
            }
            guard code == 200 else {
                print("Invalid server response \(code)")
                return nil
            }
            return data
        } catch {
            print("Request failed: \(error)")
            return nil
        }
    }
}

class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate {
    
    func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? {
        return nil
    }
}
