use std::sync::Arc;

use log::trace;
use wdev::Device;

use super::HybridLog;
use crate::error::{Error, Result};

impl<D: Device> HybridLog<D> {
  /// 推进 ReadOnlyAddress 并通过 Epoch 延迟更新 SafeReadOnlyAddress
  ///
  /// 对标 C# ShiftReadOnlyAddress:Unsafe 状态先行发布,Safe 状态经
  /// `BumpCurrentEpoch(Action)` 在纪元排空(所有旧纪元在途写入完成)后推进,
  /// 这正是刷盘/驱逐可以安全触达的严谨边界。
  pub fn shift_read_only_address(&self, new_ro: u64) {
    let old_ro = self.addresses.shift_read_only_address(new_ro);
    if new_ro > old_ro {
      let addrs = Arc::clone(&self.addresses);
      self.epoch.bump_current_epoch_action(move || {
        addrs.shift_safe_read_only_address(new_ro);
        trace!("Epoch 安全推进 SafeReadOnlyAddress 至 {new_ro:#x}");
      });
      if !self.epoch.this_instance_protected() {
        self.epoch.bump_epoch();
      }
    }
  }

  /// 推进 HeadAddress 并通过 Epoch 延迟更新 SafeHeadAddress
  ///
  /// 对标 C# ShiftHeadAddress / OnPagesClosed:head 内联推进使页槽位可进入待驱逐
  /// 候选,SafeHeadAddress 待纪元排空后推进——确保旧页上所有 epoch 保护的读者
  /// (裸指针直读)退出后槽位才可清零复用。
  ///
  /// 设计边界说明(联动 windex 扩容):页复用的 quiesce 完全依赖 SafeHead 的
  /// 纪元语义,与索引层"前台持旧表桶引用期间切表"所需的 epoch/quiesce 为同一
  /// 原语;hlog 自身不持有任何索引结构引用,切表时序由 store/session 层保证。
  pub fn shift_head_address(&self, new_head: u64) {
    let old_head = self.addresses.shift_head_address(new_head);
    if new_head > old_head {
      let addrs = Arc::clone(&self.addresses);
      self.epoch.bump_current_epoch_action(move || {
        addrs.shift_safe_head_address(new_head);
        trace!("Epoch 安全推进 SafeHeadAddress 至 {new_head:#x}");
      });
      if !self.epoch.this_instance_protected() {
        self.epoch.bump_epoch();
      }
    }
  }

  /// 推进 BeginAddress 并截断过期的历史存储段(对标 C# ShiftBeginAddress)
  ///
  /// 严谨顺序(对齐 C# 实现):先将 `[flushed_until, new_begin)` 强制落盘,确保
  /// 驱逐不越过持久化前缀(维持 `head <= flushed_until` 快照不变式),随后推进
  /// head(经 Epoch 延迟推进 safe_head,绝不内联强推)与 begin,最后物理截断
  /// 设备历史段。
  pub async fn shift_begin_address(&self, new_begin: u64) -> Result<()> {
    let flushed = self.addresses.flushed_until();
    if flushed < new_begin {
      // 先冻结 [flushed, new_begin) 为只读(对标 C# ShiftBeginAddress 先行
      // ShiftReadOnlyAddress(newBeginAddress)):补刷窗口内原位更新会撕裂设备上的
      // 待截断前缀,冻结后原位更新改走 RCU 追加。随后批量补刷(区间页仍驻留
      // 环形缓冲:head 尚未推进,必然可读)
      self.shift_read_only_address(new_begin);
      let start_page = self.config.page_id(flushed);
      let end_page = self.config.page_id(new_begin.saturating_sub(1));
      self.flush_pages_range(start_page, end_page).await?;
    }
    if self.addresses.flushed_until() < new_begin {
      return Err(Error::InvalidState(
        "shift_begin_address 前置条件不满足:待截断区间未完整落盘(调用方应先推进只读边界并排空纪元,参照 checkpoint 屏障)"
          .into(),
      ));
    }

    self.shift_head_address(new_begin);
    // begin 仅推进自身;head/safe_head 的连带推进已由 shift_head_address 以
    // 纪元安全方式完成("连带内联强推 safe_head"会绕过纪元排空语义,故不采用)
    self.addresses.shift_begin_address(new_begin);

    let dev = Arc::clone(&self.device);
    dev
      .truncate_until_address(new_begin)
      .await
      .map_err(Error::from)
  }

  /// 获取当前 TailAddress
  #[inline]
  pub fn tail_address(&self) -> u64 {
    self.addresses.tail()
  }

  /// 将 ReadOnlyAddress 迅速推进至当前 TailAddress(对标 Garnet ShiftReadOnlyToTail,使当前所有数据瞬间变为只读不可变)
  #[inline]
  pub fn shift_read_only_to_tail(&self) -> u64 {
    let tail = self.addresses.tail();
    self.shift_read_only_address(tail);
    tail
  }

  /// 获取当前 ReadOnlyAddress
  #[inline]
  pub fn read_only_address(&self) -> u64 {
    self.addresses.read_only()
  }

  /// 获取当前 SafeReadOnlyAddress
  #[inline]
  pub fn safe_read_only_address(&self) -> u64 {
    self.addresses.safe_read_only()
  }

  /// 获取当前 HeadAddress
  #[inline]
  pub fn head_address(&self) -> u64 {
    self.addresses.head()
  }

  /// 获取当前 SafeHeadAddress
  #[inline]
  pub fn safe_head_address(&self) -> u64 {
    self.addresses.safe_head()
  }

  /// 获取当前 FlushedUntilAddress
  #[inline]
  pub fn flushed_until_address(&self) -> u64 {
    self.addresses.flushed_until()
  }

  /// 获取当前 BeginAddress
  #[inline]
  pub fn begin_address(&self) -> u64 {
    self.addresses.begin()
  }

  /// 判断地址是否在可变区
  #[inline]
  pub fn is_mutable(&self, addr: u64) -> bool {
    self.addresses.is_mutable(addr)
  }

  /// 判断地址是否在只读区
  #[inline]
  pub fn is_read_only(&self, addr: u64) -> bool {
    self.addresses.is_read_only(addr)
  }

  /// 判断地址是否在内存中
  #[inline]
  pub fn is_in_memory(&self, addr: u64) -> bool {
    self.addresses.is_in_memory(addr)
  }

  /// 判断地址是否在磁盘区
  #[inline]
  pub fn is_on_disk(&self, addr: u64) -> bool {
    self.addresses.is_on_disk(addr)
  }
}