// RPC reset coverage for server main USB recovery replies.
//
// Scope: include `server/src/main.rs` and exercise reset RPC edge replies.
// Targets: `server/src/main.rs`.
// Why: USB reset is an operator recovery path, so failed HID reopen behavior
// needs deterministic coverage without requiring real gadget hardware.

#[allow(warnings)]
mod server_main_rpc_reset {
    include!(env!("LESAVKA_SERVER_MAIN_SRC"));

    use serial_test::serial;
    use temp_env::with_var;
    use tempfile::tempdir;

    fn build_handler_for_tests() -> (tempfile::TempDir, Handler) {
        let dir = tempdir().expect("tempdir");
        let kb_path = dir.path().join("hidg0.bin");
        let ms_path = dir.path().join("hidg1.bin");
        std::fs::write(&kb_path, []).expect("create kb file");
        std::fs::write(&ms_path, []).expect("create ms file");

        let kb = tokio::fs::File::from_std(
            std::fs::OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .truncate(true)
                .open(&kb_path)
                .expect("open kb"),
        );
        let ms = tokio::fs::File::from_std(
            std::fs::OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .truncate(true)
                .open(&ms_path)
                .expect("open ms"),
        );

        let handler = with_var("LESAVKA_CAPTURE_POWER_UNIT", Some("none"), || Handler {
            kb: std::sync::Arc::new(tokio::sync::Mutex::new(Some(kb))),
            ms: std::sync::Arc::new(tokio::sync::Mutex::new(Some(ms))),
            gadget: UsbGadget::new("lesavka"),
            did_cycle: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
            camera_rt: std::sync::Arc::new(CameraRuntime::new()),
            upstream_media_rt: std::sync::Arc::new(UpstreamMediaRuntime::new()),
            calibration: std::sync::Arc::new(CalibrationStore::load(std::sync::Arc::new(
                UpstreamMediaRuntime::new(),
            ))),
            capture_power: CapturePowerManager::new(),
            eye_hubs: std::sync::Arc::new(
                tokio::sync::Mutex::new(std::collections::HashMap::new()),
            ),
        });

        (dir, handler)
    }

    #[test]
    #[cfg(coverage)]
    #[serial]
    fn reset_usb_tolerates_missing_hid_after_successful_cycle() {
        let dir = tempdir().expect("tempdir");
        std::fs::write(dir.path().join("hidg0.bin"), "").expect("create kb file");
        std::fs::write(dir.path().join("hidg1.bin"), "").expect("create ms file");
        std::fs::create_dir_all(dir.path().join("sys/class/udc/fake-ctrl.usb"))
            .expect("create udc dir");
        std::fs::create_dir_all(dir.path().join("cfg/lesavka")).expect("create cfg dir");
        std::fs::write(
            dir.path().join("sys/class/udc/fake-ctrl.usb/state"),
            "configured\n",
        )
        .expect("write state");
        std::fs::write(dir.path().join("cfg/lesavka/UDC"), "fake-ctrl.usb\n").expect("write udc");

        let kb = tokio::fs::File::from_std(
            std::fs::OpenOptions::new()
                .read(true)
                .write(true)
                .open(dir.path().join("hidg0.bin"))
                .expect("open kb"),
        );
        let ms = tokio::fs::File::from_std(
            std::fs::OpenOptions::new()
                .read(true)
                .write(true)
                .open(dir.path().join("hidg1.bin"))
                .expect("open ms"),
        );

        with_var(
            "LESAVKA_GADGET_SYSFS_ROOT",
            Some(dir.path().join("sys").to_string_lossy().to_string()),
            || {
                with_var(
                    "LESAVKA_GADGET_CONFIGFS_ROOT",
                    Some(dir.path().join("cfg").to_string_lossy().to_string()),
                    || {
                        let handler = Handler {
                            kb: std::sync::Arc::new(tokio::sync::Mutex::new(Some(kb))),
                            ms: std::sync::Arc::new(tokio::sync::Mutex::new(Some(ms))),
                            gadget: UsbGadget::new("lesavka"),
                            did_cycle: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
                                false,
                            )),
                            camera_rt: std::sync::Arc::new(CameraRuntime::new()),
                            upstream_media_rt: std::sync::Arc::new(UpstreamMediaRuntime::new()),
                            calibration: std::sync::Arc::new(CalibrationStore::load(
                                std::sync::Arc::new(UpstreamMediaRuntime::new()),
                            )),
                            capture_power: CapturePowerManager::new(),
                            eye_hubs: std::sync::Arc::new(tokio::sync::Mutex::new(
                                std::collections::HashMap::new(),
                            )),
                        };

                        with_var(
                            "LESAVKA_HID_DIR",
                            Some(dir.path().join("missing").to_string_lossy().to_string()),
                            || {
                                let rt = tokio::runtime::Runtime::new().expect("runtime");
                                let reply = rt
                                    .block_on(async {
                                        handler.reset_usb(tonic::Request::new(Empty {})).await
                                    })
                                    .expect("missing HID should not fail USB reset")
                                    .into_inner();
                                assert!(reply.ok);
                            },
                        );
                    },
                );
            },
        );
    }

    #[test]
    #[cfg(coverage)]
    #[serial]
    fn reset_usb_reports_reopen_hid_failure_after_successful_cycle() {
        let dir = tempdir().expect("tempdir");
        std::fs::create_dir_all(dir.path().join("bad-hid/hidg0")).expect("create bad hidg0 dir");
        std::fs::write(dir.path().join("bad-hid/hidg1"), "").expect("create hidg1 file");
        std::fs::create_dir_all(dir.path().join("sys/class/udc/fake-ctrl.usb"))
            .expect("create udc dir");
        std::fs::create_dir_all(dir.path().join("cfg/lesavka")).expect("create cfg dir");
        std::fs::write(
            dir.path().join("sys/class/udc/fake-ctrl.usb/state"),
            "configured\n",
        )
        .expect("write state");
        std::fs::write(dir.path().join("cfg/lesavka/UDC"), "fake-ctrl.usb\n").expect("write udc");

        let (_hid_dir, handler) = build_handler_for_tests();
        with_var(
            "LESAVKA_GADGET_SYSFS_ROOT",
            Some(dir.path().join("sys").to_string_lossy().to_string()),
            || {
                with_var(
                    "LESAVKA_GADGET_CONFIGFS_ROOT",
                    Some(dir.path().join("cfg").to_string_lossy().to_string()),
                    || {
                        with_var(
                            "LESAVKA_HID_DIR",
                            Some(dir.path().join("bad-hid").to_string_lossy().to_string()),
                            || {
                                let rt = tokio::runtime::Runtime::new().expect("runtime");
                                let err = rt
                                    .block_on(async {
                                        handler.reset_usb(tonic::Request::new(Empty {})).await
                                    })
                                    .expect_err("bad HID path should fail reopen");
                                assert_eq!(err.code(), tonic::Code::Internal);
                                assert!(
                                    err.message().contains("opening")
                                        && err.message().contains("bad-hid/hidg0"),
                                    "unexpected reopen error: {}",
                                    err.message()
                                );
                            },
                        );
                    },
                );
            },
        );
    }
}
