"""
core/browser_profile.py - 浏览器 Profile 管理器
================================================
管理各网站的独立浏览器 Profile，实现登录状态持久化。

每个网站使用独立的 User Data Directory，Cookie/Session 不会互相干扰。
登录一次后，后续自动保持登录状态。

Profile 目录结构:
    ~/.myagent/browser_profiles/
    ├── google/          # Google 账号
    │   ├── user_data/   # Chromium user data directory
    │   └── cookies.json # Cookie 快照（可选，用于跨浏览器恢复）
    ├── x_com/           # X.com (Twitter)
    ├── weibo/           # 微博
    ├── wechat_mp/       # 微信公众号
    ├── douyin/          # 抖音
    ├── gmail/           # Gmail
    └── ...

用法:
    from core.browser_profile import BrowserProfileManager

    mgr = BrowserProfileManager()
    profile = mgr.get_profile("google")
    # profile.user_data_dir → 独立的用户数据目录路径
    # profile.is_logged_in → 是否已登录（基于 Cookie 检测）
    # profile.save_cookies(cookies_dict) → 保存 Cookie
    # profile.load_cookies() → 加载 Cookie
"""
from __future__ import annotations

import json
import os
import shutil
import time
from pathlib import Path
from typing import Any, Dict, List, Optional

from core.logger import get_logger

logger = get_logger("myagent.core.browser_profile")


# ════════════════════════════════════════════════════════════
# 预置 Profile（兼容性保留 — 优先从 site_registry 加载）
# ════════════════════════════════════════════════════════════
# v1.31.0: PRESET_PROFILES 不再硬编码，改为从 core.site_registry 动态获取。
# 此处保留为空 dict，由 get_preset_profiles() 延迟加载。

_PRESET_PROFILES_CACHE: Optional[Dict[str, Dict[str, str]]] = None


def get_preset_profiles() -> Dict[str, Dict[str, str]]:
    """获取预置 Profile 配置（从 SiteRegistry 延迟加载）。

    返回 {name: {display_name, login_url, detect_url}} 格式，
    保持与旧 PRESET_PROFILES 完全兼容。
    """
    global _PRESET_PROFILES_CACHE
    if _PRESET_PROFILES_CACHE is not None:
        return _PRESET_PROFILES_CACHE

    try:
        from core.site_registry import get_site_registry
        reg = get_site_registry()
        sites = reg.list_sites()
        _PRESET_PROFILES_CACHE = {}
        for s in sites:
            _PRESET_PROFILES_CACHE[s["name"]] = {
                "display_name": s["display_name"],
                "login_url": s.get("login_url", ""),
                "detect_url": s.get("detect_url", s.get("home_url", "")),
            }
        logger.debug(f"从 SiteRegistry 加载了 {len(_PRESET_PROFILES_CACHE)} 个预置 Profile")
        return _PRESET_PROFILES_CACHE
    except Exception as e:
        logger.warning(f"从 SiteRegistry 加载失败，使用空预置: {e}")
        _PRESET_PROFILES_CACHE = {}
        return _PRESET_PROFILES_CACHE


# 向后兼容别名
PRESET_PROFILES = {}  # 由 get_preset_profiles() 延迟填充，直接引用会得到空 dict
# 注意：BrowserProfile.__init__ 已改用 get_preset_profiles()，不再直接读 PRESET_PROFILES


class BrowserProfile:
    """
    单个网站的浏览器 Profile。

    管理一个网站对应的 User Data Directory 和 Cookie 存储。
    """

    def __init__(self, name: str, base_dir: Path, config: Optional[Dict] = None):
        """
        Args:
            name: Profile 名称（如 "google", "x_com"）
            base_dir: Profile 根目录
            config: 预置配置（来自 PRESET_PROFILES）
        """
        # [v1.32.2] 安全修复: 路径遍历防护
        if ".." in name or "/" in name or "\\" in name or not name:
            raise ValueError(f"无效的 Profile 名称: {name}")
        self.name = name
        self.base_dir = base_dir
        # 二次校验: 确保解析后的路径仍在 base_dir 内
        self.profile_dir = (base_dir / name).resolve()
        if not str(self.profile_dir).startswith(str(base_dir.resolve())):
            raise ValueError(f"Profile 名称 '{name}' 导致路径逃逸: {self.profile_dir}")
        self.user_data_dir = self.profile_dir / "user_data"
        self.cookies_file = self.profile_dir / "cookies.json"
        self.config_file = self.profile_dir / "config.json"

        # 预置配置（从 SiteRegistry 延迟加载）
        _presets = get_preset_profiles()
        preset = _presets.get(name, {})
        self.display_name = (config or preset).get("display_name", name)
        self.login_url = (config or preset).get("login_url", "")
        self.detect_url = (config or preset).get("detect_url", "")

        # 加载自定义配置（覆盖预置）
        self._custom_config: Dict[str, Any] = {}
        if self.config_file.exists():
            try:
                self._custom_config = json.loads(
                    self.config_file.read_text(encoding="utf-8")
                )
                self.display_name = self._custom_config.get(
                    "display_name", self.display_name
                )
                self.login_url = self._custom_config.get("login_url", self.login_url)
                self.detect_url = self._custom_config.get("detect_url", self.detect_url)
            except Exception as e:
                logger.warning(f"加载 Profile 配置失败 ({name}): {e}")

        # 状态缓存
        self._is_logged_in: Optional[bool] = None

    def ensure_dirs(self) -> None:
        """确保 Profile 目录结构存在"""
        self.profile_dir.mkdir(parents=True, exist_ok=True)

    def is_initialized(self) -> bool:
        """检查 Profile 是否已初始化（有 profile 目录且包含浏览器数据）"""
        if not self.profile_dir.is_dir():
            return False
        # 检查目录下是否有实际内容（不空）
        try:
            return any(self.profile_dir.iterdir())
        except OSError:
            return False

    def get_size_mb(self) -> float:
        """获取 Profile 占用空间（MB）"""
        if not self.profile_dir.exists():
            return 0.0
        total = 0
        for f in self.profile_dir.rglob("*"):
            if f.is_file():
                try:
                    total += f.stat().st_size
                except OSError:
                    pass
        return total / (1024 * 1024)

    def save_cookies(self, cookies: List[Dict[str, Any]]) -> None:
        """
        保存 Cookie 列表到 JSON 文件。

        Args:
            cookies: Cookie 列表，每个 Cookie 应包含 name, value, domain 等字段
        """
        self.ensure_dirs()
        try:
            data = {
                "saved_at": time.time(),
                "count": len(cookies),
                "cookies": cookies,
            }
            self.cookies_file.write_text(
                json.dumps(data, ensure_ascii=False, indent=2),
                encoding="utf-8",
            )
            self._is_logged_in = len(cookies) > 0
            logger.info(f"已保存 {len(cookies)} 个 Cookie ({self.name})")
        except Exception as e:
            logger.error(f"保存 Cookie 失败 ({self.name}): {e}")

    def load_cookies(self) -> List[Dict[str, Any]]:
        """
        从 JSON 文件加载 Cookie 列表。

        Returns:
            Cookie 字典列表
        """
        if not self.cookies_file.exists():
            return []
        try:
            data = json.loads(self.cookies_file.read_text(encoding="utf-8"))
            cookies = data.get("cookies", [])
            self._is_logged_in = len(cookies) > 0
            logger.info(f"已加载 {len(cookies)} 个 Cookie ({self.name})")
            return cookies
        except Exception as e:
            logger.error(f"加载 Cookie 失败 ({self.name}): {e}")
            return []

    def update_config(self, **kwargs) -> None:
        """更新 Profile 配置"""
        self._custom_config.update(kwargs)
        self.ensure_dirs()
        try:
            self.config_file.write_text(
                json.dumps(self._custom_config, ensure_ascii=False, indent=2),
                encoding="utf-8",
            )
            # 更新内存中的属性
            if "display_name" in kwargs:
                self.display_name = kwargs["display_name"]
            if "login_url" in kwargs:
                self.login_url = kwargs["login_url"]
            if "detect_url" in kwargs:
                self.detect_url = kwargs["detect_url"]
        except Exception as e:
            logger.error(f"更新 Profile 配置失败 ({self.name}): {e}")

    def clear_cookies(self) -> None:
        """清除保存的 Cookie"""
        if self.cookies_file.exists():
            self.cookies_file.unlink()
        self._is_logged_in = False
        logger.info(f"已清除 Cookie ({self.name})")

    def delete_profile(self) -> bool:
        """
        删除整个 Profile（包括 user_data 目录和 Cookie）。

        Returns:
            是否成功删除
        """
        try:
            if self.profile_dir.exists():
                shutil.rmtree(self.profile_dir)
            logger.info(f"已删除 Profile: {self.name}")
            return True
        except Exception as e:
            logger.error(f"删除 Profile 失败 ({self.name}): {e}")
            return False

    def to_dict(self) -> Dict[str, Any]:
        """返回 Profile 信息字典"""
        return {
            "name": self.name,
            "display_name": self.display_name,
            "login_url": self.login_url,
            "detect_url": self.detect_url,
            "initialized": self.is_initialized(),
            "size_mb": round(self.get_size_mb(), 2),
            "has_cookies": self.cookies_file.exists(),
        }


class BrowserProfileManager:
    """
    浏览器 Profile 管理器。

    管理 ~/.myagent/browser_profiles/ 下的所有网站 Profile。
    提供 Profile 的创建、获取、列表、删除等操作。

    用法:
        mgr = BrowserProfileManager()

        # 获取已有或创建新 Profile
        profile = mgr.get_profile("google")
        print(profile.user_data_dir)

        # 列出所有 Profile
        profiles = mgr.list_profiles()

        # 创建自定义 Profile
        profile = mgr.create_profile("my_site", login_url="https://example.com/login")

        # 删除 Profile
        mgr.delete_profile("google")
    """

    def __init__(self, base_dir: Optional[str] = None):
        """
        Args:
            base_dir: Profile 根目录，默认 ~/.myagent/browser_profiles/
        """
        if base_dir:
            self.base_dir = Path(base_dir)
        else:
            # 遵循项目配置的数据目录
            config_path = Path.home() / ".myagent" / "config.json"
            data_base = Path.home() / ".myagent"
            if config_path.exists():
                try:
                    cfg = json.loads(config_path.read_text(encoding="utf-8"))
                    custom = cfg.get("data_dir", "")
                    if custom:
                        expanded = os.path.expanduser(custom)
                        if not os.path.isabs(expanded):
                            expanded = str(Path.home() / expanded)
                        data_base = Path(expanded)
                except Exception:
                    pass
            self.base_dir = data_base / "browser_profiles"

        self.base_dir.mkdir(parents=True, exist_ok=True)
        logger.debug(f"Profile 管理器初始化: {self.base_dir}")

    def get_profile(self, name: str) -> BrowserProfile:
        """
        获取指定名称的 Profile（不存在则自动创建）。

        Args:
            name: Profile 名称

        Returns:
            BrowserProfile 实例
        """
        profile = BrowserProfile(name, self.base_dir)
        return profile

    def create_profile(
        self,
        name: str,
        display_name: str = "",
        login_url: str = "",
        detect_url: str = "",
    ) -> BrowserProfile:
        """
        创建自定义 Profile。

        Args:
            name: Profile 名称（英文，用于目录名）
            display_name: 显示名称
            login_url: 登录页 URL
            detect_url: 登录状态检测 URL

        Returns:
            BrowserProfile 实例
        """
        profile = BrowserProfile(name, self.base_dir)
        profile.ensure_dirs()

        config = {}
        if display_name:
            config["display_name"] = display_name
        if login_url:
            config["login_url"] = login_url
        if detect_url:
            config["detect_url"] = detect_url

        if config:
            profile.update_config(**config)

        logger.info(f"已创建 Profile: {name}")
        return profile

    def list_profiles(self) -> List[Dict[str, Any]]:
        """
        列出所有已初始化的 Profile。

        Returns:
            Profile 信息列表
        """
        profiles = []
        if not self.base_dir.exists():
            return profiles

        for subdir in sorted(self.base_dir.iterdir()):
            if not subdir.is_dir() or subdir.name.startswith("."):
                continue
            profile = BrowserProfile(subdir.name, self.base_dir)
            if profile.is_initialized():
                profiles.append(profile.to_dict())

        return profiles

    def delete_profile(self, name: str) -> bool:
        """
        删除指定 Profile。

        Args:
            name: Profile 名称

        Returns:
            是否成功
        """
        profile = self.get_profile(name)
        return profile.delete_profile()

    def get_preset_names(self) -> List[str]:
        """获取所有预置 Profile 名称列表（从 SiteRegistry 动态获取）"""
        return list(get_preset_profiles().keys())

    def init_presets(self) -> List[str]:
        """
        初始化所有预置 Profile（仅创建目录结构，不自动登录）。
        预置列表从 SiteRegistry 动态获取。

        Returns:
            已初始化的 Profile 名称列表
        """
        _presets = get_preset_profiles()
        initialized = []
        for name, config in _presets.items():
            profile = self.get_profile(name)
            if not profile.is_initialized():
                profile.ensure_dirs()
                initialized.append(name)
                logger.info(f"已初始化预置 Profile: {name}")
        return initialized

    def get_total_size_mb(self) -> float:
        """获取所有 Profile 的总占用空间（MB）"""
        total = 0.0
        if self.base_dir.exists():
            for f in self.base_dir.rglob("*"):
                if f.is_file():
                    try:
                        total += f.stat().st_size
                    except OSError:
                        pass
        return total / (1024 * 1024)


# ── 全局单例 ──

_profile_manager: Optional[BrowserProfileManager] = None


def get_browser_profile_manager() -> BrowserProfileManager:
    """获取全局 BrowserProfileManager 单例"""
    global _profile_manager
    if _profile_manager is None:
        _profile_manager = BrowserProfileManager()
    return _profile_manager
