use crate::curuntime::KeyFrame;
use cu29_traits::CopperListTuple;
use cu29_traits::CuResult;
use cu29_unifiedlog::{SectionStorage, UnifiedLogWrite};

#[cfg(feature = "std")]
use crate::copperlist::CopperList;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use cu29_clock::RobotClockMock;
#[cfg(feature = "std")]
use std::vec::Vec;

#[cfg(not(feature = "std"))]
mod imp {
    pub use alloc::string::String;
}

#[cfg(feature = "std")]
mod imp {
    pub use crate::config::CuConfig;
    pub use crate::simulation::SimOverride;
    pub use cu29_clock::RobotClock;
    pub use cu29_unifiedlog::memmap::MmapSectionStorage;
    pub use std::sync::{Arc, Mutex};
}

use imp::*;

/// Convenience trait for CuApplication when it is just a std App
#[cfg(feature = "std")]
pub trait CuStdApplication:
    CuApplication<MmapSectionStorage, cu29_unifiedlog::UnifiedLoggerWrite>
{
}

#[cfg(feature = "std")]
impl<T> CuStdApplication for T where
    T: CuApplication<MmapSectionStorage, cu29_unifiedlog::UnifiedLoggerWrite>
{
}

/// Compile-time subsystem identity embedded in generated Copper applications.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Subsystem {
    id: Option<&'static str>,
    code: u16,
}

impl Subsystem {
    #[inline]
    pub const fn new(id: Option<&'static str>, code: u16) -> Self {
        Self { id, code }
    }

    #[inline]
    pub const fn id(self) -> Option<&'static str> {
        self.id
    }

    #[inline]
    pub const fn code(self) -> u16 {
        self.code
    }
}

/// Compile-time subsystem identity embedded in generated Copper applications.
pub trait CuSubsystemMetadata {
    /// Multi-Copper subsystem identity for this generated application.
    fn subsystem() -> Subsystem;
}

/// A trait that defines the structure and behavior of a CuApplication.
///
/// CuApplication is the normal, running on robot version of an application and its runtime.
///
/// The `CuApplication` trait outlines the necessary functions required for managing an application lifecycle,
/// including configuration management, initialization, task execution, and runtime control. It is meant to be
/// implemented by types that represent specific applications, providing them with unified control and execution features.
///
/// This is the more generic version that allows you to specify a custom unified logger.
pub trait CuApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static> {
    /// Returns the original configuration as a string, typically loaded from a RON file.
    /// This configuration represents the default settings for the application before any overrides.
    fn get_original_config() -> String;

    /// Starts all tasks managed by the application/runtime.
    ///
    /// # Returns
    /// * `Ok(())` - If all tasks are started successfully.
    /// * `Err(CuResult)` - If an error occurs while attempting to start one
    ///   or more tasks.
    fn start_all_tasks(&mut self) -> CuResult<()>;

    /// Executes a single iteration of copper-generated runtime (generating and logging one copperlist)
    ///
    /// # Returns
    ///
    /// * `CuResult<()>` - Returns `Ok(())` if the iteration completes successfully, or an error
    ///   wrapped in `CuResult` if something goes wrong during execution.
    ///
    fn run_one_iteration(&mut self) -> CuResult<()>;

    /// Runs indefinitely looping over run_one_iteration
    ///
    /// # Returns
    ///
    /// Returns a `CuResult<()>`, which indicates the success or failure of the
    /// operation.
    /// - On success, the result is `Ok(())`.
    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
    fn run(&mut self) -> CuResult<()>;

    /// Stops all tasks managed by the application/runtime.
    ///
    /// # Returns
    ///
    /// Returns a `CuResult<()>`, which indicates the success or failure of the
    /// operation.
    /// - On success, the result is `Ok(())`.
    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
    ///
    fn stop_all_tasks(&mut self) -> CuResult<()>;

    /// Restore all tasks from the given frozen state
    fn restore_keyframe(&mut self, freezer: &KeyFrame) -> CuResult<()>;
}

/// A trait that defines the structure and behavior of a simulation-enabled CuApplication.
///
/// CuSimApplication is the simulation version of an application and its runtime, allowing
/// overriding of steps with simulated behavior.
///
/// The `CuSimApplication` trait outlines the necessary functions required for managing an application lifecycle
/// in simulation mode, including configuration management, initialization, task execution, and runtime control.
#[cfg(feature = "std")]
pub trait CuSimApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static> {
    /// The type representing a simulation step that can be overridden
    type Step<'z>;

    /// Returns the original configuration as a string, typically loaded from a RON file.
    /// This configuration represents the default settings for the application before any overrides.
    fn get_original_config() -> String;

    /// Returns the mission id this generated application is bound to, when applicable.
    fn mission_id() -> Option<&'static str> {
        None
    }

    /// Starts all tasks managed by the application/runtime in simulation mode.
    ///
    /// # Arguments
    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
    ///
    /// # Returns
    /// * `Ok(())` - If all tasks are started successfully.
    /// * `Err(CuResult)` - If an error occurs while attempting to start one
    ///   or more tasks.
    fn start_all_tasks(
        &mut self,
        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
    ) -> CuResult<()>;

    /// Executes a single iteration of copper-generated runtime in simulation mode.
    ///
    /// # Arguments
    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
    ///
    /// # Returns
    ///
    /// * `CuResult<()>` - Returns `Ok(())` if the iteration completes successfully, or an error
    ///   wrapped in `CuResult` if something goes wrong during execution.
    fn run_one_iteration(
        &mut self,
        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
    ) -> CuResult<()>;

    /// Runs indefinitely looping over run_one_iteration in simulation mode
    ///
    /// # Arguments
    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
    ///
    /// # Returns
    ///
    /// Returns a `CuResult<()>`, which indicates the success or failure of the
    /// operation.
    /// - On success, the result is `Ok(())`.
    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
    fn run(
        &mut self,
        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
    ) -> CuResult<()>;

    /// Stops all tasks managed by the application/runtime in simulation mode.
    ///
    /// # Arguments
    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
    ///
    /// # Returns
    ///
    /// Returns a `CuResult<()>`, which indicates the success or failure of the
    /// operation.
    /// - On success, the result is `Ok(())`.
    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
    fn stop_all_tasks(
        &mut self,
        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
    ) -> CuResult<()>;

    /// Restore all tasks from the given frozen state
    fn restore_keyframe(&mut self, freezer: &KeyFrame) -> CuResult<()>;
}

/// Optional introspection hook exposing the latest runtime-generated CopperList snapshot.
///
/// This is remote-debug-only: debugger conveniences must not add unconditional
/// runtime-path overhead to normal Copper builds. Non-`remote-debug` builds
/// should implement this as a cheap `None`.
pub trait CurrentRuntimeCopperList<P: CopperListTuple> {
    fn current_runtime_copperlist_bytes(&self) -> Option<&[u8]>;

    fn set_current_runtime_copperlist_bytes(&mut self, snapshot: Option<Vec<u8>>) {
        let _ = snapshot;
    }
}

/// Simulation-enabled applications that can replay a recorded CopperList verbatim.
///
/// This is the exact-output replay primitive used by deterministic re-sim flows:
/// task outputs and bridge receives are overridden from the recorded CopperList,
/// bridge sends are skipped, and an optional recorded keyframe can be injected
/// verbatim when the current CL is expected to capture one.
#[cfg(feature = "std")]
pub trait CuRecordedReplayApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static>:
    CuSimApplication<S, L>
{
    /// The generated recorded CopperList payload set for this application.
    type RecordedDataSet: CopperListTuple;

    /// Replay one recorded CopperList exactly as logged.
    fn replay_recorded_copperlist(
        &mut self,
        clock_mock: &RobotClockMock,
        copperlist: &CopperList<Self::RecordedDataSet>,
        keyframe: Option<&KeyFrame>,
    ) -> CuResult<()>;
}

/// Simulation-enabled applications that can be instantiated for distributed replay.
///
/// This extends exact-output replay with the one extra capability the
/// distributed engine needs: build a replayable app for a specific
/// deployment `instance_id` while keeping app construction type-safe.
#[cfg(feature = "std")]
pub trait CuDistributedReplayApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static>:
    CuRecordedReplayApplication<S, L> + CuSubsystemMetadata
{
    /// Build this app for deterministic distributed replay.
    fn build_distributed_replay(
        clock: RobotClock,
        unified_logger: Arc<Mutex<L>>,
        instance_id: u32,
        config_override: Option<CuConfig>,
    ) -> CuResult<Self>
    where
        Self: Sized;
}
