import Foundation

/// Wrapper um das `rebreak-supervise-magic` Go-binary aus
/// `ops/mdm/supervise-magic/bin/`. Spawnt es als child-process + streamt
/// stdout zeilenweise in die UI.
enum SuperviseRunner {
    enum RunnerError: Error, LocalizedError {
        case binaryMissing
        case nonZeroExit(Int32)

        var errorDescription: String? {
            switch self {
            case .binaryMissing:
                return "supervise-magic Binary nicht gefunden. Bitte aus `ops/mdm/supervise-magic/` via `make build` bauen oder REBREAK_SUPERVISE_MAGIC_BIN setzen."
            case .nonZeroExit(let code):
                return "supervise-magic ist mit Exit-Code \(code) abgebrochen."
            }
        }
    }

    /// Pre-Flight Check: liest FMI/SDP-Status + IsSupervised.
    /// Returnt das geparste output. Mit -v für detaillierte logs.
    @MainActor
    static func check(onLine: @escaping (String) -> Void) async throws -> ProcessRunner.Result {
        guard let bin = Paths.firstExecutable(in: Paths.superviseMagicCandidates) else {
            throw RunnerError.binaryMissing
        }
        return try await ProcessRunner.stream(bin, arguments: ["check", "-v"], onLine: onLine)
    }

    /// Schreibt CloudConfigurationDetails.plist auf das iPhone + reboot.
    /// supervise-magic macht die ganze MobileBackup2-Sandwich-Logik.
    @MainActor
    static func supervise(
        organizationName: String = "ReBreak",
        force: Bool = true,
        verbose: Bool = false,
        onLine: @escaping (String) -> Void
    ) async throws -> ProcessRunner.Result {
        guard let bin = Paths.firstExecutable(in: Paths.superviseMagicCandidates) else {
            throw RunnerError.binaryMissing
        }
        // -yes ist Pflicht: ohne TTY-Pipe hängt der Bestätigungs-Prompt sonst endlos.
        // CLI-Konvention: <command> [flags] — command MUSS zuerst kommen.
        var args: [String] = ["supervise", "-yes"]
        if verbose { args.append("-v") }
        if force { args.append("-force") }
        args.append(contentsOf: ["-org", organizationName])
        let result = try await ProcessRunner.stream(bin, arguments: args, onLine: onLine)
        if result.exitCode != 0 {
            throw RunnerError.nonZeroExit(result.exitCode)
        }
        return result
    }

    /// Reverse-Operation für Tests / Recovery.
    @MainActor
    static func unsupervise(onLine: @escaping (String) -> Void) async throws -> ProcessRunner.Result {
        guard let bin = Paths.firstExecutable(in: Paths.superviseMagicCandidates) else {
            throw RunnerError.binaryMissing
        }
        return try await ProcessRunner.stream(bin, arguments: ["unsupervise", "-v", "-yes"], onLine: onLine)
    }
}
