//! High-level subscription API for WebSocket status updates.

use super::messages::StatusUpdate;
#[allow(unused_imports)]
use super::transport::WebSocketTransport;
use crate::config::WebSocketConfig;
use crate::error::{LastIDError, WebSocketError};
use crate::types::{RequestId, RequestStatus};

/// Get current timestamp in ISO 8601 format.
#[cfg(not(target_arch = "wasm32"))]
fn now_iso8601() -> String {
    use std::time::{SystemTime, UNIX_EPOCH};
    let duration = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default();
    // Format as ISO 8601 with milliseconds
    let secs = duration.as_secs();
    let millis = duration.subsec_millis();
    format!("{}.{:03}Z", chrono_lite(secs), millis)
}

#[cfg(not(target_arch = "wasm32"))]
fn chrono_lite(secs: u64) -> String {
    // Simple UTC datetime formatter without chrono dependency
    const SECS_PER_DAY: u64 = 86400;
    const SECS_PER_HOUR: u64 = 3600;
    const SECS_PER_MIN: u64 = 60;

    let days = secs / SECS_PER_DAY;
    let time_secs = secs % SECS_PER_DAY;
    let hours = time_secs / SECS_PER_HOUR;
    let mins = (time_secs % SECS_PER_HOUR) / SECS_PER_MIN;
    let secs = time_secs % SECS_PER_MIN;

    // Calculate year and day of year (simplified, doesn't handle leap years
    // perfectly)
    let mut year = 1970u64;
    let mut remaining_days = days;
    loop {
        let days_in_year = if is_leap_year(year) { 366 } else { 365 };
        if remaining_days < days_in_year {
            break;
        }
        remaining_days -= days_in_year;
        year += 1;
    }

    // Calculate month and day
    let leap = is_leap_year(year);
    let month_days: [u64; 12] = if leap {
        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    } else {
        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    };

    let mut month = 1u64;
    for &days_in_month in &month_days {
        if remaining_days < days_in_month {
            break;
        }
        remaining_days -= days_in_month;
        month += 1;
    }
    let day = remaining_days + 1;

    format!("{year:04}-{month:02}-{day:02}T{hours:02}:{mins:02}:{secs:02}")
}

#[cfg(not(target_arch = "wasm32"))]
const fn is_leap_year(year: u64) -> bool {
    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}

/// Get current timestamp in ISO 8601 format (WASM version).
#[cfg(target_arch = "wasm32")]
fn now_iso8601() -> String {
    // Use js_sys::Date for WASM
    let date = js_sys::Date::new_0();
    date.to_iso_string().into()
}

#[cfg(not(target_arch = "wasm32"))]
use super::NativeWebSocket;
#[cfg(target_arch = "wasm32")]
use super::WasmWebSocket;

/// Status subscription for receiving real-time verification updates.
pub struct StatusSubscription {
    #[cfg(not(target_arch = "wasm32"))]
    transport: NativeWebSocket,
    #[cfg(target_arch = "wasm32")]
    transport: WasmWebSocket,
    config: WebSocketConfig,
    request_id: RequestId,
    reconnect_attempts: u32,
}

impl StatusSubscription {
    /// Create a new status subscription.
    #[cfg(not(target_arch = "wasm32"))]
    pub const fn new(
        transport: NativeWebSocket,
        config: WebSocketConfig,
        request_id: RequestId,
    ) -> Self {
        Self {
            transport,
            config,
            request_id,
            reconnect_attempts: 0,
        }
    }

    /// Create a new status subscription.
    #[cfg(target_arch = "wasm32")]
    pub const fn new(
        transport: WasmWebSocket,
        config: WebSocketConfig,
        request_id: RequestId,
    ) -> Self {
        Self {
            transport,
            config,
            request_id,
            reconnect_attempts: 0,
        }
    }

    /// Connect to the WebSocket server.
    pub async fn connect(&mut self) -> Result<(), WebSocketError> {
        self.transport.connect().await
    }

    /// Wait for the request to complete.
    ///
    /// Returns the final status when verification completes, is denied, or
    /// expires. Handles reconnection with exponential backoff on transient
    /// failures.
    pub async fn wait_for_completion(&mut self) -> Result<RequestStatus, LastIDError> {
        loop {
            match self.transport.receive().await {
                Ok(update) => {
                    self.reconnect_attempts = 0; // Reset on successful receive

                    match update {
                        StatusUpdate::Pending => {} // Continue waiting
                        StatusUpdate::Fulfilled { vp_token } => {
                            return Ok(RequestStatus::Fulfilled {
                                request_id: self.request_id.clone(),
                                presentation: vp_token,
                                fulfilled_at: now_iso8601(),
                            });
                        }
                        StatusUpdate::Denied { reason } => {
                            return Ok(RequestStatus::Denied {
                                request_id: self.request_id.clone(),
                                reason,
                                denied_at: now_iso8601(),
                            });
                        }
                        StatusUpdate::Expired => {
                            return Ok(RequestStatus::Expired {
                                request_id: self.request_id.clone(),
                                expired_at: now_iso8601(),
                            });
                        }
                        StatusUpdate::Error { code, message } => {
                            return Err(LastIDError::WebSocket(WebSocketError::ConnectionFailed(
                                format!("{code}: {message}"),
                            )));
                        }
                    }
                }
                Err(WebSocketError::ReceiveTimeout) => {
                    return Err(LastIDError::Timeout(self.config.receive_timeout_seconds));
                }
                Err(
                    WebSocketError::ConnectionClosed { .. } | WebSocketError::ConnectionFailed(_),
                ) => {
                    // Try to reconnect
                    if self.reconnect_attempts >= self.config.max_reconnect_attempts {
                        return Err(LastIDError::WebSocket(
                            WebSocketError::ReconnectionExhausted {
                                attempts: self.reconnect_attempts,
                            },
                        ));
                    }

                    self.reconnect_attempts += 1;
                    let delay_ms = self.config.reconnect_delay_ms(self.reconnect_attempts);

                    #[cfg(not(target_arch = "wasm32"))]
                    tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;

                    #[cfg(target_arch = "wasm32")]
                    gloo_timers::future::TimeoutFuture::new(delay_ms as u32).await;

                    // Attempt reconnection
                    if let Err(e) = self.transport.connect().await
                        && self.reconnect_attempts >= self.config.max_reconnect_attempts
                    {
                        return Err(LastIDError::WebSocket(e));
                    }
                }
                Err(e) => return Err(LastIDError::WebSocket(e)),
            }
        }
    }

    /// Disconnect from the WebSocket server.
    pub async fn disconnect(&mut self) -> Result<(), WebSocketError> {
        self.transport.disconnect().await
    }
}
