use std::time::{Duration, Instant};

use super::{ResumeAction, RuntimeTarget};

use gdbstub::target::ext::base::multithread::MultiThreadSingleStepOps;
use gdbstub::target::ext::base::multithread::{MultiThreadResume, MultiThreadSingleStep};
use probe_rs::CoreStatus;

/// Max time to wait for a core to leave halt after resuming before we poll it.
const RESUME_SETTLE_TIMEOUT: Duration = Duration::from_millis(100);

impl MultiThreadResume for RuntimeTarget<'_> {
    fn resume(&mut self) -> Result<(), Self::Error> {
        let mut session = self.session.lock();

        match self.resume_action {
            (_, ResumeAction::Resume) => {
                for core_id in self.cores.iter() {
                    let mut core = session.core(*core_id)?;
                    core.run()?;

                    // `run()` clears the halt bit but the core may still read halted briefly;
                    // wait for it to resume so the poll loop doesn't misread that as a stop (#3965).
                    let start = Instant::now();
                    while matches!(core.status()?, CoreStatus::Halted(_)) {
                        if start.elapsed() >= RESUME_SETTLE_TIMEOUT {
                            break;
                        }
                        std::thread::sleep(Duration::from_millis(1));
                    }
                }
            }
            (core_id, ResumeAction::Step) => {
                let mut core = session.core(core_id)?;
                core.step()?;
            }
            (_, ResumeAction::Unchanged) => {}
        }

        Ok(())
    }

    fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
        self.resume_action = (0, ResumeAction::Resume);

        Ok(())
    }

    fn set_resume_action_continue(
        &mut self,
        tid: gdbstub::common::Tid,
        _signal: Option<gdbstub::common::Signal>,
    ) -> Result<(), Self::Error> {
        let core_id = tid.get() - 1;
        self.resume_action = (core_id, ResumeAction::Resume);

        Ok(())
    }

    fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> {
        Some(self)
    }
}

impl MultiThreadSingleStep for RuntimeTarget<'_> {
    fn set_resume_action_step(
        &mut self,
        tid: gdbstub::common::Tid,
        _signal: Option<gdbstub::common::Signal>,
    ) -> Result<(), Self::Error> {
        let core_id = tid.get() - 1;
        self.resume_action = (core_id, ResumeAction::Step);

        Ok(())
    }
}
