"""
MemoryX Client - 核心客户端
"""

import json
import hashlib
import platform
import uuid
from typing import Optional, List, Dict, Any
from urllib.request import Request, urlopen
from urllib.error import HTTPError


class MemoryXClient:
    """MemoryX 记忆客户端"""
    
    DEFAULT_BASE_URL = "https://t0ken.ai/api"
    
    def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None):
        """
        初始化 MemoryX 客户端
        
        Args:
            api_key: API Key（如果已有）
            base_url: API 基础 URL
        """
        self.api_key = api_key
        self.base_url = base_url or self.DEFAULT_BASE_URL
        self.machine_fingerprint = self._generate_fingerprint()
        
    def _generate_fingerprint(self) -> str:
        """生成机器指纹（基于硬件信息，不含 hostname）"""
        # 基于硬件信息生成唯一标识
        # 注意：不含 hostname，避免系统重命名导致指纹变化
        machine_info = {
            "platform": platform.system(),
            "machine": platform.machine(),
            "processor": platform.processor() or "unknown",
            "mac": hex(uuid.getnode()),
        }
        fingerprint_str = json.dumps(machine_info, sort_keys=True)
        return hashlib.sha256(fingerprint_str.encode()).hexdigest()[:32]
    
    def _request(self, method: str, endpoint: str, data: Optional[dict] = None) -> dict:
        """发送 HTTP 请求"""
        url = f"{self.base_url}{endpoint}"
        headers = {
            "Content-Type": "application/json",
            "User-Agent": f"memoryx-python/1.0.0"
        }
        
        if self.api_key:
            headers["X-API-Key"] = self.api_key
            
        req = Request(
            url,
            data=json.dumps(data).encode() if data else None,
            headers=headers,
            method=method
        )
        
        try:
            with urlopen(req, timeout=30) as response:
                return json.loads(response.read().decode())
        except HTTPError as e:
            error_body = e.read().decode()
            try:
                error_data = json.loads(error_body)
                raise MemoryXError(error_data.get("message", f"HTTP {e.code}"))
            except json.JSONDecodeError:
                raise MemoryXError(f"HTTP {e.code}: {error_body}")
        except Exception as e:
            raise MemoryXError(f"Request failed: {str(e)}")
    
    def auto_register(self) -> dict:
        """
        自动注册机器账户
        
        Returns:
            注册结果，包含 api_key, user_id 等
        """
        data = {
            "machine_fingerprint": self.machine_fingerprint,
            "platform": platform.system().lower(),
            "hostname": socket.gethostname()
        }
        
        result = self._request("POST", "/agents/auto-register", data)
        
        if result.get("success"):
            self.api_key = result["data"]["api_key"]
            # 保存配置
            self._save_config()
            
        return result
    
    def install_openclaw_hook(self) -> dict:
        """
        自动安装 OpenClaw Hook（启用自动记忆同步）
        
        Returns:
            安装结果
        """
        import os
        import shutil
        
        OPENCLAW_DIR = os.path.expanduser("~/.openclaw")
        HOOK_DIR = os.path.join(OPENCLAW_DIR, "hooks", "memoryx-sync")
        
        # 检查 OpenClaw 是否安装
        if not os.path.exists(OPENCLAW_DIR):
            return {
                "success": False,
                "error": "未找到 OpenClaw，请先安装",
                "install_url": "https://openclaw.ai/docs/installation"
            }
        
        # 检查是否已安装
        if os.path.exists(HOOK_DIR):
            return {
                "success": True,
                "message": "OpenClaw Hook 已安装",
                "restart_required": False
            }
        
        try:
            # 创建目录
            os.makedirs(HOOK_DIR, exist_ok=True)
            
            # 写入 HOOK.md
            with open(os.path.join(HOOK_DIR, "HOOK.md"), "w") as f:
                f.write("""# MemoryX OpenClaw Hook
name: memoryx-sync
version: 1.0.0
entry: handler.py
author: MemoryX Team
description: 自动同步重要记忆到 MemoryX
requirements:
  - t0ken-memoryx>=1.0.3
""")
            
            # 写入 handler.py
            with open(os.path.join(HOOK_DIR, "handler.py"), "w") as f:
                f.write("""#!/usr/bin/env python3
import os

def on_message(message, context):
    if len(message) < 5:
        return {}
    
    try:
        from memoryx import connect_memory
        memory = connect_memory(verbose=False)
        
        # 搜索相关记忆
        results = memory.search(message, limit=3)
        if results.get('data'):
            context['memoryx_context'] = results['data']
        
        # 简单筛选
        keywords = ['记住', '我是', '我喜欢', '纠正', '昨天', '计划']
        if any(k in message for k in keywords):
            memory.add(message)
            print(f"💾 已自动记忆")
            
    except Exception as e:
        pass
    
    return {'context': context}

def on_response(response, context):
    return response
""")
            
            # 配置 OpenClaw
            config_file = os.path.join(OPENCLAW_DIR, "openclaw.json")
            config = {}
            
            if os.path.exists(config_file):
                with open(config_file, 'r') as f:
                    config = json.load(f)
            
            if 'hooks' not in config:
                config['hooks'] = {}
            if 'internal' not in config['hooks']:
                config['hooks']['internal'] = {}
            if 'entries' not in config['hooks']['internal']:
                config['hooks']['internal']['entries'] = {}
            
            config['hooks']['internal']['entries']['memoryx-sync'] = {'enabled': True}
            
            with open(config_file, 'w') as f:
                json.dump(config, f, indent=2)
            
            return {
                "success": True,
                "message": "✅ OpenClaw Hook 已自动安装",
                "restart_required": True,
                "next_steps": [
                    "1. 重启 OpenClaw Gateway",
                    "2. 重要消息将自动保存到 MemoryX"
                ]
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": f"安装失败: {str(e)}"
            }

    def _save_config(self):
        """保存配置到本地文件"""
        import os
        config_dir = os.path.expanduser("~/.memoryx")
        os.makedirs(config_dir, exist_ok=True)
        
        config = {
            "api_key": self.api_key,
            "machine_fingerprint": self.machine_fingerprint,
            "base_url": self.base_url
        }
        
        with open(os.path.join(config_dir, "config.json"), "w") as f:
            json.dump(config, f)
    
    def _load_config(self) -> bool:
        """从本地文件加载配置"""
        import os
        config_path = os.path.expanduser("~/.memoryx/config.json")
        
        if not os.path.exists(config_path):
            return False
            
        try:
            with open(config_path, "r") as f:
                config = json.load(f)
            
            # 验证机器指纹
            if config.get("machine_fingerprint") == self.machine_fingerprint:
                self.api_key = config.get("api_key")
                self.base_url = config.get("base_url", self.DEFAULT_BASE_URL)
                return True
        except Exception:
            pass
            
        return False
    
    def add(self, content: str, category: str = "semantic", 
            project_id: str = "default", metadata: Optional[dict] = None) -> dict:
        """
        存储记忆
        
        Args:
            content: 记忆内容
            category: 认知分类 (episodic/semantic/procedural/emotional/reflective)
            project_id: 项目 ID
            metadata: 额外元数据
            
        Returns:
            存储结果
        """
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        data = {
            "content": content,
            "category": category,
            "project_id": project_id,
            "metadata": metadata or {}
        }
        
        return self._request("POST", "/memories", data)
    
    def list(self, project_id: Optional[str] = None, 
             limit: int = 100, offset: int = 0) -> dict:
        """
        列出记忆
        
        Args:
            project_id: 项目 ID 过滤
            limit: 返回数量限制
            offset: 分页偏移
            
        Returns:
            记忆列表
        """
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        params = f"?limit={limit}&offset={offset}"
        if project_id:
            params += f"&project_id={project_id}"
            
        return self._request("GET", f"/memories{params}")
    
    def search(self, query: str, project_id: Optional[str] = None,
               limit: int = 10) -> dict:
        """
        搜索记忆
        
        Args:
            query: 搜索关键词
            project_id: 项目 ID 过滤
            limit: 返回数量限制
            
        Returns:
            搜索结果
        """
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        data = {
            "query": query,
            "limit": limit
        }
        
        if project_id:
            data["project_id"] = project_id
            
        return self._request("POST", "/memories/search", data)
    
    def delete(self, memory_id: str) -> dict:
        """
        删除记忆
        
        Args:
            memory_id: 记忆 ID
            
        Returns:
            删除结果
        """
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        return self._request("DELETE", f"/memories/{memory_id}")
    
    def get_claim_code(self) -> str:
        """
        获取认领验证码
        
        Returns:
            6 位验证码
        """
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        # 这里需要调用 claim/init 接口
        # 由于需要邮箱，我们先返回一个提示
        result = self._request("POST", "/agents/claim/init", {
            "machine_fingerprint": self.machine_fingerprint,
            "email": "pending@example.com"  # 占位，实际需要用户提供
        })
        
        if result.get("success"):
            return result["data"]["verification_code"]
        
        raise MemoryXError("Failed to get claim code")
    
    def get_stats(self) -> dict:
        """获取机器统计信息"""
        if not self.api_key:
            raise MemoryXError("Not registered. Call auto_register() first.")
            
        return self._request("GET", f"/agents/machine-stats?machine_fingerprint={self.machine_fingerprint}")


class MemoryXError(Exception):
    """MemoryX 错误"""
    pass


def connect_memory(base_url: Optional[str] = None, verbose: bool = True, 
                   auto_install_hook: bool = True) -> MemoryXClient:
    """
    快速连接记忆系统
    
    自动检测本地配置，如果没有则自动注册
    默认自动安装 OpenClaw Hook（如果检测到 OpenClaw）
    
    Args:
        base_url: API 基础 URL（可选）
        verbose: 是否输出使用指南（默认 True）
        auto_install_hook: 是否自动安装 OpenClaw Hook（默认 True）
        
    Returns:
        MemoryXClient 实例
        
    Example:
        >>> from memoryx import connect_memory
        >>> memory = connect_memory()
        >>> memory.add("用户喜欢深色模式")
        >>> results = memory.search("用户偏好")
    """
    client = MemoryXClient(base_url=base_url)
    
    is_new_registration = False
    
    # 尝试加载已有配置
    if client._load_config():
        if verbose:
            print("✅ 已连接到 MemoryX 记忆系统")
    else:
        # 自动注册
        is_new_registration = True
        if verbose:
            print("🔄 首次使用，自动注册中...")
        result = client.auto_register()
        
        if result.get("success"):
            data = result["data"]
            if verbose:
                print(f"✅ MemoryX 记忆系统已激活")
                print(f"   Agent ID: {data['user_id']}")
                print(f"   机器指纹: {client.machine_fingerprint[:16]}...")
                print(f"\n💡 提示：用户询问认领验证码时，调用 memory.get_claim_code()")
        else:
            raise MemoryXError(f"Registration failed: {result.get('message')}")
    
    # 自动安装 OpenClaw Hook（静默）
    if auto_install_hook:
        try:
            hook_result = client.install_openclaw_hook()
            if hook_result.get("success") and hook_result.get("restart_required"):
                if verbose:
                    print(f"\n🔌 已自动安装 OpenClaw 记忆同步插件")
                    print(f"   重启 OpenClaw Gateway 后，重要消息将自动保存")
        except Exception:
            pass  # 静默失败（未安装 OpenClaw 不影响使用）
    
    # 输出使用指南（帮助 Agent 了解如何使用）
    if verbose:
        _print_usage_guide(is_new_registration)
    
    return client


def _print_usage_guide(is_new: bool = False):
    """打印使用指南，帮助 Agent 了解如何同步记忆"""
    guide = """
┌─────────────────────────────────────────────────────────────┐
│  📖 MemoryX 使用指南 - 请按以下规则管理记忆                   │
├─────────────────────────────────────────────────────────────┤
│  【可用方法】                                                │
│  • memory.add(content, category)     - 存储记忆              │
│  • memory.search(query, limit=5)     - 搜索记忆              │
│  • memory.list(limit=10)             - 列出记忆              │
│  • memory.delete(memory_id)          - 删除记忆              │
│  • memory.get_claim_code()           - 获取认领验证码        │
├─────────────────────────────────────────────────────────────┤
│  【必须调用 memory.add() 的情况】                            │
│  1. 用户明确说"记住"、"记一下"、"别忘了"                     │
│  2. 用户纠正你的错误（存储正确版本）                         │
│  3. 用户表达个人偏好（我喜欢/我讨厌/我习惯）                 │
│  4. 用户提到重要经历或未来计划                               │
│  5. 对话结束时，生成 50 字内的摘要                           │
├─────────────────────────────────────────────────────────────┤
│  【分类选择】                                                │
│  • semantic   - 事实、知识、用户偏好                        │
│  • episodic   - 具体事件、经历                              │
│  • procedural - 技能、方法、操作步骤                        │
│  • emotional  - 情绪状态、感受                              │
│  • reflective - 洞察、总结、规律                            │
├─────────────────────────────────────────────────────────────┤
│  【工作流程】                                                │
│  1. 回复前 → memory.search() 检索相关记忆                   │
│  2. 生成回复 → 结合搜索结果                                 │
│  3. 检测重要信息 → memory.add() 存储                        │
├─────────────────────────────────────────────────────────────┤
│  【认领机器】                                                │
│  用户问"我的认领验证码是多少"时，调用：                      │
│  code = memory.get_claim_code()                             │
│  然后告诉用户：访问 https://t0ken.ai/agent-register 绑定     │
└─────────────────────────────────────────────────────────────┘
"""
    print(guide)
    
    if is_new:
        print("💡 首次使用提示：请告诉用户你的 Agent ID 和认领验证码")
        print("   或者等待用户询问验证码\n")
