//! Reconnection overlay component.
//!
//! Displays a full-screen overlay when WebSocket connection is lost,
//! with countdown timer and automatic retry logic.

use leptos::prelude::*;

use super::ws_client::WsState;

/// Reconnection attempt phase.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReconnectionPhase {
    /// Initial phase: 5 attempts with 5-second countdown each.
    Initial { attempt: u8 },
    /// Extended phase: 10 attempts with 10-second countdown each.
    Extended { attempt: u8 },
    /// All attempts exhausted.
    Failed,
}

impl ReconnectionPhase {
    /// Get the countdown duration in seconds for the current phase.
    pub fn countdown_duration(&self) -> u32 {
        match self {
            Self::Initial { .. } => 5,
            Self::Extended { .. } => 10,
            Self::Failed => 0,
        }
    }

    /// Get the maximum attempts for the current phase.
    pub fn max_attempts(&self) -> u8 {
        match self {
            Self::Initial { .. } => 5,
            Self::Extended { .. } => 10,
            Self::Failed => 0,
        }
    }

    /// Advance to the next attempt or phase.
    pub fn next(self) -> Self {
        match self {
            Self::Initial { attempt } if attempt < 5 => Self::Initial { attempt: attempt + 1 },
            Self::Initial { .. } => Self::Extended { attempt: 1 },
            Self::Extended { attempt } if attempt < 10 => Self::Extended { attempt: attempt + 1 },
            Self::Extended { .. } | Self::Failed => Self::Failed,
        }
    }

    /// Get the current attempt number.
    pub fn attempt(&self) -> u8 {
        match self {
            Self::Initial { attempt } | Self::Extended { attempt } => *attempt,
            Self::Failed => 0,
        }
    }

    /// Check if this is the initial phase.
    pub fn is_initial(&self) -> bool {
        matches!(self, Self::Initial { .. })
    }
}

/// Internal state for the reconnection overlay.
#[derive(Clone, Copy, Debug)]
enum OverlayState {
    /// Hidden (connected).
    Hidden,
    /// Showing countdown.
    Countdown {
        phase: ReconnectionPhase,
        remaining: u32,
    },
    /// Currently attempting to reconnect.
    Reconnecting { phase: ReconnectionPhase },
    /// All attempts failed.
    Failed,
}

/// Reconnection overlay component.
///
/// Shows a full-screen overlay when WebSocket connection is lost,
/// with countdown timer and automatic retry logic.
#[component]
pub fn ReconnectionOverlay(
    /// WebSocket connection state to monitor.
    ws_state: Signal<WsState>,
    /// Callback to trigger a reconnection attempt.
    on_reconnect: Callback<()>,
) -> impl IntoView {
    // Internal overlay state
    let (overlay_state, set_overlay_state) = signal(OverlayState::Hidden);

    // Timer handle stored for cleanup
    #[cfg(feature = "hydrate")]
    let timer_handle: std::rc::Rc<std::cell::RefCell<Option<gloo_timers::callback::Interval>>> =
        std::rc::Rc::new(std::cell::RefCell::new(None));

    // Watch for WebSocket state changes
    #[cfg(feature = "hydrate")]
    {
        let timer_handle = timer_handle.clone();

        Effect::new(move |_| {
            let state = ws_state.get();

            match state {
                WsState::Connected => {
                    // Connection restored - hide overlay and stop timer
                    if let Some(timer) = timer_handle.borrow_mut().take() {
                        drop(timer);
                    }
                    set_overlay_state.set(OverlayState::Hidden);
                }
                WsState::Disconnected | WsState::Error => {
                    // Check current state - only start countdown if we're hidden
                    let current = overlay_state.get_untracked();
                    if matches!(current, OverlayState::Hidden) {
                        // Start initial countdown
                        let phase = ReconnectionPhase::Initial { attempt: 1 };
                        let duration = phase.countdown_duration();
                        set_overlay_state.set(OverlayState::Countdown {
                            phase,
                            remaining: duration,
                        });

                        // Start timer
                        start_countdown_timer(
                            timer_handle.clone(),
                            set_overlay_state,
                            on_reconnect.clone(),
                        );
                    } else if matches!(current, OverlayState::Reconnecting { .. }) {
                        // Reconnection attempt failed - advance to next attempt
                        if let OverlayState::Reconnecting { phase } = current {
                            let next_phase = phase.next();
                            if matches!(next_phase, ReconnectionPhase::Failed) {
                                set_overlay_state.set(OverlayState::Failed);
                            } else {
                                let duration = next_phase.countdown_duration();
                                set_overlay_state.set(OverlayState::Countdown {
                                    phase: next_phase,
                                    remaining: duration,
                                });
                                // Restart timer for next countdown
                                start_countdown_timer(
                                    timer_handle.clone(),
                                    set_overlay_state,
                                    on_reconnect.clone(),
                                );
                            }
                        }
                    }
                }
                WsState::Connecting => {
                    // Currently attempting to connect - keep current state
                    // The reconnecting state should already be set
                }
            }
        });
    }

    // Render based on state
    move || {
        let state = overlay_state.get();

        match state {
            OverlayState::Hidden => None,
            OverlayState::Countdown { phase, remaining } => {
                let (phase_text, attempt_text) = match phase {
                    ReconnectionPhase::Initial { attempt } => {
                        ("Attempt", format!("{} of 5", attempt))
                    }
                    ReconnectionPhase::Extended { attempt } => {
                        ("Extended attempt", format!("{} of 10", attempt))
                    }
                    ReconnectionPhase::Failed => ("", String::new()),
                };

                Some(
                    view! {
                        <div
                            class="fixed inset-0 z-[100] flex items-center justify-center"
                            role="alertdialog"
                            aria-modal="true"
                            aria-labelledby="reconnect-title"
                        >
                            // Darkened backdrop
                            <div
                                class="absolute inset-0 bg-black/80 backdrop-blur-md"
                                aria-hidden="true"
                            ></div>

                            // Dialog box
                            <div class="relative bg-gray-800 rounded-lg shadow-2xl max-w-md w-full mx-4 p-8 border border-gray-700 text-center">
                                // Countdown circle
                                <div class="w-20 h-20 mx-auto rounded-full bg-yellow-900/30 flex items-center justify-center mb-6">
                                    <span class="text-4xl font-bold text-yellow-300">
                                        {remaining}
                                    </span>
                                </div>

                                <h2
                                    id="reconnect-title"
                                    class="text-xl font-semibold text-white mb-2"
                                >
                                    "Lost connection..."
                                </h2>

                                <p class="text-gray-300 mb-4">
                                    {format!("attempting to reconnect in {} seconds", remaining)}
                                </p>

                                <p class="text-sm text-gray-400">
                                    {format!("{} {}", phase_text, attempt_text)}
                                </p>
                            </div>
                        </div>
                    }
                    .into_any(),
                )
            }
            OverlayState::Reconnecting { phase } => {
                let (phase_text, attempt_text) = match phase {
                    ReconnectionPhase::Initial { attempt } => {
                        ("Attempt", format!("{} of 5", attempt))
                    }
                    ReconnectionPhase::Extended { attempt } => {
                        ("Extended attempt", format!("{} of 10", attempt))
                    }
                    ReconnectionPhase::Failed => ("", String::new()),
                };

                Some(
                    view! {
                        <div
                            class="fixed inset-0 z-[100] flex items-center justify-center"
                            role="alertdialog"
                            aria-modal="true"
                            aria-labelledby="reconnect-title"
                        >
                            // Darkened backdrop
                            <div
                                class="absolute inset-0 bg-black/80 backdrop-blur-md"
                                aria-hidden="true"
                            ></div>

                            // Dialog box
                            <div class="relative bg-gray-800 rounded-lg shadow-2xl max-w-md w-full mx-4 p-8 border border-gray-700 text-center">
                                // Spinner
                                <div class="w-20 h-20 mx-auto rounded-full bg-blue-900/30 flex items-center justify-center mb-6 reconnect-spinner">
                                    <svg
                                        class="w-10 h-10 text-blue-400 animate-spin"
                                        xmlns="http://www.w3.org/2000/svg"
                                        fill="none"
                                        viewBox="0 0 24 24"
                                    >
                                        <circle
                                            class="opacity-25"
                                            cx="12"
                                            cy="12"
                                            r="10"
                                            stroke="currentColor"
                                            stroke-width="4"
                                        ></circle>
                                        <path
                                            class="opacity-75"
                                            fill="currentColor"
                                            d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
                                        ></path>
                                    </svg>
                                </div>

                                <h2
                                    id="reconnect-title"
                                    class="text-xl font-semibold text-white mb-2"
                                >
                                    "Reconnecting..."
                                </h2>

                                <p class="text-gray-300 mb-4">"Attempting to restore connection"</p>

                                <p class="text-sm text-gray-400">
                                    {format!("{} {}", phase_text, attempt_text)}
                                </p>
                            </div>
                        </div>
                    }
                    .into_any(),
                )
            }
            OverlayState::Failed => Some(
                view! {
                    <div
                        class="fixed inset-0 z-[100] flex items-center justify-center"
                        role="alertdialog"
                        aria-modal="true"
                        aria-labelledby="reconnect-title"
                    >
                        // Darkened backdrop
                        <div
                            class="absolute inset-0 bg-black/80 backdrop-blur-md"
                            aria-hidden="true"
                        ></div>

                        // Dialog box
                        <div class="relative bg-gray-800 rounded-lg shadow-2xl max-w-md w-full mx-4 p-8 border border-gray-700 text-center">
                            // Error icon
                            <div class="w-20 h-20 mx-auto rounded-full bg-red-900/30 flex items-center justify-center mb-6">
                                <svg
                                    class="w-10 h-10 text-red-400"
                                    xmlns="http://www.w3.org/2000/svg"
                                    fill="none"
                                    viewBox="0 0 24 24"
                                    stroke="currentColor"
                                >
                                    <path
                                        stroke-linecap="round"
                                        stroke-linejoin="round"
                                        stroke-width="2"
                                        d="M6 18L18 6M6 6l12 12"
                                    ></path>
                                </svg>
                            </div>

                            <h2
                                id="reconnect-title"
                                class="text-xl font-semibold text-white mb-2"
                            >
                                "Connection Failed"
                            </h2>

                            <p class="text-gray-300 mb-6">
                                "Unable to reconnect after multiple attempts. Please check your network connection and try again."
                            </p>

                            <button
                                type="button"
                                class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-800 transition-colors duration-200"
                                on:click=move |_| {
                                    #[cfg(feature = "hydrate")]
                                    {
                                        if let Some(window) = web_sys::window() {
                                            let _ = window.location().reload();
                                        }
                                    }
                                }
                            >
                                "Refresh Page"
                            </button>
                        </div>
                    </div>
                }
                .into_any(),
            ),
        }
    }
}

/// Start the countdown timer.
#[cfg(feature = "hydrate")]
fn start_countdown_timer(
    timer_handle: std::rc::Rc<std::cell::RefCell<Option<gloo_timers::callback::Interval>>>,
    set_overlay_state: WriteSignal<OverlayState>,
    on_reconnect: Callback<()>,
) {
    use gloo_timers::callback::Interval;

    // Stop any existing timer
    if let Some(old_timer) = timer_handle.borrow_mut().take() {
        drop(old_timer);
    }

    // Create new timer that ticks every second
    let timer = Interval::new(1000, move || {
        set_overlay_state.update(|state| {
            if let OverlayState::Countdown { phase, remaining } = state {
                if *remaining > 1 {
                    // Decrement countdown
                    *remaining -= 1;
                } else {
                    // Countdown reached zero - trigger reconnection
                    *state = OverlayState::Reconnecting { phase: *phase };
                    on_reconnect.run(());
                }
            }
        });
    });

    *timer_handle.borrow_mut() = Some(timer);
}
