"""
文件系统SDK - 与brslet的filesystem接口对接
支持文件上传、下载、同步等操作
"""
import os
from typing import Optional, Generator, Union, IO, Dict, Any, List
from dataclasses import dataclass

# 从client.py导入请求方法
from ..handler.client import (
    post, get, async_post, async_get,
    Payload, Response,
)


@dataclass
class TaskInfo:
    """任务信息"""
    run_id: str
    direction: str = ""
    local_path: str = ""
    remote_path: str = ""
    status: str = ""
    progress: float = 0.0
    message: str = ""
    start_time: str = ""
    end_time: str = ""


@dataclass  
class SyncResult:
    """同步结果"""
    success: bool
    md5: str = ""
    size: int = 0
    duration: int = 0
    message: str = ""


class FileSystem:
    """文件系统操作类
    
    与brslet的filesystem接口对接，支持文件上传、下载等操作。
    
    路径格式: /root_path/pid/fs_name/oid/uid/file_path
    """
    
    def __init__(
        self, 
        path: str, 
        root_path: Optional[str] = None, 
        pid: Optional[str] = None, 
        oid: Optional[str] = None, 
        uid: Optional[str] = None, 
        fs_name: Optional[str] = None
    ):
        self._path = path
        self._root_path = root_path
        self._oid = oid
        self._uid = uid
        self._fs_name = fs_name
        self._pid = pid
        # 确保本地目录存在
        if not os.path.exists(path):
            os.makedirs(path, exist_ok=True)

    def write(
        self, 
        file_path: str, 
        data: Union[str, bytes], 
        mode: str = 'w', 
        encoding: str = 'utf-8',
        wait: bool = True,
        timeout: int = 3600
    ) -> Union[Response, TaskInfo, Exception]:
        """写入文件并同步到远程存储
        
        Args:
            file_path: 相对于FileSystem根路径的文件路径
            data: 要写入的数据，文本模式传str，二进制模式传bytes
            mode: 'w'文本模式，'wb'二进制模式
            encoding: 文本模式的编码，二进制模式忽略此参数
            wait: 是否等待上传完成
            timeout: 等待超时时间（秒）
            
        Returns:
            如果wait=True，返回Response；否则返回TaskInfo
        """
        try:
            if mode not in ['w', 'wb']:
                raise ValueError("mode must be 'w' or 'wb'")
            
            # 写入本地文件
            full_path = os.path.join(self._path, file_path)
            os.makedirs(os.path.dirname(full_path), exist_ok=True)
            
            if mode == 'w':
                with open(full_path, mode, encoding=encoding) as file:
                    file.write(data)
            else:
                with open(full_path, mode) as file:
                    file.write(data)
            
            # 调用brslet上传接口
            resp = post(Payload(path="fs/upload", payload={
                "file": file_path,
                "root_path": self._root_path,
                "pid": self._pid,
                "oid": self._oid,
                "uid": self._uid,
                "fs_name": self._fs_name
            }), connector_type="brslet")
            
            if resp.message != "success":
                return resp
            
            task_info = TaskInfo(
                run_id=resp.data.get('run_id', ''),
                status=resp.data.get('status', ''),
                message=resp.data.get('message', '')
            )
            
            if wait:
                return self.wait_task(task_info.run_id, timeout=timeout)
            
            return task_info
            
        except Exception as e:
            return e

    async def write_async(
        self, 
        file_path: str, 
        data: Union[str, bytes], 
        mode: str = 'w', 
        encoding: str = 'utf-8',
        wait: bool = True,
        timeout: int = 3600
    ) -> Union[Response, TaskInfo, Exception]:
        """异步写入文件并同步到远程存储"""
        try:
            if mode not in ['w', 'wb']:
                raise ValueError("mode must be 'w' or 'wb'")
            
            # 写入本地文件
            full_path = os.path.join(self._path, file_path)
            os.makedirs(os.path.dirname(full_path), exist_ok=True)
            
            if mode == 'w':
                with open(full_path, mode, encoding=encoding) as file:
                    file.write(data)
            else:
                with open(full_path, mode) as file:
                    file.write(data)
            
            # 调用brslet上传接口
            resp = await async_post(Payload(path="fs/upload", payload={
                "file": file_path,
                "root_path": self._root_path,
                "pid": self._pid,
                "oid": self._oid,
                "uid": self._uid,
                "fs_name": self._fs_name
            }), connector_type="brslet")
            
            if resp.message != "success":
                return resp
            
            task_info = TaskInfo(
                run_id=resp.data.get('run_id', ''),
                status=resp.data.get('status', ''),
                message=resp.data.get('message', '')
            )
            
            if wait:
                return await self.wait_task_async(task_info.run_id, timeout=timeout)
            
            return task_info
            
        except Exception as e:
            return e

    def read(
        self, 
        file_path: str, 
        mode: str = 'r', 
        encoding: str = 'utf-8', 
        timeout: int = 3600
    ) -> Union[IO, Exception]:
        """读取文件，如果本地不存在则从远程同步
        
        Args:
            file_path: 相对于FileSystem根路径的文件路径
            mode: 'r'文本模式，'rb'二进制模式
            encoding: 文本模式的编码，二进制模式忽略此参数
            timeout: 等待远程同步的超时时间（秒）
            
        Returns:
            打开的文件对象
        """
        try:
            if mode not in ['r', 'rb']:
                raise ValueError("mode must be 'r' or 'rb'")
            
            full_path = os.path.join(self._path, file_path)
            
            # 如果本地文件不存在，从远程下载
            if not os.path.exists(full_path):
                resp = post(Payload(path="fs/download", payload={
                    "file": file_path,
                    "root_path": self._root_path,
                    "pid": self._pid,
                    "oid": self._oid,
                    "uid": self._uid,
                    "fs_name": self._fs_name
                }), connector_type="brslet")
                
                if resp.message != "success":
                    return Exception(resp.message)
                
                # 等待下载完成
                run_id = resp.data.get('run_id', '')
                if run_id:
                    wait_resp = self.wait_task(run_id, timeout=timeout)
                    if wait_resp.message != "success":
                        return Exception(wait_resp.message)
            
            # 打开文件
            if mode == 'rb':
                return open(full_path, mode)
            else:
                return open(full_path, mode, encoding=encoding)
                
        except Exception as e:
            return e

    async def read_async(
        self, 
        file_path: str, 
        mode: str = 'r', 
        encoding: str = 'utf-8', 
        timeout: int = 3600
    ) -> Union[IO, Exception]:
        """异步读取文件，如果本地不存在则从远程同步"""
        try:
            if mode not in ['r', 'rb']:
                raise ValueError("mode must be 'r' or 'rb'")
            
            full_path = os.path.join(self._path, file_path)
            
            if not os.path.exists(full_path):
                resp = await async_post(Payload(path="fs/download", payload={
                    "file": file_path,
                    "root_path": self._root_path,
                    "pid": self._pid,
                    "oid": self._oid,
                    "uid": self._uid,
                    "fs_name": self._fs_name
                }), connector_type="brslet")
                
                if resp.message != "success":
                    return Exception(resp.message)
                
                run_id = resp.data.get('run_id', '')
                if run_id:
                    wait_resp = await self.wait_task_async(run_id, timeout=timeout)
                    if wait_resp.message != "success":
                        return Exception(wait_resp.message)
            
            if mode == 'rb':
                return open(full_path, mode)
            else:
                return open(full_path, mode, encoding=encoding)
                
        except Exception as e:
            return e

    def get_task(self, run_id: str) -> Union[TaskInfo, Exception]:
        """获取任务状态
        
        Args:
            run_id: 任务ID
            
        Returns:
            任务信息
        """
        try:
            resp = get(f"fs/task/{run_id}", connector_type="brslet")
            if resp.message != "success":
                return Exception(resp.message)
            
            data = resp.data
            return TaskInfo(
                run_id=data.get('run_id', ''),
                direction=data.get('direction', ''),
                local_path=data.get('local_path', ''),
                remote_path=data.get('remote_path', ''),
                status=data.get('status', ''),
                progress=data.get('progress', 0.0),
                message=data.get('message', ''),
                start_time=data.get('start_time', ''),
                end_time=data.get('end_time', '')
            )
        except Exception as e:
            return e

    async def get_task_async(self, run_id: str) -> Union[TaskInfo, Exception]:
        """异步获取任务状态"""
        try:
            resp = await async_get(f"fs/task/{run_id}", connector_type="brslet")
            if resp.message != "success":
                return Exception(resp.message)
            
            data = resp.data
            return TaskInfo(
                run_id=data.get('run_id', ''),
                direction=data.get('direction', ''),
                local_path=data.get('local_path', ''),
                remote_path=data.get('remote_path', ''),
                status=data.get('status', ''),
                progress=data.get('progress', 0.0),
                message=data.get('message', ''),
                start_time=data.get('start_time', ''),
                end_time=data.get('end_time', '')
            )
        except Exception as e:
            return e

    def list_tasks(self) -> Union[List[TaskInfo], Exception]:
        """获取所有任务列表"""
        try:
            resp = get("fs/tasks", connector_type="brslet")
            if resp.message != "success":
                return Exception(resp.message)
            
            tasks = []
            for item in resp.data or []:
                tasks.append(TaskInfo(
                    run_id=item.get('run_id', ''),
                    direction=item.get('direction', ''),
                    local_path=item.get('local_path', ''),
                    remote_path=item.get('remote_path', ''),
                    status=item.get('status', ''),
                    progress=item.get('progress', 0.0),
                    message=item.get('message', ''),
                    start_time=item.get('start_time', ''),
                    end_time=item.get('end_time', '')
                ))
            return tasks
        except Exception as e:
            return e

    async def list_tasks_async(self) -> Union[List[TaskInfo], Exception]:
        """异步获取所有任务列表"""
        try:
            resp = await async_get("fs/tasks", connector_type="brslet")
            if resp.message != "success":
                return Exception(resp.message)
            
            tasks = []
            for item in resp.data or []:
                tasks.append(TaskInfo(
                    run_id=item.get('run_id', ''),
                    direction=item.get('direction', ''),
                    local_path=item.get('local_path', ''),
                    remote_path=item.get('remote_path', ''),
                    status=item.get('status', ''),
                    progress=item.get('progress', 0.0),
                    message=item.get('message', ''),
                    start_time=item.get('start_time', ''),
                    end_time=item.get('end_time', '')
                ))
            return tasks
        except Exception as e:
            return e

    def cancel_task(self, run_id: str) -> Response:
        """取消任务
        
        Args:
            run_id: 任务ID
        """
        return post(Payload(path=f"fs/task/{run_id}/cancel", payload={}), connector_type="brslet")

    async def cancel_task_async(self, run_id: str) -> Response:
        """异步取消任务"""
        return await async_post(Payload(path=f"fs/task/{run_id}/cancel", payload={}), connector_type="brslet")

    def wait_task(self, run_id: str, timeout: int = 3600) -> Response:
        """等待任务完成
        
        Args:
            run_id: 任务ID
            timeout: 超时时间（秒）
            
        Returns:
            包含同步结果的Response
        """
        resp = post(Payload(path=f"fs/task/{run_id}/wait", payload={"timeout": timeout}), 
                    timeout=float(timeout + 10), connector_type="brslet")
        if resp.message != "success":
            return resp
        
        data = resp.data or {}
        return Response(
            message=data.get('message', 'success'),
            data=SyncResult(
                success=data.get('success', False),
                md5=data.get('md5', ''),
                size=data.get('size', 0),
                duration=data.get('duration', 0),
                message=data.get('message', '')
            )
        )

    async def wait_task_async(self, run_id: str, timeout: int = 3600) -> Response:
        """异步等待任务完成"""
        resp = await async_post(Payload(path=f"fs/task/{run_id}/wait", payload={"timeout": timeout}), 
                                timeout=float(timeout + 10), connector_type="brslet")
        if resp.message != "success":
            return resp
        
        data = resp.data or {}
        return Response(
            message=data.get('message', 'success'),
            data=SyncResult(
                success=data.get('success', False),
                md5=data.get('md5', ''),
                size=data.get('size', 0),
                duration=data.get('duration', 0),
                message=data.get('message', '')
            )
        )

    def walk(self, path: str = "") -> Generator[str, None, None]:
        """遍历文件系统目录
        
        Args:
            path: 相对于FileSystem根路径的目录路径
            
        Yields:
            每个文件和子目录的完整路径
        """
        try:
            resp = post(Payload(path="fs/list", payload={
                "root_path": self._root_path,
                "path": path,
                "pid": self._pid,
                "oid": self._oid,
                "uid": self._uid,
            }), connector_type="brslet")
            
            if resp.message != "success":
                return
            
            items = resp.data.get('items', []) if resp.data else []
            for item in items:
                item_path = os.path.join(path, item['name'])
                yield item_path
                if item.get('type') == 'dir':
                    yield from self.walk(item_path)
                    
        except Exception:
            return

    def exists(self, file_path: str) -> bool:
        """检查本地文件是否存在"""
        full_path = os.path.join(self._path, file_path)
        return os.path.exists(full_path)

    def delete_local(self, file_path: str) -> bool:
        """删除本地文件
        
        Args:
            file_path: 相对路径
            
        Returns:
            是否删除成功
        """
        try:
            full_path = os.path.join(self._path, file_path)
            if os.path.exists(full_path):
                os.remove(full_path)
                return True
            return False
        except Exception:
            return False

    def get_local_path(self, file_path: str) -> str:
        """获取文件的本地完整路径"""
        return os.path.join(self._path, file_path)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass


def Open(
    root_path: Optional[str] = None, 
    pid: Optional[str] = None, 
    oid: Optional[str] = None, 
    uid: Optional[str] = None, 
    fs_name: Optional[str] = None
) -> FileSystem:
    """打开文件系统
    
    Args:
        root_path: 根路径，默认从环境变量IEOPS_STORAGE_ROOT_PATH获取
        pid: 项目ID，默认从环境变量IEOPS_STORAGE_PID获取
        oid: 组织ID，默认从环境变量IEOPS_STORAGE_OID获取
        uid: 用户ID，默认从环境变量IEOPS_STORAGE_UID获取
        fs_name: 文件系统名称，默认从环境变量IEOPS_STORAGE_FS_NAME获取
        
    Returns:
        FileSystem实例
    """
    if fs_name is None or fs_name == '':
        fs_name = os.getenv('IEOPS_STORAGE_FS_NAME', 'jfs-dev')
    if pid is None or pid == '':
        pid = os.getenv('IEOPS_STORAGE_PID', 'u-00000000')
    if root_path is None or root_path == '':
        root_path = os.getenv('IEOPS_STORAGE_ROOT_PATH', "/mnt/juicefs")
    if oid is None or oid == '':
        oid = os.getenv('IEOPS_STORAGE_OID', 'ieops')
    if uid is None or uid == '':
        uid = os.getenv('IEOPS_STORAGE_UID', 'ieops')
    
    if not os.path.exists(root_path):
        os.makedirs(root_path, exist_ok=True)
    
    # 构建完整路径: /root_path/pid/fs_name/oid/uid
    path = os.path.join(root_path, pid, fs_name, oid, uid)
    
    return FileSystem(
        path=path, 
        root_path=root_path,
        pid=pid, 
        oid=oid, 
        uid=uid, 
        fs_name=fs_name
    )


# 便捷函数
def upload(
    file_path: str,
    data: Union[str, bytes],
    mode: str = 'w',
    encoding: str = 'utf-8',
    wait: bool = True,
    timeout: int = 3600,
    **kwargs
) -> Union[Response, TaskInfo, Exception]:
    """便捷上传函数
    
    Args:
        file_path: 文件路径
        data: 文件内容
        mode: 写入模式 'w' 或 'wb'
        encoding: 编码
        wait: 是否等待完成
        timeout: 超时时间
        **kwargs: 传递给Open()的参数
    """
    with Open(**kwargs) as fs:
        return fs.write(file_path, data, mode, encoding, wait, timeout)


async def upload_async(
    file_path: str,
    data: Union[str, bytes],
    mode: str = 'w',
    encoding: str = 'utf-8',
    wait: bool = True,
    timeout: int = 3600,
    **kwargs
) -> Union[Response, TaskInfo, Exception]:
    """异步便捷上传函数"""
    fs = Open(**kwargs)
    return await fs.write_async(file_path, data, mode, encoding, wait, timeout)


def download(
    file_path: str,
    mode: str = 'r',
    encoding: str = 'utf-8',
    timeout: int = 3600,
    **kwargs
) -> Union[IO, Exception]:
    """便捷下载函数
    
    Args:
        file_path: 文件路径
        mode: 读取模式 'r' 或 'rb'
        encoding: 编码
        timeout: 超时时间
        **kwargs: 传递给Open()的参数
    """
    fs = Open(**kwargs)
    return fs.read(file_path, mode, encoding, timeout)


async def download_async(
    file_path: str,
    mode: str = 'r',
    encoding: str = 'utf-8',
    timeout: int = 3600,
    **kwargs
) -> Union[IO, Exception]:
    """异步便捷下载函数"""
    fs = Open(**kwargs)
    return await fs.read_async(file_path, mode, encoding, timeout)


__all__ = [
    'FileSystem',
    'TaskInfo', 
    'SyncResult',
    'Response',
    'Open',
    'upload',
    'upload_async',
    'download',
    'download_async',
]
