//! Android JNI entry point for the platform permission requester.
//!
//! Trace: SDD-106 §5/§6, SRS-209.
//!
//! `AndroidPermissionRequester` (Kotlin) resolves the runtime
//! `RECORD_AUDIO` permission state and forwards every transition to
//! Dart over the existing MethodChannel. Per SDD-106 §5/§6 the
//! authoritative consumer of that state is the Rust audio engine's
//! `TransmitModeSelector`, not the Dart UI — Dart-only delivery is
//! insufficient because the UI may not have re-rendered by the time
//! the audio thread next reads the gate.
//!
//! This module exposes a JNI function that the Kotlin
//! `MainActivity.stateChangeListener` calls in addition to the
//! MethodChannel. The function:
//!
//! 1. Decodes the `permission` and `state` strings out of the JVM.
//! 2. Maps the `state` string to [`crate::api::PermissionStateKind`]
//!    (unrecognised values map to `Unknown` — fail-safe per SRS-209).
//! 3. Calls [`crate::api::publish_permission_state`] which:
//!      a. clamps the audio engine's transmit gate when the
//!         permission identifier is `RECORD_AUDIO` (SDD-106 §6); and
//!      b. broadcasts a `BridgeEvent::PermissionState` so any Dart
//!         subscriber observes the same authoritative state.
//!
//! ## Panic-safety
//!
//! Every call site is wrapped in [`std::panic::catch_unwind`]. A
//! Rust panic must never unwind into the JVM (UB). Panics are
//! logged via the `log` facade and otherwise swallowed; the JNI
//! function returns `void`.

use std::panic::catch_unwind;

use jni::objects::{JClass, JString};
use jni::JNIEnv;
use log::{error, warn};

use crate::api::{publish_permission_state, PermissionStateKind};

/// JNI entry point called from
/// `app.chanora.chanora_flutter.MainActivity.publishPermissionState`.
///
/// Symbol mangling note (mirrors SDD-105's `initChanoraContext`):
/// the literal `_` inside the package segment `chanora_flutter` is
/// escaped as `_1` in the JNI symbol — this is JNI's package-name
/// encoding for `_`.
///
/// Trace: SDD-106 §5.
#[no_mangle]
pub extern "system" fn Java_app_chanora_chanora_1flutter_MainActivity_publishPermissionState<
    'local,
>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    permission: JString<'local>,
    state: JString<'local>,
) {
    // Wrap the entire body in catch_unwind: a panic across the JNI
    // boundary is undefined behaviour. We log and swallow on panic.
    let result = catch_unwind(std::panic::AssertUnwindSafe(|| {
        let permission_str: String = match env.get_string(&permission) {
            Ok(s) => s.into(),
            Err(e) => {
                warn!("publishPermissionState: invalid permission string: {e}");
                return;
            }
        };
        let state_str: String = match env.get_string(&state) {
            Ok(s) => s.into(),
            Err(e) => {
                warn!("publishPermissionState: invalid state string: {e}");
                return;
            }
        };
        let kind = PermissionStateKind::from_kotlin_str(&state_str);
        // SDD-106 §5/§6: hands off to the bridge's shared publisher
        // which performs the audio-engine clamp and event fan-out.
        publish_permission_state(permission_str, kind);
    }));
    if let Err(_panic) = result {
        // Do NOT propagate the panic payload across the FFI
        // boundary. `_panic` may carry a non-UnwindSafe payload.
        error!(
            target: "chanora_bridge",
            "publishPermissionState: panic caught at JNI boundary; swallowed"
        );
    }
}
