import Foundation
import WireProtocol

/// Deterministic RNG for the roundtrip property test (reproducible failures:
/// the seed is baked into the test; change it only with a note in the test).
struct SplitMix64: RandomNumberGenerator {
    private var state: UInt64

    init(seed: UInt64) {
        state = seed
    }

    mutating func next() -> UInt64 {
        state &+= 0x9E37_79B9_7F4A_7C15
        var z = state
        z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9
        z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB
        return z ^ (z >> 31)
    }
}

enum MessageGen {
    /// Character pool that stresses the JSON escaper: quotes, backslashes,
    /// C0 control bytes (ESC / CR / TAB / ^C / NUL), CJK, emoji, plain ASCII.
    private static let stressPool: [Character] = [
        "a", "Z", "0", " ", "\"", "\\", "/", "{", "}",
        "\u{1B}", "\r", "\n", "\t", "\u{03}", "\u{00}", "\u{7F}",
        "中", "文", "終", "端", "🚀", "🧪", "é", "ß",
    ]

    static func randomString(using rng: inout some RandomNumberGenerator, maxLength: Int = 48) -> String {
        let length = Int.random(in: 0...maxLength, using: &rng)
        return String((0..<length).map { _ in stressPool.randomElement(using: &rng)! })
    }

    static func randomAbsolutePath(using rng: inout some RandomNumberGenerator) -> String {
        "/" + randomString(using: &rng, maxLength: 24)
    }

    /// Seeded UUID v4 (version nibble 4, variant 10xx) — passes SESSION_ID_RE.
    static func randomUUIDv4(using rng: inout some RandomNumberGenerator) -> UUID {
        var bytes = (0..<16).map { _ in UInt8.random(in: 0...255, using: &rng) }
        bytes[6] = (bytes[6] & 0x0F) | 0x40
        bytes[8] = (bytes[8] & 0x3F) | 0x80
        return UUID(uuid: (
            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
        ))
    }

    static func randomClientMessage(using rng: inout some RandomNumberGenerator) -> ClientMessage {
        switch Int.random(in: 0...4, using: &rng) {
        case 0:
            let sessionId = Bool.random(using: &rng) ? randomUUIDv4(using: &rng) : nil
            let cwd = Bool.random(using: &rng) ? randomAbsolutePath(using: &rng) : nil
            return .attach(sessionId: sessionId, cwd: cwd)
        case 1:
            return .input(data: randomString(using: &rng))
        case 2:
            return .resize(
                cols: Int.random(in: 1...1000, using: &rng),
                rows: Int.random(in: 1...1000, using: &rng)
            )
        case 3:
            let mode = Bool.random(using: &rng) ? ApproveMode.allCases.randomElement(using: &rng) : nil
            return .approve(mode: mode)
        default:
            return .reject
        }
    }
}
