import UIKit
import Models
import Shared
import XXModels
import Foundation

extension Session {
    public func send(imageData: Data, to contact: Contact, completion: @escaping (Result<Void, Error>) -> Void) {
        client.bindings.compress(image: imageData) { [weak self] in
            guard let self = self else {
                completion(.success(()))
                return
            }

            switch $0 {
            case .success(let compressed):
                let name = "image_\(Date.asTimestamp)"
                try! FileManager.store(data: compressed, name: name, type: Attachment.Extension.image.written)
                let attachment = Attachment(name: name, data: compressed, _extension: .image)
                self.send(Payload(text: "You sent an image", reply: nil, attachment: attachment), toContact: contact)
                completion(.success(()))
            case .failure(let error):
                completion(.failure(error))
                log(string: "Error when compressing image: \(error.localizedDescription)", type: .error)
            }
        }
    }

    public func send(_ payload: Payload, toContact contact: Contact) {
        var message = Message(
            networkId: nil,
            senderId: client.bindings.meMarshalled,
            recipientId: contact.id,
            groupId: nil,
            date: Date(),
            status: .sending,
            isUnread: false,
            text: payload.text,
            replyMessageId: payload.reply?.messageId,
            roundURL: nil,
            fileTransferId: nil
        )

        do {
            message = try dbManager.saveMessage(message)
            send(message: message)
        } catch {
            log(string: error.localizedDescription, type: .error)
        }
    }

    public func retryMessage(_ id: Int64) {
        if var message = try? dbManager.fetchMessages(.init(id: [id])).first {
            message.status = .sending
            message.date = Date()

            do {
                message = try dbManager.saveMessage(message)
                send(message: message)
            } catch {
                print(error.localizedDescription)
            }
        }
    }

    private func send(message: Message) {
        var message = message

//        if let _ = message.payload.attachment {
//            sendAttachment(message: message)
//            return
//        }

        DispatchQueue.global().async { [weak self] in
            guard let self = self else { return }
            switch self.client.bindings.send(message.text.data(using: .utf8)!, to: message.recipientId!) {
            case .success(let report):
                message.roundURL = report.roundURL

                self.client.bindings.listen(report: report.marshalled) { result in
                    switch result {
                    case .success(let status):
                        switch status {
                        case .failed:
                            message.status = .sendingFailed
                        case .sent:
                            message.status = .sent
                        case .timedout:
                            message.status = .sendingTimedOut
                        }
                    case .failure:
                        message.status = .sendingFailed
                    }

                    message.networkId = report.uniqueId
                    message.date = Date.fromTimestamp(Int(report.timestamp))
                    DispatchQueue.main.async {
                        do {
                            _ = try self.dbManager.saveMessage(message)
                        } catch {
                            log(string: error.localizedDescription, type: .error)
                        }
                    }
                }
            case .failure(let error):
                message.status = .sendingFailed
                log(string: error.localizedDescription, type: .error)
            }

            DispatchQueue.main.async {
                do {
                    _ = try self.dbManager.saveMessage(message)
                } catch {
                    log(string: error.localizedDescription, type: .error)
                }
            }
        }
    }

//    private func sendAttachment(message: Message) {
//        guard let manager = client.transferManager else { fatalError("A transfer manager was not created") }
//
//        var message = message
//        let attachment = message.payload.attachment!
//
//        DispatchQueue.global().async { [weak self] in
//            guard let self = self else { return }
//
//            do {
//                let tid = try manager.uploadFile(attachment, to: message.receiver) { completed, send, arrived, total, error in
//                    if completed {
//                        self.endTransferFrom(message: message)
//                        message.status = .sent
//                        message.payload.attachment?.progress = 1.0
//                        log(string: "FT Up finished", type: .info)
//                    } else {
//                        if let error = error {
//                            log(string: error.localizedDescription, type: .error)
//                            message.status = .failedToSend
//                        } else {
//                            let progress = Float(arrived)/Float(total)
//                            message.payload.attachment?.progress = progress
//                            log(string: "FT Up: \(progress)", type: .crumbs)
//                        }
//                    }
//
//                    do {
//                        _ = try self.dbManager.save(message) // If it fails here, means the chat was cleared.
//                    } catch {
//                        log(string: error.localizedDescription, type: .error)
//                    }
//                }
//
//                let transfer = FileTransfer(
//                    tid: tid,
//                    contact: message.receiver,
//                    fileName: attachment.name,
//                    fileType: attachment._extension.written,
//                    isIncoming: false
//                )
//
//                message.payload.attachment?.transferId = tid
//                message.status = .sending
//
//                do {
//                    _ = try self.dbManager.saveMessage(message)
//                    _ = try self.dbManager.save(transfer)
//                } catch {
//                    log(string: error.localizedDescription, type: .error)
//                }
//            } catch {
//                message.status = .sendingFailed
//                log(string: error.localizedDescription, type: .error)
//
//                do {
//                    _ = try self.dbManager.saveMessage(message)
//                } catch let otherError {
//                    log(string: otherError.localizedDescription, type: .error)
//                }
//            }
//        }
//    }
//
//    private func endTransferFrom(message: Message) {
//        guard let manager = client.transferManager else { fatalError("A transfer manager was not created") }
//        guard let tid = message.payload.attachment?.transferId else { fatalError("Tried to finish a transfer that had no TID") }
//
//        do {
//            try manager.endTransferUpload(with: tid)
//
//            if let transfer: FileTransfer = try? dbManager.fetch(.withTID(tid)).first {
//                try dbManager.delete(transfer)
//            }
//        } catch {
//            log(string: error.localizedDescription, type: .error)
//        }
//    }
//
//    func handle(incomingTransfer transfer: FileTransfer) {
//        guard let manager = client.transferManager else { fatalError("A transfer manager was not created") }
//
//        let fileExtension: Attachment.Extension = transfer.fileType == "m4a" ? .audio : .image
//        let name = "\(Date.asTimestamp)_\(transfer.fileName)"
//
//        var fakeContent: Data
//
//        if fileExtension == .image {
//            fakeContent = Asset.transferImagePlaceholder.image.jpegData(compressionQuality: 0.1)!
//        } else {
//            fakeContent = FileManager.dummyAudio()
//        }
//
//        let attachment = Attachment(name: name, data: fakeContent, transferId: transfer.tid, _extension: fileExtension)
//
//        var message = Message(
//            sender: transfer.contact,
//            receiver: client.bindings.meMarshalled,
//            payload: .init(text: "Sent you a \(fileExtension.writtenExtended)", reply: nil, attachment: attachment),
//            unread: true,
//            timestamp: Date.asTimestamp,
//            uniqueId: nil,
//            status: .receivingAttachment
//        )
//
//        do {
//            message = try self.dbManager.saveMessage(message)
//            try self.dbManager.save(transfer)
//        } catch {
//            log(string: "Failed to save message/transfer to the database. Will not start listening to transfer... \(error.localizedDescription)", type: .info)
//            return
//        }
//
//        log(string: "FT Down starting", type: .info)
//
//        try! manager.listenDownloadFromTransfer(with: transfer.tid) { completed, arrived, total, error in
//            if let error = error {
//                fatalError(error.localizedDescription)
//            }
//
//            if completed {
//                log(string: "FT Down finished", type: .info)
//
//                guard let rawFile = try? manager.downloadFileFromTransfer(with: transfer.tid) else {
//                    log(string: "Received finalized transfer, file was nil. Ignoring...", type: .error)
//                    return
//                }
//
//                try! FileManager.store(data: rawFile, name: name, type: fileExtension.written)
//                var realAttachment = Attachment(name: name, data: rawFile, transferId: transfer.tid, _extension: fileExtension)
//                realAttachment.progress = 1.0
//                message.payload = .init(text: "Sent you a \(transfer.fileType)", reply: nil, attachment: realAttachment)
//                message.status = .received
//
//                if let toDelete: FileTransfer = try? self.dbManager.fetch(.withTID(transfer.tid)).first {
//                    do {
//                        try self.dbManager.delete(toDelete)
//                    } catch {
//                        log(string: error.localizedDescription, type: .error)
//                    }
//                }
//            } else {
//                let progress = Float(arrived)/Float(total)
//                log(string: "FT Down: \(progress)", type: .crumbs)
//                message.payload.attachment?.progress = progress
//            }
//
//            do {
//                try self.dbManager.save(message) // If it fails here, means the chat was cleared.
//            } catch {
//                log(string: "Failed to update message model from an incoming transfer. Probably chat was cleared: \(error.localizedDescription)", type: .error)
//            }
//        }
//    }
}