// API/security contract for unauthenticated relay RPC rejection.
//
// Scope: preserve the security boundary between transport authentication and
// handler execution for media, HID, and paste RPCs.
// Targets: server entrypoint and relay helper modules.
// Why: without TLS/mTLS on the server builder, any client could stream media or
// HID reports into the remote controlled target.

const ENTRYPOINT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/entrypoint.rs"
));
const SERVER_SECURITY: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/security.rs"
));
const INPUT_RPC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service/input_stream_rpc.rs"
));
const CAMERA_RPC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service/camera_stream_rpc.rs"
));
const MICROPHONE_RPC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/relay_service/microphone_stream_rpc.rs"
));
const RPC_HELPERS: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/server/src/main/rpc_helpers.rs"
));

#[test]
fn server_installs_tls_before_exposing_relay_services() {
    let tls_idx = ENTRYPOINT
        .find("security::server_tls_config()?")
        .expect("entrypoint should request TLS config");
    let relay_idx = ENTRYPOINT
        .find(".add_service(RelayServer::new(handler))")
        .expect("entrypoint should expose relay service");
    assert!(
        tls_idx < relay_idx,
        "TLS must be configured before media/HID RPC services are exposed"
    );
    assert!(ENTRYPOINT.contains("server = server.tls_config(tls)?"));
}

#[test]
fn production_mtls_requires_a_client_ca_unless_operator_explicitly_opts_out() {
    assert!(SERVER_SECURITY.contains("client_ca_root"));
    assert!(SERVER_SECURITY.contains("LESAVKA_TLS_CLIENT_AUTH_OPTIONAL"));
    assert!(
        SERVER_SECURITY.contains("TLS enabled with required client certificate authentication"),
        "required client certificate auth should be the normal CA-backed path"
    );
}

#[test]
fn powerful_rpc_handlers_depend_on_transport_auth_boundary() {
    for (name, source, lease_marker) in [
        ("keyboard", INPUT_RPC, "capture_power.acquire_session()"),
        ("mouse", INPUT_RPC, "capture_power.acquire_session()"),
        ("camera", CAMERA_RPC, "activate_camera()"),
        ("microphone", MICROPHONE_RPC, "reserve_microphone_sink"),
    ] {
        assert!(
            source.contains(lease_marker),
            "{name} RPC should continue using runtime leases after transport auth"
        );
        assert!(
            !source.contains("LESAVKA_ALLOW_INSECURE"),
            "{name} RPC should not override transport security inside handler code"
        );
    }
}

#[test]
fn unauthenticated_paste_payloads_map_to_unauthenticated_status() {
    assert!(RPC_HELPERS.contains("paste::decrypt(&req)"));
    assert!(RPC_HELPERS.contains("Status::unauthenticated"));
    assert!(
        RPC_HELPERS
            .find("paste::decrypt(&req)")
            .expect("decrypt marker")
            < RPC_HELPERS
                .find("paste::type_text")
                .expect("type text marker"),
        "paste payloads must authenticate/decrypt before HID typing begins"
    );
}
