//! Minimal single-account session coordinator.
//!
//! Phase 1 scope: register one [`Site`] + one [`BrowserSession`], provide
//! `refresh` / `refresh_if_stale` / `validate` / `reload_cookies` entry
//! points with per-call debouncing.
//!
//! Phase 3+ extends this to multi-account pools, credential vaults,
//! background schedulers, and quarantine / failure-recovery.

use std::sync::Arc;
use std::time::Instant;

use night_fury_core::BrowserSession;
use serde_json::Value;
use tokio::sync::Mutex;

use crate::error::SiteError;
use crate::site::{SessionStatus, Site};

/// A coordinator pairing one [`Site`] implementation with one [`BrowserSession`].
///
/// The manager:
/// - tracks the last refresh timestamp and respects [`Site::refresh_interval_min`]
/// - serialises refresh / validate calls so two concurrent callers don't double-refresh
/// - exposes the current cookie snapshot for downstream agents
///
/// # Example
///
/// ```ignore
/// use std::sync::Arc;
/// use tail_fin_common::{BrowserSession, SessionManager};
/// use tail_fin_twitter::TwitterSite;
///
/// let session = BrowserSession::builder().build().await?;
/// let manager = SessionManager::new(Arc::new(TwitterSite), session);
///
/// // Force refresh:
/// manager.refresh().await?;
///
/// // Check validity:
/// let status = manager.validate().await?;
/// ```
pub struct SessionManager {
    site: Arc<dyn Site>,
    browser: Arc<BrowserSession>,
    state: Mutex<SessionState>,
}

struct SessionState {
    cookies: Vec<Value>,
    last_refresh: Option<Instant>,
}

impl SessionManager {
    /// Create a manager wrapping a site + browser session. Initial cookie
    /// snapshot is empty — call `refresh()` or `reload_cookies()` to populate.
    pub fn new(site: Arc<dyn Site>, browser: BrowserSession) -> Self {
        Self {
            site,
            browser: Arc::new(browser),
            state: Mutex::new(SessionState {
                cookies: Vec::new(),
                last_refresh: None,
            }),
        }
    }

    /// Return the site this manager is scoped to.
    pub fn site(&self) -> &Arc<dyn Site> {
        &self.site
    }

    /// Return the browser session.
    pub fn browser(&self) -> &Arc<BrowserSession> {
        &self.browser
    }

    /// Force a server-side refresh. Updates internal cookie snapshot.
    ///
    /// Does NOT respect `refresh_interval_min` — for debounced refresh,
    /// use `refresh_if_stale`.
    pub async fn refresh(&self) -> Result<Vec<Value>, SiteError> {
        let cookies = self.site.refresh(&self.browser).await?;
        let mut state = self.state.lock().await;
        state.cookies = cookies.clone();
        state.last_refresh = Some(Instant::now());
        Ok(cookies)
    }

    /// Refresh only if the last refresh is older than `site.refresh_interval_min()`.
    /// Returns `Some(cookies)` on actual refresh, `None` if debounced.
    pub async fn refresh_if_stale(&self) -> Result<Option<Vec<Value>>, SiteError> {
        let should_refresh = {
            let state = self.state.lock().await;
            match state.last_refresh {
                None => true,
                Some(t) => t.elapsed() >= self.site.refresh_interval_min(),
            }
        };

        if should_refresh {
            self.refresh().await.map(Some)
        } else {
            Ok(None)
        }
    }

    /// Validate session liveness via the site's `validate` hook.
    pub async fn validate(&self) -> Result<SessionStatus, SiteError> {
        self.site.validate(&self.browser).await
    }

    /// Snapshot of the current cookies held by this manager.
    /// Initially empty until first `refresh()` or `reload_cookies()`.
    pub async fn cookies(&self) -> Vec<Value> {
        self.state.lock().await.cookies.clone()
    }

    /// Reload cookies from the browser without triggering server-side refresh.
    /// Useful if cookies were updated via some path outside the manager.
    pub async fn reload_cookies(&self) -> Result<Vec<Value>, SiteError> {
        let pattern = self
            .site
            .cookie_domain_patterns()
            .first()
            .copied()
            .unwrap_or("*");
        let cookies = self
            .browser
            .get_cookies_for_domain(pattern)
            .await
            .map_err(|e| SiteError::RefreshFailed {
                site: self.site.id(),
                reason: format!("reload_cookies: {e}"),
            })?;
        let mut state = self.state.lock().await;
        state.cookies = cookies.clone();
        Ok(cookies)
    }
}
