import Foundation

final class TransferHandler: ObservableObject {

    @Published
    var bytesTransferred: Double = 0.0

    @Published
    var totalBytes: Double = 0.0

    @Published
    var measurements: [TemperatureMeasurement] = []

    @Published
    var transferIsRunning = false

    func startTransfer(from device: BluetoothDevice, with info: DeviceInfo, storage: PersistentStorage) {
        #warning("Update device info during transfer")
        discardTransfer()
        transferIsRunning = true
        let total = info.numberOfRecordedBytes
        let chunkSize = info.transferBlockSize
        totalBytes = Double(total)
        Task {
            defer {
                DispatchQueue.main.async {
                    self.transferIsRunning = false
                }
            }
            var data = Data(capacity: total)
            while data.count < total {
                let remainingBytes = total - data.count
                let currentChunkSize = min(remainingBytes, chunkSize)
                guard let chunk = await device.getDeviceData(offset: data.count, count: currentChunkSize) else {
                    log.warning("Failed to finish transfer")
                    return
                }
                guard !chunk.isEmpty else {
                    break
                }
                data.append(chunk)
                let count = Double(data.count)
                DispatchQueue.main.async {
                    self.bytesTransferred = count
                }
            }

            if data.count != info.numberOfRecordedBytes {
                log.warning("Expected \(info.numberOfRecordedBytes) in transfer, got only \(data.count)")
            }
            let sum = data.reduce(0) { $0 &+ UInt16($1) }
            if sum != info.dataChecksum {
                log.warning("Checksum does not match")
            }
            if data.count != 4 * info.numberOfStoredMeasurements {
                log.warning("expected \(4 * info.numberOfStoredMeasurements) bytes for \(info.numberOfStoredMeasurements) measurements, got \(data.count)")
            }
            storage.saveTransferData(data: data, date: info.time.date)

            DispatchQueue.main.async {
                self.bytesTransferred = self.totalBytes
            }

            let recordingStart = info.currentMeasurementStartTime
            while !data.isEmpty {
                guard data.count >= 4 else {
                    log.error("Expected four bytes at index \(total - data.count - 1)")
                    break
                }
                let intervalCount = try! data.decodeUInt16()
                let temp0 = TemperatureValue(byte: data.removeFirst())
                let temp1 = TemperatureValue(byte: data.removeFirst())
                let date = recordingStart
                    .addingTimeInterval(TimeInterval(intervalCount) * TimeInterval(info.measurementInterval))
                let measurement = TemperatureMeasurement(
                    sensor0: temp0,
                    sensor1: temp1,
                    date: date)
                DispatchQueue.main.async {
                    self.measurements.append(measurement)
                }
            }
        }
    }

    func saveTransfer(in storage: PersistentStorage) {
        storage.add(measurements)
        discardTransfer()
    }

    func discardTransfer() {
        self.measurements = []
        self.bytesTransferred = 0
        self.totalBytes = 0
    }
}
