use provctl_core::{ProcessStatus, ProvctlResult, ServiceDefinition};

/// Trait for service management backends.
///
/// Implementations of this trait abstract over different service management systems
/// (systemd, launchd, pidfile, etc.) and provide a unified interface for provctl.
///
/// All methods are async to support non-blocking I/O operations.
///
/// # Examples
///
/// ```ignore
/// use provctl_backend::Backend;
/// use provctl_core::{ServiceName, ServiceDefinition};
/// use std::path::PathBuf;
///
/// async fn manage_service<B: Backend + Send + Sync>(
///     backend: &B,
///     service: &ServiceDefinition,
/// ) -> ProvctlResult<()> {
///     let pid = backend.start(service).await?;
///     println!("Service started with PID: {}", pid);
///
///     let status = backend.status(&service.name).await?;
///     println!("Status: {:?}", status);
///
///     backend.stop(&service.name).await?;
///     Ok(())
/// }
/// ```
#[async_trait::async_trait]
pub trait Backend: Send + Sync {
    /// Starts a service.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The service is already running
    /// - The binary does not exist
    /// - Permission denied to execute binary
    /// - Process spawning failed
    async fn start(&self, service: &ServiceDefinition) -> ProvctlResult<u32>;

    /// Stops a running service.
    ///
    /// Sends SIGTERM to the process. If the process doesn't stop within
    /// a reasonable timeout (see `Defaults::spawn_timeout_secs`), sends SIGKILL.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The service is not running
    /// - Permission denied to stop process
    /// - Process termination failed
    async fn stop(&self, service_name: &str) -> ProvctlResult<()>;

    /// Gets the current status of a service.
    ///
    /// # Errors
    ///
    /// Returns an error if status cannot be determined.
    async fn status(&self, service_name: &str) -> ProvctlResult<ProcessStatus>;

    /// Restarts a service (stop + start).
    ///
    /// # Errors
    ///
    /// Returns an error if stop or start fails.
    async fn restart(&self, service: &ServiceDefinition) -> ProvctlResult<u32> {
        self.stop(&service.name.to_string()).await?;
        self.start(service).await
    }

    /// Gets logs for a service.
    ///
    /// Returns the last `lines` lines of the service log, or all available lines
    /// if there are fewer than requested.
    ///
    /// # Errors
    ///
    /// Returns an error if log file cannot be read.
    async fn logs(&self, service_name: &str, lines: usize) -> ProvctlResult<Vec<String>>;
}

#[cfg(test)]
pub mod test_helpers {
    use super::*;
    use std::sync::{Arc, Mutex};

    /// Mock backend for testing (feature-gated).
    ///
    /// Follows M-TEST-UTIL-FEATURE-GATED pattern.
    pub struct MockBackend {
        pub running_services: Arc<Mutex<std::collections::HashMap<String, u32>>>,
    }

    impl MockBackend {
        /// Creates a new mock backend.
        pub fn new() -> Self {
            Self {
                running_services: Arc::new(Mutex::new(std::collections::HashMap::new())),
            }
        }

        /// Gets the current running services for assertions.
        pub fn get_running(&self) -> std::collections::HashMap<String, u32> {
            self.running_services.lock().unwrap().clone()
        }
    }

    impl Default for MockBackend {
        fn default() -> Self {
            Self::new()
        }
    }

    #[async_trait::async_trait]
    impl Backend for MockBackend {
        async fn start(&self, service: &ServiceDefinition) -> ProvctlResult<u32> {
            let mut running = self.running_services.lock().unwrap();
            if running.contains_key(service.name.as_str()) {
                return Err(provctl_core::ProvctlError::new(
                    provctl_core::ProvctlErrorKind::AlreadyRunning {
                        service: service.name.to_string(),
                        pid: running[service.name.as_str()],
                    },
                    "Starting service".to_string(),
                ));
            }

            let mock_pid = 1000 + running.len() as u32;
            running.insert(service.name.to_string(), mock_pid);
            Ok(mock_pid)
        }

        async fn stop(&self, service_name: &str) -> ProvctlResult<()> {
            let mut running = self.running_services.lock().unwrap();
            if !running.contains_key(service_name) {
                return Err(provctl_core::ProvctlError::new(
                    provctl_core::ProvctlErrorKind::NotRunning(service_name.to_string()),
                    "Stopping service".to_string(),
                ));
            }

            running.remove(service_name);
            Ok(())
        }

        async fn status(&self, service_name: &str) -> ProvctlResult<ProcessStatus> {
            let running = self.running_services.lock().unwrap();
            if let Some(pid) = running.get(service_name) {
                Ok(ProcessStatus::Running { pid: *pid })
            } else {
                Ok(ProcessStatus::NotRunning)
            }
        }

        async fn logs(&self, _service_name: &str, _lines: usize) -> ProvctlResult<Vec<String>> {
            Ok(vec![
                "Mock log line 1".to_string(),
                "Mock log line 2".to_string(),
            ])
        }
    }
}
