import os
from datetime import datetime, timezone, timedelta
import json
import time
import threading
import re
import urllib.request
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from qiniu import Auth, put_file_v2, CdnManager, BucketManager
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

claw = "claw"

BASE_URL = f"https://cdn.yiranlaoshi.com/{claw}"

QINIU_KEY = "T3tgxM7EMx1j4VESw4m4PIfFXoOvBo-wQEOQewXX"
QINIU_TOKEN = "PVZvlKVOjX2RqlV2ILMg-QwpNMssOlpVbaEzypz0"


QINIU_BUCKET = "yiran1"

access_key = QINIU_KEY
secret_key = QINIU_TOKEN
bucket_name = QINIU_BUCKET
q = Auth(access_key, secret_key)
cdn_manager = CdnManager(q)
bucket = BucketManager(q)


class MyHandler(FileSystemEventHandler):
    def _is_file_event(self, event):
        return not getattr(event, "is_directory", False)

    def on_modified(self, event):
        if not self._is_file_event(event):
            return
        print(f"🟡 修改: {event.src_path}")
        file_gen(event.src_path, "add")

    def on_deleted(self, event):
        if not self._is_file_event(event):
            return
        print(f"🔴 删除: {event.src_path}")
        file_gen(event.src_path, "delete")

    def on_moved(self, event):
        if not self._is_file_event(event):
            return
        print(f"🔵 移动: {event.src_path} -> {getattr(event, 'dest_path', '')}")
        file_gen(event.src_path, "delete")
        if getattr(event, 'dest_path', ''):
            file_gen(event.dest_path, "add")


def now_iso():
    # 生成 +08:00 时间格式
    tz = timezone(timedelta(hours=8))
    return datetime.now(tz).isoformat()


def gen_id():
    return f"asset-{int(datetime.now().timestamp() * 1000)}"


def get_type(file_path):
    return file_path.split(".")[-1] if "." in file_path else "unknown"


def init_config(workspace_id, file_path, method="add"):
    file = f"/root/.openclaw/{workspace_id}/.myclaw/__MY_ARTIFACTS__.json"

    # 确保目录存在
    os.makedirs(os.path.dirname(file), exist_ok=True)

    now = now_iso()

    # 如果文件不存在，先初始化（包含根级创建/更改时间）
    if not os.path.exists(file):
        data = {
            "workspace_id": workspace_id,
            "base_url": BASE_URL,
            "assets": [],
            "created_at": now,
            "updated_at": now
        }
    else:
        with open(file, "r", encoding="utf-8") as f:
            try:
                data = json.load(f)
            except BaseException:
                data = {
                    "workspace_id": workspace_id,
                    "assets": [],
                    "created_at": now,
                    "updated_at": now
                }
        # 确保根级时间字段存在；保留已有 created_at，更新 updated_at
        if "created_at" not in data:
            data["created_at"] = now
        data["updated_at"] = now

    if method == "delete":
        # 删除逻辑：将匹配 path 的项过滤掉，并更新根级更新时间
        data["assets"] = [asset for asset in data.get(
            "assets", []) if asset.get("path") != file_path]
        data["updated_at"] = now
    else:
        found = False
        # 查找是否已存在该 path
        for asset in data.get("assets", []):
            if asset.get("path") == file_path:
                # 保证已有 created_at，不被覆盖；更新 updated_at 和 type（以防扩展名变化）
                asset.setdefault("created_at", now)
                asset["updated_at"] = now
                asset["type"] = get_type(file_path)
                found = True
                break

        # 不存在则新增（asset 已包含创建/更改时间）
        if not found:
            new_asset = {
                "id": gen_id(),
                "type": get_type(file_path),
                "path": file_path,
                "created_at": now,
                "updated_at": now
            }
            data["assets"].append(new_asset)

        # 更新根级更新时间
        data["updated_at"] = now

    # 写回文件
    with open(file, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)


def file_gen(path, method, timestamp=None):
    # 排除配置记录文件自身，防止循环触发
    if "__MY_ARTIFACTS__.json" in path:
        return

    # 规范化路径，兼容 Windows 和 *nix
    norm_path = os.path.normpath(path)
    parts = norm_path.split(os.sep)

    # 查找包含 workspace 标识的分段（支持 workspace 和 workspace-xxx 两种格式）
    space_idx = -1
    for i, p in enumerate(parts):
        # 匹配精确的 "workspace" 或以 "workspace-" 开头的目录
        if p == "workspace" or p.startswith("workspace-"):
            space_idx = i
            break

    if space_idx == -1:
        # 路径中不包含 workspace，不处理
        return

    space_id = parts[space_idx]
    # 相对路径：workspace 后面的所有段，使用 '/' 作为存储格式（保证跨平台一致）
    relative_parts = parts[space_idx + 1:]
    if not relative_parts:
        # 没有文件名，可能是目录事件，忽略
        return
    relative_path = "/".join(relative_parts)

    # 上传 key 保留 workspace 段（用于 CDN 存储），包含本地目录的时间戳
    path_url = f"{space_id}/{relative_path}"
    # 使用 claw 前缀作为存储 key 的一部分，保证与 CDN 访问路径一致
    key = f"{claw}/{path_url}"

    if method == "delete":
        # 删除时不需要上传，只清理配置文件记录，传入的 file_path 不包含 space_id（relative_path）
        init_config(space_id, relative_path, method="delete")
        print(f"🗑️ 已删除配置记录: {relative_path}")
    else:
        # 添加或修改时进行上传
        try:
            token = q.upload_token(bucket_name, key, 3600)
            # 直接使用 put_file_v2 上传文件路径，避免读取到空内容时导致 SDK 报错缺少 data 参数
            ret, info = put_file_v2(token, key, path)

            # 刷新 CDN
            cdn_url = f"https://cdn.yiranlaoshi.com/{key}"
            cdn_ret, cdn_info = cdn_manager.refresh_urls([cdn_url])

            # 记录时使用不包含 workspace 的相对路径
            init_config(space_id, relative_path, method="add")
            print(f"🚀 已上传并记录: {relative_path}")
            print(
                f"风 CDN刷新: {cdn_url} - {cdn_info.status_code if info else 'unknown'}")
        except Exception as e:
            print(f"❌ 上传/读取文件失败: {e}")


def list_files_from_qiniu(bucket_name, prefix, auth):
    """
    从七牛云列出指定前缀的所有文件

    Args:
        bucket_name: 空间名称
        prefix: 文件前缀
        auth: 认证对象

    Returns:
        list: 文件列表
    """
    bucket = BucketManager(auth)
    marker = None
    files = []

    while True:
        ret, eof, info = bucket.list(
            bucket_name, prefix=prefix, marker=marker, limit=1000)
        if ret is not None:
            for item in ret.get('items', []):
                files.append(item)
            marker = ret.get('marker')
            if eof:
                break
        else:
            break

    return files


def download_qiniu_file(bucket_manager, bucket_name, key, local_path):
    """
    从七牛云下载单个文件

    Args:
        bucket_manager: BucketManager 对象
        bucket_name: 空间名称
        key: 文件 key
        local_path: 本地路径

    Returns:
        bool: 下载是否成功
    """
    import os

    # 生成私有空间的下载链接
    base_url = f"http://cdn.yiranlaoshi.com/{key}"

    # 尝试使用多种方法生成私有链接
    private_url = None
    try:
        # 方法1: 使用 bucket_manager.private_url
        if hasattr(bucket_manager, 'private_url'):
            private_url = bucket_manager.private_url(base_url)
    except Exception:
        pass

    if private_url is None:
        try:
            # 方法2: 使用 q.private_url
            private_url = q.private_url(base_url)
        except Exception:
            pass

    if private_url is None:
        # 方法3: 直接使用公共 URL
        private_url = base_url

    print(f" download URL: {private_url}")

    # 确保目录存在
    os.makedirs(os.path.dirname(local_path), exist_ok=True)

    try:
        # 下载文件
        urllib.request.urlretrieve(private_url, local_path)
        return True
    except Exception as e:
        print(f"下载文件失败 {key}: {e}")
        # 尝试使用公共 URL
        public_url = base_url
        try:
            urllib.request.urlretrieve(public_url, local_path)
            return True
        except Exception as e2:
            print(f"  公共 URL 也失败: {e2}")
            return False


def extract_cdn_prefix_from_url(remote_url):
    """
    从 URL 中提取 CDN 存储路径前缀

    Args:
        remote_url: 远程 URL，例如 https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html

    Returns:
        str: CDN 存储路径前缀，例如 "myclaw/show/claw/"
    """
    # 从 URL 中提取路径部分
    match = re.search(r'https?://[^/]+(/[^?]*)/', remote_url)
    if match:
        url_path = match.group(1)
        # 移除末尾的 /
        url_path = url_path.rstrip('/')
        # 找到 workspace- 开始的部分
        workspace_match = re.search(r'(.*)/workspace-', url_path)
        if workspace_match:
            return workspace_match.group(1) + '/'
    return None


def sync_workspace_from_cdn(remote_url, timestamp=None):
    """
    从 CDN 同步 workspace 目录到本地

    Args:
        remote_url: 远程 URL，例如 https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html
        timestamp: 时间戳，如果不提供则自动生成

    Returns:
        dict: 包含成功状态和相关信息
    """
    try:
        # 解析远程 URL 以获取路径和 workspace 名称
        parsed_url = urlparse(remote_url)
        # 例如: myclaw/show/claw/workspace-huangxicheng/2/parkour.html
        url_path = parsed_url.path.lstrip('/')

        # 提取 workspace 名称 (匹配 workspace- 开的部分)
        match = re.search(r'(workspace-[^/]+)', url_path)
        if not match:
            return {
                "success": False,
                "error": "无法从 URL 中提取 workspace 名称"
            }

        workspace_name = match.group(1)
        print(f"检测到 workspace 名称: {workspace_name}")

        # 提取 workspace 前面的所有前缀 (例如 myclaw/show/claw/)
        workspace_idx_in_path = url_path.find(workspace_name)
        remote_prefix = url_path[:workspace_idx_in_path]
        print(f"检测到远程前缀: {remote_prefix}")

        # 生成时间戳（如果未提供）
        if timestamp is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

        # 新的本地目录名
        local_dir_name = f"{workspace_name}_{timestamp}"
        print(f"新目录名: {local_dir_name}")

        # 目标路径
        target_dir = f"/root/.openclaw/{local_dir_name}"
        print(f"目标路径: {target_dir}")

        # 七牛云存储的查询前缀
        source_prefix = f"{remote_prefix}{workspace_name}/"
        print(f"七牛云存储查询前缀: {source_prefix}")

        # 尝试的前缀列表
        prefixes_to_try = [source_prefix]

        print(f"源目录前缀: {source_prefix}")
        print(f"Bucket 名称: {QINIU_BUCKET}")
        for prefix in prefixes_to_try:
            print(f"尝试使用前缀: {prefix}")

        # 执行 myclaw new 命令
        print(f"执行 myclaw new {local_dir_name}...")
        import subprocess
        result = subprocess.run(
            ["myclaw", "new", local_dir_name],
            cwd="/root/.openclaw",
            capture_output=True,
            text=True
        )

        if result.returncode != 0:
            return {
                "success": False,
                "error": f"myclaw new 命令执行失败: {result.stderr}",
                "stdout": result.stdout
            }

        print(f"myclaw new 执行成功: {result.stdout}")

        # 列出源目录的所有文件（尝试多个前缀）
        print(f"开始从七牛云列出文件...")
        files = []
        for prefix in prefixes_to_try:
            print(f"尝试前缀: {prefix}")
            prefix_files = list_files_from_qiniu(QINIU_BUCKET, prefix, q)
            print(f"  找到 {len(prefix_files)} 个文件")
            if prefix_files:
                files.extend(prefix_files)
                print(f"  使用前缀 {prefix} 找到文件，停止尝试其他前缀")
                break

        print(f"总共找到 {len(files)} 个文件")

        if not files:
            return {
                "success": False,
                "error": "未找到任何文件"
            }

        # 下载所有文件
        success_count = 0
        fail_count = 0
        for item in files:
            key = item.get('key', '')
            if key.startswith(source_prefix):
                # 提取相对于 workspace 的路径，例如 "2/parkour.html"
                file_name = key[len(source_prefix):]

                # 移除第一级目录（如版本号 "2/"）
                # 用户要求：被复制之后不需要版本号目录
                parts = file_name.split('/')
                if len(parts) > 1:
                    file_name = '/'.join(parts[1:])
            else:
                # 不应该发生，因为我们按前缀列出的
                continue

            local_file_path = os.path.join(target_dir, file_name)

            if download_qiniu_file(bucket, QINIU_BUCKET, key, local_file_path):
                success_count += 1
                print(f"  ✓ 下载: {file_name}")
            else:
                fail_count += 1

        print(f"下载完成: 成功 {success_count} 个, 失败 {fail_count} 个")

        if success_count == 0:
            return {
                "success": False,
                "error": "没有文件下载成功"
            }

        # 构建访问 URL (使用带时间戳的本地目录名作为 CDN 路径)
        access_url = f"https://cdn.yiranlaoshi.com/{claw}/{local_dir_name}/"

        return {
            "success": True,
            "message": "同步成功",
            "workspace_name": workspace_name,
            "local_dir": local_dir_name,
            "local_path": target_dir,
            "access_url": access_url,
            "download_count": success_count
        }

    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


class FlashPolicyHandler(BaseHTTPRequestHandler):
    """Flash跨域策略请求处理器"""

    def log_message(self, format, *args):
        """简化日志输出"""
        print(f"[FlashPolicy] {args[0]}")

    def do_GET(self):
        """处理GET请求"""
        if self.path == '/' or self.path == '':
            # 返回简单欢迎信息
            self.send_response(200)
            self.send_header('Content-type', 'text/html; charset=utf-8')
            self.end_headers()
            response = {
                "status": "ok",
                "message": "Flash同步服务运行中",
                "endpoints": {
                    "/sync": "同步远程workspace到本地"
                }
            }
            self.wfile.write(json.dumps(response, ensure_ascii=False).encode())
        elif self.path.startswith('/sync'):
            # 处理同步请求
            self.handle_sync()
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain; charset=utf-8')
            self.end_headers()
            self.wfile.write(b"Not Found")

    def do_POST(self):
        """处理POST请求"""
        if self.path == '/sync':
            self.handle_sync()
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain; charset=utf-8')
            self.end_headers()
            self.wfile.write(b"Not Found")

    def handle_sync(self):
        """处理同步请求"""
        try:
            content_length = int(self.headers.get('Content-Length', 0))

            if content_length > 0:
                # 从JSON body中读取参数
                post_data = self.rfile.read(content_length).decode('utf-8')
                data = json.loads(post_data)
                remote_url = data.get('url', '')
            else:
                # 从URL查询参数中读取
                parsed = urlparse(self.path)
                params = parse_qs(parsed.query)
                remote_url = params.get('url', [''])[0]

            if not remote_url:
                self.send_response(400)
                self.send_header(
                    'Content-type', 'application/json; charset=utf-8')
                self.end_headers()
                response = {
                    "success": False,
                    "error": "缺少必要的参数: url"
                }
                self.wfile.write(json.dumps(
                    response, ensure_ascii=False).encode())
                return

            print(f"收到同步请求: {remote_url}")

            # 执行同步
            result = sync_workspace_from_cdn(remote_url)

            if result["success"]:
                self.send_response(200)
            else:
                self.send_response(500)

            self.send_header('Content-type', 'application/json; charset=utf-8')
            self.end_headers()
            self.wfile.write(json.dumps(result, ensure_ascii=False).encode())

        except Exception as e:
            self.send_response(500)
            self.send_header('Content-type', 'application/json; charset=utf-8')
            self.end_headers()
            response = {
                "success": False,
                "error": str(e)
            }
            self.wfile.write(json.dumps(response, ensure_ascii=False).encode())

    def do_OPTIONS(self):
        """处理OPTIONS请求（Flash预检）"""
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()

    def do_HEAD(self):
        """处理HEAD请求"""
        self.send_response(200)
        self.send_header('Content-type', 'text/html; charset=utf-8')
        self.end_headers()


def run_http_server(port=8080):
    """启动HTTP服务器"""
    server_address = ('', port)
    httpd = HTTPServer(server_address, FlashPolicyHandler)
    print(f"HTTP服务器启动，监听端口: {port}")
    print(f"同步接口示例:")
    print(
        f"  GET:  http://localhost:{port}/sync?url=https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html")
    print(f"  POST: http://localhost:{port}/sync")
    print(f"        Body: url=https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html")
    httpd.serve_forever()


if __name__ == "__main__":
    path = "/root/.openclaw"  # 你要监听的目录
    port = 8080  # HTTP服务端口

    # 启动文件监听器
    event_handler = MyHandler()
    observer = Observer()
    observer.schedule(event_handler, path, recursive=True)
    observer.start()
    print(f"✅ 文件监听器启动，监听目录: {path}")

    # 启动HTTP服务器（在单独线程中）
    http_thread = threading.Thread(
        target=run_http_server,
        args=(port,),
        daemon=True
    )
    http_thread.start()
    print(f"✅ HTTP服务器启动，监听端口: {port}")
    print(f"📝 同步接口示例:")
    print(
        f"   GET:  http://localhost:{port}/sync?url=https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html")
    print(f"   POST: http://localhost:{port}/sync")
    print(f"         Body: url=https://cdn.yiranlaoshi.com/myclaw/show/claw/workspace-huangxicheng/2/parkour.html")

    # 保持主进程运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()