from typing import Optional, Dict, Any
from .base import BaseMixin
from ..const import API_PATHS


class Upload(BaseMixin):
    async def directory(self, name: str, parent_id: int = 0) -> Dict[str, Any]:
        """
        创建目录
        
        在指定父目录下创建新目录。目录名不能与同级目录重名。
        
        注意：此接口有访问频率限制(QPS: 20)。
        
        Args:
            name: 目录名(不能重名)
            parent_id: 父目录ID，上传到根目录时填写0，默认为0
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "dirID": 12345
                }
            }
        
        Example:
            >>> result = await upload.directory("新建文件夹", 0)
            >>> if result["code"] == 0:
            ...     dir_id = result["data"]["dirID"]
        """
        return await self._send_request(
            API_PATHS.mkdir,
            json={
                "name": name,
                "parentID": parent_id
            }
        )

    async def file(self, parent_file_id: int, filename: str, etag: str, size: int, 
                  duplicate: Optional[int] = None, contain_dir: Optional[bool] = None) -> Dict[str, Any]:
        """
        创建文件(上传流程第一步)
        
        此接口是文件上传流程的第一步。根据返回结果判断是否需要上传：
        - 如果reuse为true，表示秒传成功，无需继续上传
        - 如果reuse为false，返回preuploadID和sliceSize，需要继续分片上传
        
        文件名要求：
        - 小于256个字符
        - 不能包含以下字符："\/:*?|><
        - 不能全部是空格
        - 开发者上传单文件大小限制10GB
        
        Args:
            parent_file_id: 父目录ID，上传到根目录时填写0
            filename: 文件名(不能重名)
            etag: 文件MD5值
            size: 文件大小，单位为字节
            duplicate: 文件处理策略(可选)，1保留两者(新文件名将自动添加后缀)，2覆盖原文件
            contain_dir: 上传文件是否包含路径(可选)，默认false
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            秒传成功时:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "fileID": 12345,
                    "reuse": true
                }
            }
            需要上传时:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "preuploadID": "预上传ID",
                    "reuse": false,
                    "sliceSize": 10485760
                }
            }
        
        Example:
            >>> result = await upload.file(0, "test.txt", "md5_hash", 1024)
            >>> if result["data"]["reuse"]:
            ...     print("秒传成功")
            ... else:
            ...     preupload_id = result["data"]["preuploadID"]
            ...     slice_size = result["data"]["sliceSize"]
        """
        json_data = {
            "parentFileID": parent_file_id,
            "filename": filename,
            "etag": etag,
            "size": size
        }
        if duplicate is not None:
            json_data["duplicate"] = duplicate
        if contain_dir is not None:
            json_data["containDir"] = contain_dir
        
        return await self._send_request(
            API_PATHS.create_file,
            json=json_data
        )

    async def url(self, preupload_id: str, slice_no: int) -> Dict[str, Any]:
        """
        获取上传地址(上传流程第二步)
        
        此接口用于获取文件分片的上传地址。需要为每个分片调用此接口，
        sliceNo从1开始递增。
        
        Args:
            preupload_id: 预上传ID，从create_file接口获取
            slice_no: 分片序号，从1开始自增
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "presignedURL": "https://upload-url..."
                }
            }
        
        Example:
            >>> result = await upload.url("preupload_id", 1)
            >>> upload_url = result["data"]["presignedURL"]
            >>> # 使用PUT方法上传文件分片到upload_url
        """
        return await self._send_request(
            API_PATHS.get_upload_url,
            json={
                "preuploadID": preupload_id,
                "sliceNo": slice_no
            }
        )

    async def parts(self, preupload_id: str) -> Dict[str, Any]:
        """
        列举已上传分片(上传流程第四步，非必需)
        
        此接口用于列出云端已上传的分片，供用户自行比对MD5。
        建议在调用upload_complete前调用此接口进行比对。
        
        注意：当文件大小小于sliceSize时，该接口返回空值，可以跳过此步。
        
        Args:
            preupload_id: 预上传ID，从create_file接口获取
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "parts": [
                        {
                            "partNumber": 1,
                            "size": 10485760,
                            "etag": "分片MD5"
                        }
                    ]
                }
            }
        
        Example:
            >>> result = await upload.parts("preupload_id")
            >>> parts = result["data"]["parts"]
            >>> # 与本地分片MD5进行比对
        """
        return await self._send_request(
            API_PATHS.list_upload_parts,
            json={
                "preuploadID": preupload_id
            }
        )

    async def complete(self, preupload_id: str) -> Dict[str, Any]:
        """
        上传完毕(上传流程第五步)
        
        此接口用于通知服务器文件分片已全部上传完成。
        根据返回结果判断是否需要异步轮询：
        - 如果async为false且fileID不为0，上传完成
        - 如果async为true，需要调用upload_async_result轮询获取结果
        
        建议：调用此接口前，请优先调用list_upload_parts进行MD5比对。
        
        Args:
            preupload_id: 预上传ID，从create_file接口获取
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            上传完成时:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "async": false,
                    "completed": true,
                    "fileID": 12345
                }
            }
            需要异步查询时:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "async": true,
                    "completed": false,
                    "fileID": 0
                }
            }
        
        Example:
            >>> result = await upload.complete("preupload_id")
            >>> if result["data"]["async"]:
            ...     # 需要轮询查询
            ...     pass
            ... else:
            ...     file_id = result["data"]["fileID"]
        """
        return await self._send_request(
            API_PATHS.upload_complete,
            json={
                "preuploadID": preupload_id
            }
        )

    async def result(self, preupload_id: str) -> Dict[str, Any]:
        """
        异步轮询获取上传结果(上传流程第六步)
        
        当upload_complete返回async=true时，需要调用此接口轮询查询上传结果。
        如果completed为false，请至少1秒后再次调用此接口。
        
        服务器会校验用户预上传时的MD5与实际上传成功的MD5是否一致。
        
        Args:
            preupload_id: 预上传ID，从create_file接口获取
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            上传中:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "completed": false,
                    "fileID": 0
                }
            }
            上传完成:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "completed": true,
                    "fileID": 12345
                }
            }
        
        Example:
            >>> import time
            >>> while True:
            ...     result = await upload.result("preupload_id")
            ...     if result["data"]["completed"]:
            ...         file_id = result["data"]["fileID"]
            ...         break
            ...     time.sleep(1)
        """
        return await self._send_request(
            API_PATHS.upload_async_result,
            json={
                "preuploadID": preupload_id
            }
        )

    async def reuse(self, parent_file_id: int, filename: str, sha1: str, size: int, 
                   duplicate: Optional[int] = None) -> Dict[str, Any]:
        """
        SHA1哈希值文件上传(秒传接口)
        
        使用文件的SHA1值进行秒传。如果云端已有相同SHA1的文件，
        则直接复制，无需重新上传。
        
        文件名要求：
        - 小于256个字符
        - 不能包含以下字符："\/:*?|><
        - 不能全部是空格
        
        Args:
            parent_file_id: 父目录ID，上传到根目录时填写0
            filename: 文件名(不能重名)
            sha1: 文件SHA1值
            size: 文件大小，单位为字节
            duplicate: 文件处理策略(可选)，1保留两者(新文件名将自动添加后缀)，2覆盖原文件
        
        Returns:
            Dict[str, Any]: API响应数据
            
        Returns Data Structure:
            秒传成功:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "fileID": 12345,
                    "reuse": true
                }
            }
            需要上传:
            {
                "code": 0,
                "message": "ok",
                "data": {
                    "reuse": false
                }
            }
        
        Example:
            >>> result = await upload.reuse(0, "test.txt", "sha1_hash", 1024)
            >>> if result["data"]["reuse"]:
            ...     print("秒传成功")
        """
        json_data = {
            "parentFileID": parent_file_id,
            "filename": filename,
            "sha1": sha1,
            "size": size
        }
        if duplicate is not None:
            json_data["duplicate"] = duplicate
        
        return await self._send_request(
            API_PATHS.sha1_reuse,
            json=json_data
        )
