import Foundation
import os
import UIKit

#if DEBUG
/// Lightweight startup benchmarks for local before/after performance checks.
/// Run with launch env:
/// `ROSETTA_RUN_BENCHMARKS=1` and optionally `ROSETTA_BENCHMARK_EXIT=1`.
enum DebugPerformanceBenchmarks {
    nonisolated private static let logger = Logger(subsystem: "com.rosetta.messenger", category: "Bench")

    nonisolated static func runIfRequested() {
        guard ProcessInfo.processInfo.environment["ROSETTA_RUN_BENCHMARKS"] == "1" else { return }

        Task.detached(priority: .userInitiated) {
            let nativeCryptoDisabled = ProcessInfo.processInfo.environment["ROSETTA_DISABLE_NATIVE_CRYPTO"] == "1"
            let nativeBlurHashDisabled = ProcessInfo.processInfo.environment["ROSETTA_DISABLE_NATIVE_BLURHASH"] == "1"

            let beginLine = "BENCH_BEGIN native_crypto_disabled=\(nativeCryptoDisabled) native_blurhash_disabled=\(nativeBlurHashDisabled)"
            print(beginLine)
            logger.info("\(beginLine)")

            do {
                let cryptoResult = try benchmarkXChaCha20()
                let blurResult = benchmarkBlurHash()
                let streamResult = await MainActor.run {
                    benchmarkStream()
                }

                let cryptoLine =
                    "BENCH_CRYPTO encrypt_ms_total=\(fmt(cryptoResult.encryptTotalMs)) encrypt_ms_per_op=\(fmt(cryptoResult.encryptMsPerOp)) decrypt_ms_total=\(fmt(cryptoResult.decryptTotalMs)) decrypt_ms_per_op=\(fmt(cryptoResult.decryptMsPerOp))"
                let blurLine =
                    "BENCH_BLURHASH decode_ms_total=\(fmt(blurResult.totalMs)) decode_ms_per_op=\(fmt(blurResult.msPerOp))"
                let streamLine =
                    "BENCH_STREAM write_ms_total=\(fmt(streamResult.writeTotalMs)) write_ms_per_op=\(fmt(streamResult.writeMsPerOp)) read_ms_total=\(fmt(streamResult.readTotalMs)) read_ms_per_op=\(fmt(streamResult.readMsPerOp))"
                print(cryptoLine)
                print(blurLine)
                print(streamLine)
                print("BENCH_END")
                logger.info("\(cryptoLine)")
                logger.info("\(blurLine)")
                logger.info("\(streamLine)")
                logger.info("BENCH_END")
            } catch {
                let errorLine = "BENCH_ERROR \(error)"
                print(errorLine)
                logger.error("\(errorLine)")
            }

            if ProcessInfo.processInfo.environment["ROSETTA_BENCHMARK_EXIT"] == "1" {
                exit(0)
            }
        }
    }

    nonisolated private static func benchmarkXChaCha20() throws -> (
        encryptTotalMs: Double,
        encryptMsPerOp: Double,
        decryptTotalMs: Double,
        decryptMsPerOp: Double
    ) {
        let payload = Data((0..<1024).map { UInt8($0 & 0xFF) })
        let key = Data((0..<32).map { UInt8(($0 + 1) & 0xFF) })
        let nonce = Data((0..<24).map { UInt8(($0 + 2) & 0xFF) })
        let iterations = 1000

        let encryptTotalMs = try measureMs(iterations: iterations) {
            _ = try XChaCha20Engine.encrypt(plaintext: payload, key: key, nonce: nonce)
        }

        let ciphertext = try XChaCha20Engine.encrypt(plaintext: payload, key: key, nonce: nonce)
        let decryptTotalMs = try measureMs(iterations: iterations) {
            _ = try XChaCha20Engine.decrypt(ciphertextWithTag: ciphertext, key: key, nonce: nonce)
        }

        return (
            encryptTotalMs: encryptTotalMs,
            encryptMsPerOp: encryptTotalMs / Double(iterations),
            decryptTotalMs: decryptTotalMs,
            decryptMsPerOp: decryptTotalMs / Double(iterations)
        )
    }

    nonisolated private static func benchmarkBlurHash() -> (totalMs: Double, msPerOp: Double) {
        let blurHash = "LEHV6nWB2yk8pyo0adR*.7kCMdnj"
        let iterations = 800

        let totalMs = measureMs(iterations: iterations) {
            _ = UIImage.fromBlurHash(blurHash, width: 32, height: 32, punch: 1)
        }
        return (totalMs: totalMs, msPerOp: totalMs / Double(iterations))
    }

    @MainActor
    private static func benchmarkStream() -> (
        writeTotalMs: Double,
        writeMsPerOp: Double,
        readTotalMs: Double,
        readMsPerOp: Double
    ) {
        let iterations = 20_000
        var packet = Data()

        let writeTotalMs = measureMs(iterations: iterations) {
            let stream = Stream()
            stream.writeInt16(0x19)
            stream.writeInt64(1_741_129_600_123)
            stream.writeString("bench_message")
            stream.writeBoolean(true)
            stream.writeBytes(Data([1, 2, 3, 4, 5, 6, 7, 8]))
            packet = stream.toData()
        }

        let readTotalMs = measureMs(iterations: iterations) {
            let stream = Stream(data: packet)
            _ = stream.readInt16()
            _ = stream.readInt64()
            _ = stream.readString()
            _ = stream.readBoolean()
            _ = stream.readBytes()
        }

        return (
            writeTotalMs: writeTotalMs,
            writeMsPerOp: writeTotalMs / Double(iterations),
            readTotalMs: readTotalMs,
            readMsPerOp: readTotalMs / Double(iterations)
        )
    }

    nonisolated private static func measureMs(iterations: Int, block: () throws -> Void) rethrows -> Double {
        let start = DispatchTime.now().uptimeNanoseconds
        for _ in 0..<iterations {
            try block()
        }
        let end = DispatchTime.now().uptimeNanoseconds
        let elapsedNs = end - start
        return Double(elapsedNs) / 1_000_000.0
    }

    nonisolated private static func fmt(_ value: Double) -> String {
        String(format: "%.3f", value)
    }
}
#endif
