//! SSR 增量渲染缓存失效的未来就绪基础设施。
//!
//! 本模块维护一个全局单调递增的世代号（generation）。文章写入成功后调用方会
//! 使其递增，从而**标记** SSR 渲染结果已过期。然而：
//!
//! **Dioxus 0.7 的增量渲染器使用请求 URI 的 `path_and_query()` 作为内部缓存键，
//! 且没有暴露公开 API 供外部代码自定义缓存键或按路由失效已渲染页面。**
//! 因此，当前世代号并**不会**实际使 Dioxus 的 SSR 缓存失效；它只是为未来 API
//! 准备好状态，并在请求/响应中提供可观测性。
//!
//! 在 Dioxus 提供以下任一能力之前，有效的 SSR 缓存失效手段仍是调低
//! `SSR_CACHE_SECS` 这一兜底 TTL：
//! - 自定义增量渲染缓存键的回调；或
//! - 从 server function 内部按路由失效缓存的公开 API。
//!
//! 当前实现：
//! - `bump_global_generation()` / `current_global_generation()`：全局世代号。
//! - `SsrGeneration`：注入到请求扩展中的类型；未来 Dioxus 支持读取扩展生成
//!   缓存键时可直接使用。
//! - `src/main.rs` 的中间件把当前世代号附加到 `X-SSR-Generation` 响应头
//!   （仅 GET 请求），便于调试与监控。
//!
//! 仅在启用 `server` feature 时编译。

#![cfg(feature = "server")]

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::LazyLock;

/// 全局 SSR 世代号。
///
/// 任何文章写入操作都会使其递增，从而让所有基于该全局世代的 SSR 缓存键在未来
/// Dioxus 支持自定义缓存键时失效。
static GLOBAL_GENERATION: LazyLock<AtomicU64> = LazyLock::new(AtomicU64::default);

/// 注入到请求扩展中的当前 SSR 世代号。
///
/// 这是为未来 Dioxus 支持自定义 SSR 缓存键预留的钩子。当前 Dioxus 0.7 的渲染器
/// 不会读取此扩展。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SsrGeneration(pub u64);

/// 原子递增并返回新的全局世代号。
///
/// 任何文章写入操作都会调用本函数,标记 SSR 渲染结果已过期。当前 Dioxus 0.7
/// 不读取此世代号(详见模块文档),实际失效仍依赖 `SSR_CACHE_SECS` 兜底 TTL。
/// 此处打一条 info 日志,既证明写入路径已执行,也提醒部署者此处存在滞后——
/// 避免"我改了文章怎么没变"被误判成 DB/发布链路问题。
pub fn bump_global_generation() -> u64 {
    let new = GLOBAL_GENERATION
        .fetch_add(1, Ordering::SeqCst)
        .wrapping_add(1);
    tracing::info!(
        new_generation = new,
        "SSR 世代号已递增（写入路径触发）。注意：Dioxus 0.7 不读取此值，\
         SSR 缓存需等待 SSR_CACHE_SECS 过期后才反映新内容"
    );
    new
}

/// 返回当前全局世代号。
pub fn current_global_generation() -> u64 {
    GLOBAL_GENERATION.load(Ordering::SeqCst)
}

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

    #[test]
    #[serial]
    fn global_generation_is_monotonic() {
        let before = current_global_generation();
        let g1 = bump_global_generation();
        let g2 = bump_global_generation();
        let current = current_global_generation();

        assert!(g1 > before || g1 == 1);
        assert!(g2 > g1);
        assert_eq!(current, g2);
    }
}
