"""PC 侧专属远程文件传输 Mixin

此模块包含只能在 PC 侧运行的 API（不能在设备端 Python 内调用）：
- push(): 将 PC 本地文件/目录推送到设备
- pull(): 从设备拉取文件/目录到 PC 本地

原理: 通过 HTTP multipart POST 上传 / GET 下载，由引擎写入设备文件系统。
"""

from __future__ import annotations

import os
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from ..device import Device


class RemoteMixin:
    """PC 侧独有 API — 跨机器文件传输"""

    _client: ...

    # ─────────────────────────────────────────
    # PC → Device
    # ─────────────────────────────────────────

    def push(self, local: str, remote: str) -> None:
        """推送 PC 本地文件到设备

        Args:
            local:  PC 端本地文件路径
            remote: 设备目标完整路径（含文件名）

        Example:
            >>> d.push("C:/test.py", "/sdcard/test.py")
        """
        import requests as _req
        if not os.path.exists(local):
            raise FileNotFoundError(f"本地文件不存在: {local}")
        with open(local, "rb") as f:
            data = f.read()
        remote_dir = os.path.dirname(remote)
        filename = os.path.basename(remote)
        # 服务器 handle_post_file: 读取 multipart 字段 "path"(目录) + "file"(文件名+数据)
        # 再 os.path.join(path, filename) → 组合出最终路径
        resp = _req.post(
            f"{self._client.base_url}/post-file",  # type: ignore[attr-defined]
            data={"path": remote_dir or "/data/local/tmp"},
            files={"file": (filename, data, "application/octet-stream")},
            timeout=120,
        )
        if resp.status_code not in (200, 201):
            raise RuntimeError(f"push 失败: HTTP {resp.status_code} — {resp.text[:300]}")

    def push_dir(self, local_dir: str, remote_dir: str) -> int:
        """递归推送本地目录到设备

        Args:
            local_dir:  PC 端本地目录路径
            remote_dir: 设备目标目录路径

        Returns:
            已推送文件数量
        """
        if not os.path.isdir(local_dir):
            raise NotADirectoryError(f"本地目录不存在: {local_dir}")
        count = 0
        for root, dirs, files in os.walk(local_dir):
            for name in files:
                local_file = os.path.join(root, name)
                rel = os.path.relpath(local_file, local_dir).replace("\\", "/")
                remote_file = f"{remote_dir}/{rel}"
                self.push(local_file, remote_file)
                count += 1
        return count

    # ─────────────────────────────────────────
    # Device → PC
    # ─────────────────────────────────────────

    def pull(self, remote: str, local: str) -> None:
        """从设备拉取文件到 PC 本地

        Args:
            remote: 设备文件完整路径
            local:  PC 端保存路径（含文件名）

        Example:
            >>> d.pull("/sdcard/log.txt", "C:/log.txt")
        """
        data = self._client.get_bytes("/pull-file", params={"path": remote})  # type: ignore[attr-defined]
        local_dir = os.path.dirname(local)
        if local_dir:
            os.makedirs(local_dir, exist_ok=True)
        with open(local, "wb") as f:
            f.write(data)

    def pull_dir(self, remote_dir: str, local_dir: str) -> int:
        """从设备拉取目录到 PC（需要目录内的文件列表）

        Args:
            remote_dir: 设备目录路径
            local_dir:  PC 端目标目录

        Returns:
            已拉取文件数量
        """
        os.makedirs(local_dir, exist_ok=True)
        files = self.list_files(remote_dir)  # type: ignore[attr-defined]
        count = 0
        for f in files:
            name = f.name if hasattr(f, "name") else str(f)
            self.pull(f"{remote_dir}/{name}", os.path.join(local_dir, name))
            count += 1
        return count
