//! Mothership lifecycle state machine
//!
//! Tracks the overall application lifecycle from initialization through shutdown,
//! including all intermediate stages like preflight, prelaunch, and fleet operations.

use state_machines::state_machine;
use std::fmt;

/// Current status of the Mothership application
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MothershipStatus {
    /// Loading manifest, parsing config
    #[default]
    Initializing,
    /// Verifying uplinks and external dependencies
    Preflight,
    /// Flagship coordination / leader election
    Electing,
    /// Running prelaunch jobs (migrations, etc.)
    Prelaunch,
    /// Bays starting up and establishing sockets
    Docking,
    /// Ships starting in dependency order
    Launching,
    /// Normal operation, monitoring fleet
    Running,
    /// Distress - attempting recovery after critical issues
    Mayday,
    /// Graceful shutdown / teleport handoff in progress
    Draining,
    /// Mission complete, clean shutdown
    Landed,
    /// Startup failure - never got off the ground
    Failed,
    /// Runtime failure - was flying, then died
    Crashed,
}

impl MothershipStatus {
    fn from_str(s: &str) -> Self {
        match s {
            "Initializing" => MothershipStatus::Initializing,
            "Preflight" => MothershipStatus::Preflight,
            "Electing" => MothershipStatus::Electing,
            "Prelaunch" => MothershipStatus::Prelaunch,
            "Docking" => MothershipStatus::Docking,
            "Launching" => MothershipStatus::Launching,
            "Running" => MothershipStatus::Running,
            "Mayday" => MothershipStatus::Mayday,
            "Draining" => MothershipStatus::Draining,
            "Landed" => MothershipStatus::Landed,
            "Failed" => MothershipStatus::Failed,
            "Crashed" => MothershipStatus::Crashed,
            _ => MothershipStatus::Initializing,
        }
    }
}

impl fmt::Display for MothershipStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MothershipStatus::Initializing => write!(f, "initializing"),
            MothershipStatus::Preflight => write!(f, "preflight"),
            MothershipStatus::Electing => write!(f, "electing"),
            MothershipStatus::Prelaunch => write!(f, "prelaunch"),
            MothershipStatus::Docking => write!(f, "docking"),
            MothershipStatus::Launching => write!(f, "launching"),
            MothershipStatus::Running => write!(f, "running"),
            MothershipStatus::Mayday => write!(f, "mayday"),
            MothershipStatus::Draining => write!(f, "draining"),
            MothershipStatus::Landed => write!(f, "landed"),
            MothershipStatus::Failed => write!(f, "failed"),
            MothershipStatus::Crashed => write!(f, "crashed"),
        }
    }
}

state_machine! {
    name: MothershipLifecycle,
    context: (),
    dynamic: true,

    initial: Initializing,
    states: [
        Initializing,
        Preflight,
        Electing,
        Prelaunch,
        Docking,
        Launching,
        Running,
        Mayday,
        Draining,
        Landed,
        Failed,
        Crashed,
    ],
    events {
        // Normal flow transitions
        manifest_loaded {
            transition: { from: Initializing, to: Preflight }
        }
        preflight_complete {
            transition: { from: Preflight, to: Electing }
        }
        preflight_skipped {
            transition: { from: [Initializing, Preflight], to: Electing }
        }
        election_complete {
            transition: { from: Electing, to: Prelaunch }
        }
        election_skipped {
            transition: { from: [Preflight, Initializing, Electing], to: Prelaunch }
        }
        prelaunch_complete {
            transition: { from: Prelaunch, to: Docking }
        }
        prelaunch_skipped {
            transition: { from: [Electing, Preflight, Prelaunch], to: Docking }
        }
        docking_complete {
            transition: { from: Docking, to: Launching }
        }
        docking_skipped {
            transition: { from: [Prelaunch, Electing, Docking], to: Launching }
        }
        launch_complete {
            transition: { from: Launching, to: Running }
        }
        drain_started {
            transition: { from: [Running, Mayday], to: Draining }
        }
        drain_complete {
            transition: { from: Draining, to: Landed }
        }

        // Mayday - distress and recovery
        distress {
            transition: { from: Running, to: Mayday }
        }
        stabilized {
            transition: { from: Mayday, to: Running }
        }

        // Startup failure transitions (never got off the ground)
        preflight_failed {
            transition: { from: Preflight, to: Failed }
        }
        election_failed {
            transition: { from: Electing, to: Failed }
        }
        prelaunch_failed {
            transition: { from: Prelaunch, to: Failed }
        }
        docking_failed {
            transition: { from: Docking, to: Failed }
        }
        launch_failed {
            transition: { from: Launching, to: Failed }
        }

        // Runtime crash (was flying, then died)
        crash {
            transition: { from: [Running, Mayday, Draining], to: Crashed }
        }

        // Universal transitions
        shutdown {
            transition: { from: [Initializing, Preflight, Electing, Prelaunch, Docking, Launching, Running, Mayday, Draining], to: Landed }
        }
        abort {
            transition: { from: [Initializing, Preflight, Electing, Prelaunch, Docking, Launching], to: Failed }
        }
    }
}

/// Mothership lifecycle coordinator
///
/// Manages the overall application state and provides methods
/// for transitioning through startup stages.
pub struct Lifecycle {
    machine: DynamicMothershipLifecycle,
    status: MothershipStatus,
}

/// Generate a lifecycle transition method that handles an event and refreshes status
macro_rules! transition_method {
    ($(#[$meta:meta])* $method:ident, $event:ident) => {
        $(#[$meta])*
        pub fn $method(&mut self) -> bool {
            if self.machine.handle(MothershipLifecycleEvent::$event).is_ok() {
                self.refresh_status();
                true
            } else {
                false
            }
        }
    };
}

impl Lifecycle {
    /// Create a new lifecycle in Initializing state
    pub fn new() -> Self {
        let machine = DynamicMothershipLifecycle::new(());
        let status = MothershipStatus::from_str(machine.current_state());
        Self { machine, status }
    }

    /// Get current status
    pub fn status(&self) -> MothershipStatus {
        self.status
    }

    /// Refresh cached status from machine
    fn refresh_status(&mut self) {
        self.status = MothershipStatus::from_str(self.machine.current_state());
    }

    // === Normal flow transitions ===

    transition_method!(
        /// Manifest has been loaded, start preflight checks
        manifest_loaded, ManifestLoaded
    );
    transition_method!(
        /// Preflight checks completed successfully
        preflight_complete, PreflightComplete
    );
    transition_method!(
        /// Preflight checks skipped (not configured or CLI override)
        preflight_skipped, PreflightSkipped
    );
    transition_method!(
        /// Flagship election completed
        election_complete, ElectionComplete
    );
    transition_method!(
        /// Flagship election skipped (single-server mode)
        election_skipped, ElectionSkipped
    );
    transition_method!(
        /// Prelaunch jobs completed successfully
        prelaunch_complete, PrelaunchComplete
    );
    transition_method!(
        /// Prelaunch skipped (escort instance or no jobs configured)
        prelaunch_skipped, PrelaunchSkipped
    );
    transition_method!(
        /// All bays have docked successfully
        docking_complete, DockingComplete
    );
    transition_method!(
        /// Docking skipped (no bays configured)
        docking_skipped, DockingSkipped
    );
    transition_method!(
        /// All ships launched and healthy
        launch_complete, LaunchComplete
    );
    transition_method!(
        /// Begin graceful drain (shutdown or teleport)
        drain_started, DrainStarted
    );
    transition_method!(
        /// Drain completed, ready to land
        drain_complete, DrainComplete
    );

    // === Mayday transitions ===

    transition_method!(
        /// Enter distress mode (critical issues detected)
        distress, Distress
    );
    transition_method!(
        /// Recovered from distress, back to normal operation
        stabilized, Stabilized
    );

    // === Startup failure transitions ===

    transition_method!(
        /// Preflight checks failed
        preflight_failed, PreflightFailed
    );
    transition_method!(
        /// Flagship election failed
        election_failed, ElectionFailed
    );
    transition_method!(
        /// Prelaunch jobs failed
        prelaunch_failed, PrelaunchFailed
    );
    transition_method!(
        /// Bay docking failed
        docking_failed, DockingFailed
    );
    transition_method!(
        /// Ship launch failed
        launch_failed, LaunchFailed
    );

    // === Runtime crash ===

    transition_method!(
        /// Runtime crash (was flying, then died)
        crash, Crash
    );

    // === Universal transitions ===

    transition_method!(
        /// Graceful shutdown from any state -> Landed
        shutdown, Shutdown
    );
    transition_method!(
        /// Abort during startup -> Failed
        abort, Abort
    );

    // === State queries ===

    /// Check if currently running (normal operation)
    pub fn is_running(&self) -> bool {
        self.status == MothershipStatus::Running
    }

    /// Check if in mayday (distress, attempting recovery)
    pub fn is_mayday(&self) -> bool {
        self.status == MothershipStatus::Mayday
    }

    /// Check if operational (running or recovering)
    pub fn is_operational(&self) -> bool {
        matches!(
            self.status,
            MothershipStatus::Running | MothershipStatus::Mayday
        )
    }

    /// Check if in terminal state (Landed, Failed, or Crashed)
    pub fn is_terminal(&self) -> bool {
        matches!(
            self.status,
            MothershipStatus::Landed | MothershipStatus::Failed | MothershipStatus::Crashed
        )
    }

    /// Check if startup is in progress
    pub fn is_starting(&self) -> bool {
        matches!(
            self.status,
            MothershipStatus::Initializing
                | MothershipStatus::Preflight
                | MothershipStatus::Electing
                | MothershipStatus::Prelaunch
                | MothershipStatus::Docking
                | MothershipStatus::Launching
        )
    }

    /// Check if landed successfully
    pub fn is_landed(&self) -> bool {
        self.status == MothershipStatus::Landed
    }

    /// Check if crashed (runtime failure)
    pub fn is_crashed(&self) -> bool {
        self.status == MothershipStatus::Crashed
    }

    /// Check if failed (startup failure)
    pub fn is_failed(&self) -> bool {
        self.status == MothershipStatus::Failed
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_initial_state() {
        let lifecycle = Lifecycle::new();
        assert_eq!(lifecycle.status(), MothershipStatus::Initializing);
        assert!(lifecycle.is_starting());
        assert!(!lifecycle.is_running());
        assert!(!lifecycle.is_terminal());
    }

    #[test]
    fn test_normal_flow() {
        let mut lifecycle = Lifecycle::new();

        // Full startup sequence
        assert!(lifecycle.manifest_loaded());
        assert_eq!(lifecycle.status(), MothershipStatus::Preflight);

        assert!(lifecycle.preflight_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Electing);

        assert!(lifecycle.election_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Prelaunch);

        assert!(lifecycle.prelaunch_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Docking);

        assert!(lifecycle.docking_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Launching);

        assert!(lifecycle.launch_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Running);
        assert!(lifecycle.is_running());
        assert!(lifecycle.is_operational());

        // Graceful shutdown
        assert!(lifecycle.drain_started());
        assert_eq!(lifecycle.status(), MothershipStatus::Draining);

        assert!(lifecycle.drain_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Landed);
        assert!(lifecycle.is_terminal());
        assert!(lifecycle.is_landed());
    }

    #[test]
    fn test_skip_preflight() {
        let mut lifecycle = Lifecycle::new();

        assert!(lifecycle.preflight_skipped());
        assert_eq!(lifecycle.status(), MothershipStatus::Electing);
    }

    #[test]
    fn test_skip_election() {
        let mut lifecycle = Lifecycle::new();

        assert!(lifecycle.manifest_loaded());
        assert!(lifecycle.election_skipped());
        assert_eq!(lifecycle.status(), MothershipStatus::Prelaunch);
    }

    #[test]
    fn test_skip_prelaunch() {
        let mut lifecycle = Lifecycle::new();

        assert!(lifecycle.preflight_skipped());
        assert!(lifecycle.election_complete());
        assert!(lifecycle.prelaunch_skipped());
        assert_eq!(lifecycle.status(), MothershipStatus::Docking);
    }

    #[test]
    fn test_preflight_failure() {
        let mut lifecycle = Lifecycle::new();

        assert!(lifecycle.manifest_loaded());
        assert!(lifecycle.preflight_failed());
        assert_eq!(lifecycle.status(), MothershipStatus::Failed);
        assert!(lifecycle.is_terminal());
        assert!(lifecycle.is_failed());
    }

    #[test]
    fn test_prelaunch_failure() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.preflight_skipped();
        lifecycle.election_complete();
        assert!(lifecycle.prelaunch_failed());
        assert_eq!(lifecycle.status(), MothershipStatus::Failed);
    }

    #[test]
    fn test_mayday_and_recovery() {
        let mut lifecycle = Lifecycle::new();

        // Get to running state
        lifecycle.preflight_skipped();
        lifecycle.election_skipped();
        lifecycle.prelaunch_skipped();
        lifecycle.docking_complete();
        lifecycle.launch_complete();
        assert!(lifecycle.is_running());

        // Enter mayday
        assert!(lifecycle.distress());
        assert_eq!(lifecycle.status(), MothershipStatus::Mayday);
        assert!(lifecycle.is_mayday());
        assert!(lifecycle.is_operational()); // Still operational!
        assert!(!lifecycle.is_running());

        // Stabilize
        assert!(lifecycle.stabilized());
        assert_eq!(lifecycle.status(), MothershipStatus::Running);
        assert!(lifecycle.is_running());
    }

    #[test]
    fn test_crash_from_running() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.preflight_skipped();
        lifecycle.election_skipped();
        lifecycle.prelaunch_skipped();
        lifecycle.docking_complete();
        lifecycle.launch_complete();

        assert!(lifecycle.is_running());
        assert!(lifecycle.crash());
        assert_eq!(lifecycle.status(), MothershipStatus::Crashed);
        assert!(lifecycle.is_crashed());
        assert!(lifecycle.is_terminal());
    }

    #[test]
    fn test_crash_from_mayday() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.preflight_skipped();
        lifecycle.election_skipped();
        lifecycle.prelaunch_skipped();
        lifecycle.docking_complete();
        lifecycle.launch_complete();
        lifecycle.distress();

        assert!(lifecycle.is_mayday());
        assert!(lifecycle.crash());
        assert_eq!(lifecycle.status(), MothershipStatus::Crashed);
    }

    #[test]
    fn test_drain_from_mayday() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.preflight_skipped();
        lifecycle.election_skipped();
        lifecycle.prelaunch_skipped();
        lifecycle.docking_complete();
        lifecycle.launch_complete();
        lifecycle.distress();

        // Can still drain from mayday
        assert!(lifecycle.drain_started());
        assert_eq!(lifecycle.status(), MothershipStatus::Draining);
    }

    #[test]
    fn test_abort_during_startup() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.manifest_loaded();
        assert!(lifecycle.abort());
        assert_eq!(lifecycle.status(), MothershipStatus::Failed);
        assert!(lifecycle.is_failed());
    }

    #[test]
    fn test_shutdown_from_any_state() {
        let mut lifecycle = Lifecycle::new();

        lifecycle.manifest_loaded();
        assert!(lifecycle.shutdown());
        assert_eq!(lifecycle.status(), MothershipStatus::Landed);
        assert!(lifecycle.is_landed());
    }

    #[test]
    fn test_invalid_transition() {
        let mut lifecycle = Lifecycle::new();

        // Can't complete preflight without loading manifest first
        assert!(!lifecycle.preflight_complete());
        assert_eq!(lifecycle.status(), MothershipStatus::Initializing);
    }

    #[test]
    fn test_status_display() {
        assert_eq!(MothershipStatus::Running.to_string(), "running");
        assert_eq!(MothershipStatus::Preflight.to_string(), "preflight");
        assert_eq!(MothershipStatus::Draining.to_string(), "draining");
        assert_eq!(MothershipStatus::Mayday.to_string(), "mayday");
        assert_eq!(MothershipStatus::Landed.to_string(), "landed");
        assert_eq!(MothershipStatus::Crashed.to_string(), "crashed");
    }
}
