import Foundation
import CoreBluetooth

@MainActor
final class PrinterManager: NSObject, ObservableObject {
    @Published var status = "Printer belum terhubung"
    @Published var scanning = false
    @Published var connected = false

    private var central: CBCentralManager!
    private var peripheral: CBPeripheral?
    private var writable: CBCharacteristic?
    private var queuedText: String?

    private let candidateServices: [CBUUID] = [
        CBUUID(string: "FFE0"), CBUUID(string: "FF00"), CBUUID(string: "FFF0"), CBUUID(string: "18F0"),
        CBUUID(string: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
    ]

    override init() {
        super.init()
        central = CBCentralManager(delegate: self, queue: .main)
    }

    func handle(url: URL) {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
              let item = components.queryItems?.first(where: { $0.name == "data" }),
              let raw = item.value,
              let data = raw.data(using: .utf8),
              let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let text = json["receipt_text"] as? String else {
            status = "Payload cetak tidak valid"
            return
        }
        queuedText = text
        scan()
    }

    func scan() {
        guard central.state == .poweredOn else { status = "Bluetooth belum siap"; return }
        scanning = true
        status = "Mencari printer BLE…"
        central.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
        Task { try? await Task.sleep(for: .seconds(6)); await MainActor.run { self.central.stopScan(); self.scanning = false; if !self.connected { self.status = "Printer belum ditemukan" } } }
    }

    func printText(_ text: String) {
        guard let characteristic = writable, let peripheral else {
            queuedText = text
            scan()
            return
        }
        var bytes = Data([0x1B, 0x40])
        bytes.append(text.data(using: .utf8) ?? Data())
        bytes.append(contentsOf: [0x0A, 0x0A, 0x0A])
        peripheral.writeValue(bytes, for: characteristic, type: characteristic.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse)
        status = "Struk dikirim ke printer"
    }
}

extension PrinterManager: CBCentralManagerDelegate {
    nonisolated func centralManagerDidUpdateState(_ central: CBCentralManager) {
        Task { @MainActor in
            self.status = central.state == .poweredOn ? "Bluetooth siap" : "Aktifkan Bluetooth"
        }
    }

    nonisolated func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
                                    advertisementData: [String : Any], rssi RSSI: NSNumber) {
        Task { @MainActor in
            self.peripheral = peripheral
            self.status = "Menghubungkan: \(peripheral.name ?? "Printer")"
            self.central.stopScan()
            self.central.connect(peripheral, options: nil)
        }
    }

    nonisolated func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        Task { @MainActor in
            self.connected = true
            self.status = "Terhubung: \(peripheral.name ?? "Printer")"
            peripheral.delegate = self
            peripheral.discoverServices(nil)
        }
    }

    nonisolated func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        Task { @MainActor in
            self.connected = false
            self.writable = nil
            self.status = "Printer terputus"
        }
    }
}

extension PrinterManager: CBPeripheralDelegate {
    nonisolated func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        peripheral.services?.forEach { peripheral.discoverCharacteristics(nil, for: $0) }
    }

    nonisolated func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        Task { @MainActor in
            if let ch = service.characteristics?.first(where: { $0.properties.contains(.write) || $0.properties.contains(.writeWithoutResponse) }) {
                self.writable = ch
                self.status = "Printer siap mencetak"
                if let text = self.queuedText { self.queuedText = nil; self.printText(text) }
            }
        }
    }
}
