import ExpoModulesCore

public class ZkExpoModule: Module {

    public func definition() -> ModuleDefinition {

        Name("ZkExpo")

        AsyncFunction("groth16Prove") { (a: String, b: String, c: String) -> String in
            let c_str = groth16_prove(a, b, c) as UnsafePointer<CChar>?
            return self.string(from: c_str)
        }
        AsyncFunction("groth16ProveV2") { (a: String, b: String, c: String) -> String in
            let c_str = groth16_prove_v2(a, b, c) as UnsafePointer<CChar>?
            return self.string(from: c_str)
        }

        AsyncFunction("halo2Prove") {
            (wasm_path: String, public_inputs: String) throws -> String in

            try ensureFile(wasm_path)
            let c_str = halo2_prove(wasm_path, public_inputs) as UnsafePointer<CChar>?
            return self.string(from: c_str)
        }

        AsyncFunction("noirProve") {
            (dataPath: String, witnessJson: String, type: String) -> String in

            try ensureFile(dataPath)

            let c_str = noir_prove(dataPath, witnessJson, type) as UnsafePointer<CChar>?
            return self.string(from: c_str)
        }

        AsyncFunction("addToCache") {
            (functionName: String, path1: String, path2: String) -> Void in

            try ensureFile(path1)
            try ensureFile(path2)

            add_to_cache(functionName, path1, path2)
        }

        AsyncFunction("clearCache") {
            () -> Void in
            clear_cache()
        }
    }

    @inline(__always)
    private func string(from cstr: UnsafePointer<CChar>?) -> String {
        guard let cstr = cstr else { return "" }
        // The defer block will execute when the function returns.
        // It calls our Rust function to safely free the memory.
        defer {
            free_string(UnsafeMutablePointer(mutating: cstr))
        }
        return String(cString: cstr)
    }

    private func ensureFile(_ path: String) throws {
        guard FileManager.default.fileExists(atPath: path) else {
            throw NSError(
                domain: "ZkExpoModule",
                code: 1,
                userInfo: [NSLocalizedDescriptionKey: "File not found: \(path)"]
            )
        }
    }
}
