"""
FlaxKV2 FlaxList — 持久化列表

支持完整 Python list API + sorted 模式（自动维护有序）。
后端可选 lmdb（默认，支持多进程并发读）或 leveldb。
"""

import math
import os
import random
from dataclasses import dataclass, field
from typing import Any, Callable, Generator, Iterator, List, Optional

import lmdb

from flaxkv2.serialization import encoder, decoder
from flaxkv2.serialization.value_meta import ValueWithMeta
from flaxkv2.utils.log import get_logger
from flaxkv2.config import create_leveldb_options

logger = get_logger(__name__)

# 元数据键：排在所有 8 字节大端整数键之后
_LENGTH_KEY = b'\xff' * 8
_SORTED_KEY = b'\xff' * 7 + b'\xfe'  # 标记是否为 sorted 模式
_SENTINEL = object()  # 用于区分 "未提供默认值" 和 None
_DEFAULT_BATCH = 1000


@dataclass
class ListStats:
    """统计分析结果"""
    count: int = 0
    min: float = float('inf')
    max: float = float('-inf')
    sum: float = 0.0
    mean: float = 0.0
    variance: float = 0.0
    std: float = 0.0
    # sorted 模式额外提供
    median: float | None = None
    percentiles: dict[str, float] = field(default_factory=dict)

    def __repr__(self):
        lines = [
            f"  count    = {self.count}",
            f"  min      = {self.min}",
            f"  max      = {self.max}",
            f"  sum      = {self.sum}",
            f"  mean     = {self.mean}",
            f"  variance = {self.variance}",
            f"  std      = {self.std}",
        ]
        if self.median is not None:
            lines.append(f"  median   = {self.median}")
        if self.percentiles:
            for k, v in self.percentiles.items():
                lines.append(f"  {k} = {v}")
        return "ListStats(\n" + "\n".join(lines) + "\n)"


# ========== 存储后端抽象 ==========


class _LmdbWriteBatch:
    """收集 put/delete 操作，write() 时批量提交（单进程快路径）"""

    def __init__(self, env: lmdb.Environment):
        self._env = env
        self._ops: list = []

    def put(self, key: bytes, value: bytes):
        self._ops.append(('put', key, value))

    def delete(self, key: bytes):
        self._ops.append(('delete', key))

    def write(self):
        with self._env.begin(write=True) as txn:
            for op in self._ops:
                if op[0] == 'put':
                    txn.put(op[1], op[2])
                else:
                    txn.delete(op[1])
        self._ops.clear()


class _LmdbAtomicWriteBatch:
    """立即开启写事务，持有写锁直到 commit（多进程安全）"""

    def __init__(self, env: lmdb.Environment):
        self._txn = env.begin(write=True)

    def put(self, key: bytes, value: bytes):
        self._txn.put(key, value)

    def delete(self, key: bytes):
        self._txn.delete(key)

    def write(self):
        self._txn.commit()
        self._txn = None

    def __del__(self):
        if self._txn is not None:
            self._txn.abort()
            self._txn = None


class _LmdbStorage:
    """LMDB 后端：多进程并发读，可选多进程写安全"""

    def __init__(self, path: str, map_size: int = 2 ** 30, mp_safe: bool = False):
        self._env = lmdb.open(path, map_size=map_size)
        self._mp_safe = mp_safe

    def get(self, key: bytes) -> Optional[bytes]:
        with self._env.begin() as txn:
            return txn.get(key)

    def put(self, key: bytes, value: bytes):
        with self._env.begin(write=True) as txn:
            txn.put(key, value)

    def write_batch(self, transaction: bool = False):
        if self._mp_safe:
            return _LmdbAtomicWriteBatch(self._env)
        return _LmdbWriteBatch(self._env)

    def iterator(self, start: Optional[bytes] = None, stop: Optional[bytes] = None):
        """生成器 + finally 管理事务生命周期"""
        txn = self._env.begin()
        cursor = txn.cursor()

        if start is not None:
            found = cursor.set_range(start)
        else:
            found = cursor.first()

        if not found:
            txn.abort()
            return

        def _gen():
            try:
                for key, value in cursor:
                    if stop is not None and key >= stop:
                        break
                    yield key, value
            finally:
                txn.abort()

        yield from _gen()

    def close(self):
        self._env.close()


class _LevelDBStorage:
    """LevelDB 后端：薄包装 plyvel.DB"""

    def __init__(self, path: str, **leveldb_opts):
        import plyvel
        self._db = plyvel.DB(path, **leveldb_opts)

    def get(self, key: bytes) -> Optional[bytes]:
        return self._db.get(key)

    def put(self, key: bytes, value: bytes):
        self._db.put(key, value)

    def write_batch(self, transaction: bool = False):
        return self._db.write_batch(transaction=transaction)

    def iterator(self, start: Optional[bytes] = None, stop: Optional[bytes] = None):
        return self._db.iterator(start=start, stop=stop)

    def close(self):
        self._db.close()


class FlaxList:
    """
    持久化列表，支持完整 Python list API。

    普通模式：
        lst = FlaxList("events", "./data")
        lst.append(42)
        lst[0] = 99
        lst.insert(0, "first")
        lst.pop()

    Sorted 模式（自动维护有序）：
        lst = FlaxList("sorted", "./data", sorted=True)
        lst.add(3); lst.add(1); lst.add(2)
        list(lst)  # [1, 2, 3]

    后端选择：
        backend='lmdb'    — 默认，支持多进程并发读
        backend='leveldb' — 经典 LevelDB 后端
    """

    def __init__(
        self,
        name: str,
        path: str = ".",
        rebuild: bool = False,
        performance_profile: str = 'balanced',
        sorted: bool = False,
        key: Optional[Callable] = None,
        backend: str = 'lmdb',
        map_size: int = 2 ** 30,
        mp_safe: bool = False,
        **kwargs,
    ):
        self.name = name
        self.path = os.path.abspath(path)
        self.db_path = os.path.join(self.path, name)

        if rebuild and os.path.exists(self.db_path):
            import shutil
            shutil.rmtree(self.db_path)

        os.makedirs(os.path.dirname(self.db_path), exist_ok=True)

        if backend == 'lmdb':
            os.makedirs(self.db_path, exist_ok=True)
            self._db = _LmdbStorage(self.db_path, map_size=map_size, mp_safe=mp_safe)
        elif backend == 'leveldb':
            if sorted:
                raise ValueError(
                    "LevelDB 后端不支持 sorted 模式（大量覆盖写导致严重性能退化），"
                    "请使用 backend='lmdb'"
                )
            leveldb_opts = create_leveldb_options(
                create_if_missing=True,
                performance_profile=performance_profile,
                **{k: v for k, v in kwargs.items() if k in (
                    'lru_cache_size', 'bloom_filter_bits', 'block_size',
                    'write_buffer_size', 'max_open_files', 'compression',
                )},
            )
            self._db = _LevelDBStorage(self.db_path, **leveldb_opts)
        else:
            raise ValueError(f"Unknown backend: {backend!r}, expected 'lmdb' or 'leveldb'")

        self._backend = backend
        self._mp_safe = mp_safe
        self._closed = False

        # 从存储读取已有长度
        raw = self._db.get(_LENGTH_KEY)
        self._length = int.from_bytes(raw, 'big') if raw else 0

        # sorted 模式：从存储恢复或首次设置
        raw_sorted = self._db.get(_SORTED_KEY)
        if raw_sorted is not None:
            self._sorted = bool(int.from_bytes(raw_sorted, 'big'))
        else:
            self._sorted = sorted
            if sorted:
                self._db.put(_SORTED_KEY, (1).to_bytes(1, 'big'))

        self._key = key  # 排序键函数

    # ========== 内部辅助 ==========

    def _sync_length(self):
        """mp_safe 模式下从 DB 重新读取 _length（在写锁内调用）"""
        if not self._mp_safe:
            return
        raw = self._db.get(_LENGTH_KEY)
        self._length = int.from_bytes(raw, 'big') if raw else 0

    def refresh(self):
        """手动刷新内存状态（用于长期运行的读进程获取最新数据）"""
        raw = self._db.get(_LENGTH_KEY)
        self._length = int.from_bytes(raw, 'big') if raw else 0

    def _index_key(self, idx: int) -> bytes:
        return idx.to_bytes(8, 'big')

    def _get_raw(self, idx: int) -> bytes:
        """读取原始字节，不解码"""
        raw = self._db.get(self._index_key(idx))
        if raw is None:
            raise IndexError(f"FlaxList index out of range: {idx}")
        return raw

    def _put_raw(self, idx: int, raw: bytes, wb=None):
        """写入原始字节"""
        if wb is not None:
            wb.put(self._index_key(idx), raw)
        else:
            self._db.put(self._index_key(idx), raw)

    def _shift_right(self, start: int, count: int, wb):
        """将 [start, _length) 元素向右移 count 位（从后往前避免覆盖）"""
        for i in range(self._length - 1, start - 1, -1):
            raw = self._get_raw(i)
            wb.put(self._index_key(i + count), raw)

    def _shift_left(self, start: int, count: int, wb):
        """将 [start, _length) 元素向左移 count 位（从前往后）"""
        for i in range(start, self._length):
            raw = self._get_raw(i)
            wb.put(self._index_key(i - count), raw)

    def _update_length(self, new_length: int, wb=None):
        self._length = new_length
        data = new_length.to_bytes(8, 'big')
        if wb is not None:
            wb.put(_LENGTH_KEY, data)
        else:
            self._db.put(_LENGTH_KEY, data)

    def _check_sorted_disallow(self, op_name: str):
        if self._sorted:
            raise TypeError(
                f"'{op_name}' is not allowed in sorted mode. "
                f"Use 'add()' to insert elements."
            )

    # ========== 写入 ==========

    def append(self, value: Any) -> int:
        """追加一条数据，返回索引。sorted 模式下重定向到 add()。"""
        if self._sorted:
            return self.add(value)
        val_bytes = ValueWithMeta.encode_value(value, ttl_seconds=None)
        wb = self._db.write_batch()
        self._sync_length()
        idx = self._length
        wb.put(self._index_key(idx), val_bytes)
        self._length = idx + 1
        wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
        wb.write()
        return idx

    def extend(self, values) -> int:
        """批量追加，返回第一条的索引。sorted 模式下逐个 add()。"""
        if self._sorted:
            values = list(values)
            if not values:
                return self._length
            first = self.add(values[0])
            for v in values[1:]:
                self.add(v)
            return first
        wb = self._db.write_batch()
        self._sync_length()
        start_idx = self._length
        idx = start_idx
        for v in values:
            val_bytes = ValueWithMeta.encode_value(v, ttl_seconds=None)
            wb.put(self._index_key(idx), val_bytes)
            idx += 1
        self._length = idx
        wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
        wb.write()
        return start_idx

    def insert(self, i: int, value: Any):
        """在索引 i 处插入元素，后续元素右移。"""
        self._check_sorted_disallow('insert')
        val_bytes = ValueWithMeta.encode_value(value, ttl_seconds=None)
        wb = self._db.write_batch(transaction=True)
        self._sync_length()
        # 截断到有效范围（在写锁内用最新 _length 计算）
        if i < 0:
            i = max(0, self._length + i)
        if i > self._length:
            i = self._length
        self._shift_right(i, 1, wb)
        wb.put(self._index_key(i), val_bytes)
        self._length += 1
        wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
        wb.write()

    # ========== 修改 ==========

    def __setitem__(self, index, value):
        self._check_sorted_disallow('__setitem__')
        if isinstance(index, slice):
            self._setitem_slice(index, value)
        else:
            idx = self._resolve_index(index)
            val_bytes = ValueWithMeta.encode_value(value, ttl_seconds=None)
            self._db.put(self._index_key(idx), val_bytes)

    def _setitem_slice(self, s: slice, values):
        values = list(values)
        wb = self._db.write_batch(transaction=True)
        self._sync_length()
        start, stop, step = s.indices(self._length)
        if step == 1:
            old_count = stop - start
            new_count = len(values)
            if new_count > old_count:
                # 需要右移 [stop, _length) 来腾出空间
                self._shift_right(stop, new_count - old_count, wb)
                self._length += new_count - old_count
            elif new_count < old_count:
                # 需要左移 [stop, _length) 来填补空隙
                self._shift_left(stop, old_count - new_count, wb)
                # 删除尾部多余的键
                for j in range(self._length - (old_count - new_count), self._length):
                    wb.delete(self._index_key(j))
                self._length -= old_count - new_count
            # 写入新值
            for j, v in enumerate(values):
                val_bytes = ValueWithMeta.encode_value(v, ttl_seconds=None)
                wb.put(self._index_key(start + j), val_bytes)
            wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
            wb.write()
        else:
            # step != 1：数量必须匹配
            indices = list(range(start, stop, step))
            if len(values) != len(indices):
                wb.write()
                raise ValueError(
                    f"attempt to assign sequence of size {len(values)} "
                    f"to extended slice of size {len(indices)}"
                )
            for idx, v in zip(indices, values):
                val_bytes = ValueWithMeta.encode_value(v, ttl_seconds=None)
                wb.put(self._index_key(idx), val_bytes)
            wb.write()

    # ========== 删除 ==========

    def __delitem__(self, index):
        if isinstance(index, slice):
            self._delitem_slice(index)
        else:
            wb = self._db.write_batch(transaction=True)
            self._sync_length()
            idx = self._resolve_index(index)
            # 左移 [idx+1, _length) 覆盖被删元素
            self._shift_left(idx + 1, 1, wb)
            # 删除尾部多余键
            wb.delete(self._index_key(self._length - 1))
            self._length -= 1
            wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
            wb.write()

    def _delitem_slice(self, s: slice):
        wb = self._db.write_batch(transaction=True)
        self._sync_length()
        start, stop, step = s.indices(self._length)
        if step == 1:
            count = stop - start
            if count <= 0:
                wb.write()
                return
            self._shift_left(stop, count, wb)
            for j in range(self._length - count, self._length):
                wb.delete(self._index_key(j))
            self._length -= count
            wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
            wb.write()
        else:
            wb.write()  # 释放写锁，每次 del 自行加锁
            indices = sorted(range(start, stop, step), reverse=True)
            for idx in indices:
                del self[idx]

    def pop(self, i: int = -1) -> Any:
        """移除并返回索引 i 处的元素（默认末尾）"""
        idx = self._resolve_index(i)
        value = self._get_by_index(idx)
        del self[idx]
        return value

    def remove(self, value: Any):
        """移除第一个等于 value 的元素，不存在则抛 ValueError"""
        idx = self.index(value)
        del self[idx]

    def clear(self):
        """清空列表"""
        wb = self._db.write_batch()
        self._sync_length()
        for i in range(self._length):
            wb.delete(self._index_key(i))
        self._length = 0
        wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
        wb.write()

    # ========== 读取 ==========

    def _resolve_index(self, idx: int) -> int:
        """解析负索引，越界抛 IndexError"""
        if idx < 0:
            idx += self._length
        if idx < 0 or idx >= self._length:
            raise IndexError(f"FlaxList index out of range: {idx}")
        return idx

    def _get_by_index(self, idx: int) -> Any:
        raw = self._db.get(self._index_key(idx))
        if raw is None:
            raise IndexError(f"FlaxList index out of range: {idx}")
        value, _, _ = ValueWithMeta.decode_value(raw)
        return value

    def __getitem__(self, index):
        if isinstance(index, slice):
            start, stop, step = index.indices(self._length)
            if step == 1:
                return self._range_read(start, stop)
            return [self._get_by_index(i) for i in range(start, stop, step)]
        idx = self._resolve_index(index)
        return self._get_by_index(idx)

    def _range_read(self, start: int, stop: int) -> List:
        """利用 range iterator 高效范围读取"""
        if start >= stop:
            return []
        start_key = self._index_key(start)
        stop_key = self._index_key(stop)
        result = []
        for _, val_bytes in self._db.iterator(start=start_key, stop=stop_key):
            value, _, _ = ValueWithMeta.decode_value(val_bytes)
            result.append(value)
        return result

    # ========== 查找 ==========

    def index(self, value: Any, start: int = 0, stop: Optional[int] = None) -> int:
        """返回第一个等于 value 的索引，不存在则抛 ValueError"""
        if self._sorted and self._length > 0:
            return self._sorted_index(value)
        if stop is None:
            stop = self._length
        for i, v in enumerate(self.iter_range(start, stop), start=start):
            if v == value:
                return i
        raise ValueError(f"{value!r} is not in FlaxList")

    def count(self, value: Any) -> int:
        """统计 value 出现次数"""
        c = 0
        for v in self:
            if v == value:
                c += 1
        return c

    def __contains__(self, value: Any) -> bool:
        if self._sorted and self._length > 0:
            pos = self._bisect_left(value)
            if pos < self._length:
                candidate = self._get_by_index(pos)
                key_func = self._key or (lambda x: x)
                if key_func(candidate) == key_func(value):
                    return candidate == value
            return False
        for v in self:
            if v == value:
                return True
        return False

    # ========== 迭代 ==========

    def __iter__(self) -> Iterator:
        return self.iter_range(0, self._length)

    def iter_range(self, start: int = 0, stop: Optional[int] = None) -> Iterator:
        """范围迭代（内存友好）"""
        if stop is None:
            stop = self._length
        if start >= stop:
            return
        start_key = self._index_key(start)
        stop_key = self._index_key(stop)
        for _, val_bytes in self._db.iterator(start=start_key, stop=stop_key):
            value, _, _ = ValueWithMeta.decode_value(val_bytes)
            yield value

    # ========== 函数式查询/过滤 ==========

    def _iter_batched(self, start: int = 0, stop: int | None = None,
                      batch_size: int = _DEFAULT_BATCH) -> Generator:
        """分批读取元素的内部迭代器，避免逐条 get 开销。yield value"""
        if stop is None:
            stop = self._length
        for i in range(start, stop, batch_size):
            end = min(i + batch_size, stop)
            yield from self._range_read(i, end)

    def _iter_batched_indexed(self, start: int = 0, stop: int | None = None,
                              batch_size: int = _DEFAULT_BATCH) -> Generator[tuple[int, Any], None, None]:
        """分批读取元素，yield (index, value)"""
        if stop is None:
            stop = self._length
        for i in range(start, stop, batch_size):
            end = min(i + batch_size, stop)
            batch = self._range_read(i, end)
            for j, v in enumerate(batch):
                yield i + j, v

    def _iter_batched_reverse(self, start: int = 0, stop: int | None = None,
                              batch_size: int = _DEFAULT_BATCH) -> Generator:
        """反向分批读取元素。yield value"""
        if stop is None:
            stop = self._length
        for end in range(stop, start, -batch_size):
            begin = max(end - batch_size, start)
            batch = self._range_read(begin, end)
            yield from reversed(batch)

    def _iter_batched_reverse_indexed(self, start: int = 0, stop: int | None = None,
                                      batch_size: int = _DEFAULT_BATCH) -> Generator[tuple[int, Any], None, None]:
        """反向分批读取元素，yield (index, value)"""
        if stop is None:
            stop = self._length
        for end in range(stop, start, -batch_size):
            begin = max(end - batch_size, start)
            batch = self._range_read(begin, end)
            for j in range(len(batch) - 1, -1, -1):
                yield begin + j, batch[j]

    def where(self, predicate: Callable[[Any], bool],
              with_index: bool = False) -> Generator:
        """
        过滤元素，惰性求值。

        Args:
            predicate: 过滤条件函数。
            with_index: 为 True 时 yield (index, value)，否则 yield value。
        """
        for idx, item in self._iter_batched_indexed():
            if predicate(item):
                yield (idx, item) if with_index else item

    def first(self, predicate: Callable[[Any], bool] | None = None,
              default: Any = _SENTINEL, with_index: bool = False) -> Any:
        """
        返回第一个满足条件的元素。无匹配返回 default 或抛 ValueError。

        Args:
            predicate: 过滤条件。None 表示第一个元素。
            default: 无匹配时的默认值。
            with_index: 为 True 时返回 (index, value)。
        """
        for idx, item in self._iter_batched_indexed():
            if predicate is None or predicate(item):
                return (idx, item) if with_index else item
        if default is not _SENTINEL:
            return default
        raise ValueError("No matching element found")

    def last(self, predicate: Callable[[Any], bool] | None = None,
             default: Any = _SENTINEL, with_index: bool = False) -> Any:
        """
        返回最后一个满足条件的元素。反向分批读取。

        Args:
            predicate: 过滤条件。None 表示最后一个元素。
            default: 无匹配时的默认值。
            with_index: 为 True 时返回 (index, value)。
        """
        for idx, item in self._iter_batched_reverse_indexed():
            if predicate is None or predicate(item):
                return (idx, item) if with_index else item
        if default is not _SENTINEL:
            return default
        raise ValueError("No matching element found")

    def map(self, func: Callable, with_index: bool = False) -> Generator:
        """
        对每个元素应用函数，返回生成器。

        Args:
            func: with_index=False 时签名为 func(value)，
                  with_index=True 时签名为 func(index, value)。
            with_index: 为 True 时将索引传给 func 并 yield (index, result)。
        """
        if with_index:
            for idx, item in self._iter_batched_indexed():
                yield idx, func(idx, item)
        else:
            for item in self._iter_batched():
                yield func(item)

    def batch(self, size: int) -> Generator[List, None, None]:
        """按批次迭代，每批返回一个 list。"""
        if size <= 0:
            raise ValueError("batch size must be positive")
        for i in range(0, self._length, size):
            end = min(i + size, self._length)
            yield self._range_read(i, end)

    def any_match(self, predicate: Callable[[Any], bool]) -> bool:
        """是否存在满足条件的元素。短路求值。"""
        for item in self._iter_batched():
            if predicate(item):
                return True
        return False

    def all_match(self, predicate: Callable[[Any], bool]) -> bool:
        """是否全部满足条件。短路求值。"""
        for item in self._iter_batched():
            if not predicate(item):
                return False
        return True

    # ========== 统计分析 ==========

    def stats(self, key: Callable[[Any], float] | None = None,
              percentiles: list[float] | None = None) -> ListStats:
        """
        流式统计分析，O(1) 内存，单次遍历。

        使用 Welford 在线算法计算 count/min/max/sum/mean/variance/std。
        sorted 模式下额外支持精确 median 和 percentiles（通过索引直接访问）。

        Args:
            key: 提取数值的函数。例如 lambda x: x["score"]。
                 默认 None 表示元素本身为数值。
            percentiles: 需要计算的分位数列表，如 [25, 50, 75, 90, 99]。
                 仅 sorted 模式支持精确计算；非 sorted 模式忽略此参数。

        Returns:
            ListStats 对象

        Examples:
            # 纯数值列表
            lst.stats()

            # 字典列表，按某字段统计
            lst.stats(key=lambda x: x["price"])

            # sorted 模式，带分位数
            lst.stats(percentiles=[25, 50, 75, 99])
        """
        if self._length == 0:
            return ListStats()

        result = ListStats()
        # Welford 在线算法
        n = 0
        mean = 0.0
        m2 = 0.0
        total = 0.0
        vmin = float('inf')
        vmax = float('-inf')

        for item in self._iter_batched():
            v = float(key(item)) if key else float(item)
            n += 1
            total += v
            if v < vmin:
                vmin = v
            if v > vmax:
                vmax = v
            delta = v - mean
            mean += delta / n
            delta2 = v - mean
            m2 += delta * delta2

        result.count = n
        result.min = vmin
        result.max = vmax
        result.sum = total
        result.mean = mean
        result.variance = m2 / n if n > 0 else 0.0
        result.std = math.sqrt(result.variance)

        # sorted 模式：精确 percentile / median（O(1) 索引访问）
        if self._sorted:
            key_func = key or (lambda x: x)
            result.median = float(key_func(self._get_by_index(n // 2)))
            if n % 2 == 0 and n > 1:
                left = float(key_func(self._get_by_index(n // 2 - 1)))
                result.median = (left + result.median) / 2

            if percentiles:
                for p in percentiles:
                    if not 0 <= p <= 100:
                        raise ValueError(f"percentile must be in [0, 100], got {p}")
                    idx = int(p / 100 * (n - 1))
                    result.percentiles[f"p{p}"] = float(
                        key_func(self._get_by_index(idx))
                    )

        return result

    def reduce(self, func: Callable[[Any, Any], Any], initial: Any = _SENTINEL) -> Any:
        """
        流式归约，O(1) 内存。

        Args:
            func: 归约函数 func(accumulator, element) -> new_accumulator
            initial: 初始值。未提供则使用第一个元素。

        Examples:
            # 求和
            total = lst.reduce(lambda acc, x: acc + x, 0)

            # 求最大值
            max_val = lst.reduce(lambda acc, x: max(acc, x))

            # 自定义聚合
            result = lst.reduce(
                lambda acc, x: {
                    'sum': acc['sum'] + x,
                    'count': acc['count'] + 1,
                },
                {'sum': 0, 'count': 0},
            )
        """
        acc = initial
        started = acc is not _SENTINEL
        for item in self._iter_batched():
            if not started:
                acc = item
                started = True
            else:
                acc = func(acc, item)
        if not started:
            raise TypeError("reduce() of empty FlaxList with no initial value")
        return acc

    # ========== 随机 ==========

    def shuffle(self):
        """原地随机打乱（Fisher-Yates，交换原始字节避免编解码开销）"""
        self._check_sorted_disallow('shuffle')
        n = self._length
        if n <= 1:
            return
        # 内存覆盖层：write_batch 是延迟写入，_get_raw 读不到未提交的数据
        overlay: dict[int, bytes] = {}
        wb = self._db.write_batch(transaction=True)
        for i in range(n - 1, 0, -1):
            j = random.randint(0, i)
            if i != j:
                raw_i = overlay.get(i) or self._get_raw(i)
                raw_j = overlay.get(j) or self._get_raw(j)
                overlay[i] = raw_j
                overlay[j] = raw_i
                wb.put(self._index_key(i), raw_j)
                wb.put(self._index_key(j), raw_i)
        wb.write()

    def sample(self, k: int) -> list:
        """
        随机抽样 k 个不重复元素，O(k) 内存。

        Args:
            k: 抽样数量。k > len(self) 时抛 ValueError。

        Returns:
            包含 k 个元素的列表（顺序随机）。
        """
        n = self._length
        if k < 0:
            raise ValueError("sample size must be non-negative")
        if k > n:
            raise ValueError(f"sample size {k} exceeds list length {n}")
        if k == 0:
            return []
        indices = random.sample(range(n), k)
        return [self._get_by_index(i) for i in indices]

    def choice(self):
        """随机返回一个元素。空列表抛 IndexError。"""
        if self._length == 0:
            raise IndexError("cannot choose from empty FlaxList")
        return self._get_by_index(random.randint(0, self._length - 1))

    # ========== 排序与翻转 ==========

    def reverse(self):
        """原地翻转列表（交换原始字节避免编解码开销）"""
        self._check_sorted_disallow('reverse')
        if self._length <= 1:
            return
        wb = self._db.write_batch(transaction=True)
        lo, hi = 0, self._length - 1
        while lo < hi:
            raw_lo = self._get_raw(lo)
            raw_hi = self._get_raw(hi)
            wb.put(self._index_key(lo), raw_hi)
            wb.put(self._index_key(hi), raw_lo)
            lo += 1
            hi -= 1
        wb.write()

    def sort(self, *, key: Optional[Callable] = None, reverse: bool = False):
        """原地排序（全量读入内存排序后写回）"""
        self._check_sorted_disallow('sort')
        if self._length <= 1:
            return
        items = list(self)
        items.sort(key=key, reverse=reverse)
        wb = self._db.write_batch()
        for i, v in enumerate(items):
            val_bytes = ValueWithMeta.encode_value(v, ttl_seconds=None)
            wb.put(self._index_key(i), val_bytes)
        wb.write()

    # ========== 运算符 ==========

    def __add__(self, other) -> list:
        """返回普通 Python list（FlaxList 创建需要 name/path，不隐式创建）"""
        result = list(self)
        if isinstance(other, FlaxList):
            result.extend(list(other))
        else:
            result.extend(other)
        return result

    def __iadd__(self, other):
        """就地 extend"""
        if isinstance(other, FlaxList):
            self.extend(list(other))
        else:
            self.extend(other)
        return self

    def __eq__(self, other) -> bool:
        if isinstance(other, FlaxList):
            if self._length != len(other):
                return False
            for a, b in zip(self, other):
                if a != b:
                    return False
            return True
        if isinstance(other, list):
            if self._length != len(other):
                return False
            for a, b in zip(self, other):
                if a != b:
                    return False
            return True
        return NotImplemented

    # ========== 工具方法 ==========

    def copy(self, name: str, path: Optional[str] = None) -> 'FlaxList':
        """创建副本到新的 FlaxList"""
        new_list = FlaxList(
            name, path or self.path,
            sorted=self._sorted, key=self._key,
            backend=self._backend,
        )
        new_list.extend(list(self))
        return new_list

    def to_list(self) -> list:
        return list(self)

    # ========== Sorted 模式 ==========

    def add(self, value: Any) -> int:
        """sorted 模式：二分查找插入点后 insert。普通模式等同 append。"""
        if not self._sorted:
            return self.append(value)
        val_bytes = ValueWithMeta.encode_value(value, ttl_seconds=None)
        wb = self._db.write_batch(transaction=True)
        self._sync_length()
        pos = self._bisect_right(value)
        self._shift_right(pos, 1, wb)
        wb.put(self._index_key(pos), val_bytes)
        self._length += 1
        wb.put(_LENGTH_KEY, self._length.to_bytes(8, 'big'))
        wb.write()
        return pos

    def _bisect_left(self, value) -> int:
        """二分查找：返回 value 应插入的最左位置"""
        lo, hi = 0, self._length
        key_func = self._key or (lambda x: x)
        target = key_func(value)
        while lo < hi:
            mid = (lo + hi) // 2
            mid_val = self._get_by_index(mid)
            if key_func(mid_val) < target:
                lo = mid + 1
            else:
                hi = mid
        return lo

    def _bisect_right(self, value) -> int:
        """二分查找：返回 value 应插入的最右位置"""
        lo, hi = 0, self._length
        key_func = self._key or (lambda x: x)
        target = key_func(value)
        while lo < hi:
            mid = (lo + hi) // 2
            mid_val = self._get_by_index(mid)
            if key_func(mid_val) <= target:
                lo = mid + 1
            else:
                hi = mid
        return lo

    def _sorted_index(self, value: Any) -> int:
        """sorted 模式：O(log n) 查找 value 的索引"""
        pos = self._bisect_left(value)
        if pos < self._length:
            candidate = self._get_by_index(pos)
            if candidate == value:
                return pos
        raise ValueError(f"{value!r} is not in FlaxList")

    # ========== 长度 ==========

    def __len__(self) -> int:
        return self._length

    # ========== 生命周期 ==========

    def close(self):
        if self._closed:
            return
        self._db.close()
        self._closed = True

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
        return False

    def __repr__(self):
        mode = " sorted" if self._sorted else ""
        return f"FlaxList(name={self.name!r}, length={self._length}{mode})"
