# -*- coding: utf-8 -*-
"""
SimpleSlice 抽象基类

提供数据分片读取的抽象接口，支持：
- 流式哈希计算
- 异步操作
- 并发安全
- 上下文管理
"""

import asyncio
import hashlib
from abc import ABC, abstractmethod
from typing import Optional


class SimpleSlice(ABC):
    """
    数据分片抽象基类
    
    特性:
    - 高内聚低耦合
    - 异步设计
    - 流式哈希计算
    - 并发安全
    
    属性:
        _length: 数据总长度
        _position: 当前读取位置
        _hash: MD5 哈希对象
        _hash_position: 已计算哈希的位置
        _lock: 并发锁
    """

    def __init__(self) -> None:
        """初始化数据分片"""
        self._length: int = 0
        self._position: int = 0
        self._hash: Optional[hashlib._Hash] = None
        self._hash_position: int = 0
        self._lock: asyncio.Lock = asyncio.Lock()

    @abstractmethod
    async def read_abstract(self, size: int) -> bytes:
        """
        读取原始数据（子类实现具体逻辑）
        
        Args:
            size: 读取的字节数
        
        Returns:
            bytes: 原始数据
        """
        pass

    @abstractmethod
    async def _close(self) -> None:
        """
        关闭资源（子类实现具体逻辑）
        """
        pass

    async def read(self, size: Optional[int] = None) -> bytes:
        """
        读取数据并自动更新哈希
        
        Args:
            size: 本次读取的字节数，None 表示读取到末尾
        
        Returns:
            bytes: 原始数据
        """
        self._check_closed()

        async with self._lock:
            if self._position >= self._length:
                return b""

            remain = self._length - self._position
            read_size = remain if size is None else min(size, remain)

            if read_size <= 0:
                return b""

            data = await self.read_abstract(read_size)

            self._position += len(data)

            if self._hash is None:
                self._hash = hashlib.md5()

            if self._hash_position < self._position:
                self._hash.update(data)
                self._hash_position = self._position

            return data

    async def seek(self, offset: int, whence: int = 0) -> int:
        """
        移动读取位置
        
        Args:
            offset: 偏移量（支持负数）
            whence: 起始位置 (0=文件头, 1=当前位置, 2=文件尾)
        
        Returns:
            int: 新位置
        
        Raises:
            ValueError: 位置超出范围
        """
        self._check_closed()

        if whence == 0:
            new_pos = offset
        elif whence == 1:
            new_pos = self._position + offset
        elif whence == 2:
            new_pos = self._length + offset
        else:
            raise ValueError(f"无效的 whence 值: {whence}")

        if new_pos < 0:
            raise ValueError(f"位置不能为负数: {new_pos}")
        if new_pos > self._length:
            raise ValueError(f"位置超出范围: {new_pos} > {self._length}")

        self._position = new_pos
        return new_pos

    async def compute_hash(self) -> dict:
        """
        计算 MD5 哈希
        
        如果 _hash_position < _length，自动读取剩余数据补全哈希。
        计算完成后恢复原始位置。
        
        Returns:
            dict: 包含 md5 和 sha1 的字典
        """
        self._check_closed()

        if self._hash is None:
            self._hash = hashlib.md5()

        if self._hash_position == self._length:
            return {"md5": self._hash.hexdigest()}

        saved_position = self._position

        try:
            while self._hash_position < self._length:
                data = await self.read_abstract(1024 * 1024)
                if not data:
                    break
                self._hash.update(data)
                self._hash_position += len(data)
        finally:
            await self.seek(saved_position)

        return {"md5": self._hash.hexdigest()}

    def __len__(self) -> int:
        """
        返回数据长度
        
        Returns:
            int: 数据总长度
        """
        return self._length

    async def __aenter__(self):
        """
        异步上下文管理器入口
        
        Returns:
            SimpleSlice: self
        """
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """
        异步上下文管理器出口
        
        Args:
            exc_type: 异常类型
            exc_val: 异常值
            exc_tb: 异常追踪
        """
        await self.close()

    def _check_closed(self) -> None:
        """
        检查资源是否已关闭
        
        Raises:
            ValueError: 资源已关闭
        """
        if self._length < 0:
            raise ValueError("资源已关闭")

    async def close(self) -> None:
        """
        关闭资源
        """
        await self._close()
        self._length = -1
