// Contract tests for lesavka-core gadget rebuild guardrails.
//
// Scope: statically guard the shell logic in `scripts/daemon/lesavka-core.sh`.
// Targets: incomplete gadget recovery behavior.
// Why: the gadget can be bound but half-applied, and the core script must
// rebuild that state instead of refusing reset forever.

const CORE_SCRIPT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/daemon/lesavka-core.sh"
));
const UVC_SCRIPT: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/scripts/daemon/lesavka-uvc.sh"
));

#[test]
fn core_script_rebuilds_incomplete_bound_gadgets() {
    for expected in [
        "GADGET_NEEDS_REBUILD=0",
        "gadget is configured but missing expected functions; continuing toward rebuild.",
        "GADGET_NEEDS_REBUILD=1",
        "if [[ -n $BOUND_UDC && -z $ALLOW_RESET && \"$GADGET_NEEDS_REBUILD\" != \"1\" ]]; then",
        "if [[ -z $ALLOW_RESET ]] && is_attached_state \"$UDC_STATE\" && [[ \"$GADGET_NEEDS_REBUILD\" != \"1\" ]]; then",
        "incomplete gadget detected; rebuilding even though the old gadget is still bound.",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core guardrail missing: {expected}"
        );
    }
}

#[test]
fn core_script_skips_soft_connect_for_dwc2() {
    for expected in [
        "udc_driver()",
        "udc_supports_soft_connect()",
        "[[ $driver == \"dwc2\" ]] && return 1",
        "if udc_supports_soft_connect \"$udc\"; then",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core soft_connect guard missing: {expected}"
        );
    }
}

#[test]
fn core_script_uses_kernel_doc_control_header_links() {
    for expected in [
        "pushd \"$F/control\" >/dev/null",
        "ln -s header/h class/fs",
        "ln -s header/h class/ss",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core control-header guard missing: {expected}"
        );
    }
    assert!(
        !CORE_SCRIPT.contains("mkdir -p \"$F/control/class/$s\""),
        "lesavka-core should not build the UVC control class as nested directories"
    );
}

#[test]
fn core_script_keeps_known_good_uvc_descriptor_links() {
    for expected in ["for s in fs hs ss; do", "ln -s ../../header/h h"] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core descriptor guard missing: {expected}"
        );
    }
    for unexpected in ["streaming_class_speeds", "control_class_speeds"] {
        assert!(
            !CORE_SCRIPT.contains(unexpected),
            "lesavka-core should not trim UVC descriptor links by gadget speed: {unexpected}"
        );
    }
}

#[test]
fn core_script_treats_uvc_env_as_defaults_not_overrides() {
    for expected in [
        "load_uvc_env_defaults()",
        "[[ -z ${!key+x} ]] || continue",
        "export \"$key=$value\"",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core env-default guard missing: {expected}"
        );
    }
}

#[test]
fn core_script_keeps_uvc_output_on_supported_mjpeg_descriptor() {
    for expected in [
        "UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg}",
        "UVC codec '$UVC_CODEC' is not supported by the MJPEG UVC helper; using mjpeg",
        "UVC_CODEC=mjpeg",
        "flag_enabled \"${LESAVKA_UVC_BULK:-1}\"",
        "UVC bulk requested but this kernel lacks streaming_bulk; using isochronous descriptors",
        "apply_uvc_payload_limits",
        "UVC_MAXPACKET=${LESAVKA_UVC_MAXPACKET:-1024}",
        "UVC_ADVERTISE_EXTRA_MODES=${LESAVKA_UVC_ADVERTISE_EXTRA_MODES:-0}",
        "uvc_mjpeg_frame_size_for_fps()",
        "UVC_MJPEG_BUDGET_BYTES_PER_SEC=${LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC:-4500000}",
        "UVC_ISOCHRONOUS_LIMIT_PCT=${LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT:-85}",
        "uvc_isochronous_budget_bytes_per_sec()",
        "isoch_budget=\"$(uvc_isochronous_budget_bytes_per_sec)\"",
        "UVC_DERIVED_FRAME_SIZE=\"$(uvc_mjpeg_frame_size_for_fps \"$UVC_FPS\")\"",
        "clamping requested UVC frame size $UVC_FRAME_SIZE -> $UVC_DERIVED_FRAME_SIZE (wire budget)",
        "UVC bulk request did not stick; using isochronous descriptors",
        "UVC bulk readback diverged after descriptor setup; falling back to isochronous sizing",
        "write_active_mjpeg_frame_descriptor()",
        "if flag_enabled \"$UVC_ADVERTISE_EXTRA_MODES\"; then",
        "write_mjpeg_frame_descriptor 1080p 1920 1080",
        "write_mjpeg_frame_descriptor 720p 1280 720",
        "write_active_mjpeg_frame_descriptor",
        "echo \"$(uvc_frame_size_for \"$width\" \"$height\")\" >\"$frame/dwMaxVideoFrameBufferSize\"",
        "echo \"$default_interval\" >\"$frame/dwFrameInterval\"",
        "UVC_INTERVAL_30=${LESAVKA_UVC_INTERVAL_30:-333333}",
        "UVC_INTERVAL_20=${LESAVKA_UVC_INTERVAL_20:-500000}",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core should keep UVC output on the supported MJPEG helper path: {expected}"
        );
    }
}

#[test]
fn core_script_hides_uac_hardware_mixer_controls_by_default() {
    for expected in [
        "LESAVKA_UAC_HOST_MIXER_CONTROLS:-0",
        "echo 0 >\"$U/p_volume_present\"",
        "echo 0 >\"$U/p_mute_present\"",
        "echo 0 >\"$U/c_volume_present\"",
        "echo 0 >\"$U/c_mute_present\"",
        "echo 1 >\"$U/p_volume_present\"",
        "echo 1 >\"$U/c_volume_present\"",
        "PipeWire/WirePlumber can persist empty channel maps",
    ] {
        assert!(
            CORE_SCRIPT.contains(expected),
            "lesavka-core UAC mixer guard missing: {expected}"
        );
    }
}

#[test]
fn uvc_script_migrates_starvation_prone_legacy_defaults() {
    for expected in [
        "migrate_unsafe_uvc_defaults()",
        "LESAVKA_UVC_BUFFER_COUNT:-",
        "LESAVKA_UVC_ALLOW_SINGLE_BUFFER",
        "export LESAVKA_UVC_BUFFER_COUNT=4",
        "LESAVKA_UVC_QUEUE_PACING:-",
        "LESAVKA_UVC_ALLOW_QUEUE_PACING",
        "export LESAVKA_UVC_QUEUE_PACING=0",
    ] {
        assert!(
            UVC_SCRIPT.contains(expected),
            "lesavka-uvc should migrate unsafe old defaults: {expected}"
        );
    }
}
