# Copyright (c) 2025, JINGROW and contributors
# For license information, please see license.txt

"""
Jingrow App 生命周期管理
统一管理 app 的安装、卸载、路由注册等生命周期操作
"""

import sys
import logging
from pathlib import Path
from jingrow.utils.path import get_root_path
from typing import Set

logger = logging.getLogger(__name__)

# 已加载的 app 路径集合（避免重复添加）
_loaded_app_paths: Set[str] = set()


def get_project_root() -> Path:
    """获取项目根目录路径"""
    return get_root_path()


def get_apps_dir() -> Path:
    """获取 apps 目录路径"""
    return get_project_root() / "apps"


def get_apps_txt_path() -> Path:
    """获取 apps.txt 文件路径"""
    return get_apps_dir() / "apps.txt"


def ensure_apps_on_sys_path():
    """确保 apps.txt 中所有 app 的路径都在 sys.path 中"""
    apps_txt = get_apps_txt_path()
    if not apps_txt.exists():
        return
    
    apps_dir = get_apps_dir()
    for app_name in apps_txt.read_text(encoding='utf-8').splitlines():
        app_name = app_name.strip()
        if app_name:
            app_path = str(apps_dir / app_name)
            if app_path not in _loaded_app_paths and (apps_dir / app_name).exists():
                if app_path not in sys.path:
                    sys.path.insert(0, app_path)
                    logger.debug(f"已添加 app 路径: {app_path}")
                _loaded_app_paths.add(app_path)


def update_apps_txt(app_name: str, add: bool = True) -> bool:
    """
    更新 apps.txt 文件（末尾追加，保持顺序）
    
    Args:
        app_name: 应用名称
        add: True 添加，False 删除
    
    Returns:
        bool: 是否成功
    """
    try:
        apps_txt = get_apps_txt_path()
        apps_txt.parent.mkdir(parents=True, exist_ok=True)
        
        # 读取现有列表（保持顺序）
        lines = []
        if apps_txt.exists():
            lines = [ln.strip() for ln in apps_txt.read_text(encoding='utf-8').splitlines() if ln.strip()]
        
        # 添加或删除
        if add:
            if app_name not in lines:
                lines.append(app_name)
                logger.info(f"添加应用到 apps.txt: {app_name}")
                
                # 立即添加路径并清除缓存
                _add_app_to_sys_path(app_name)
        else:
            if app_name in lines:
                lines.remove(app_name)
                logger.info(f"从 apps.txt 移除应用: {app_name}")
                
                # 立即移除路径并清除缓存
                _remove_app_from_sys_path(app_name)
        
        # 写回文件
        apps_txt.write_text('\n'.join(lines) + ('\n' if lines else ''), encoding='utf-8')
        return True
    except Exception as e:
        logger.error(f"更新 apps.txt 失败: {e}", exc_info=True)
        return False


def _add_app_to_sys_path(app_name: str):
    """添加 app 路径到 sys.path 并清除相关缓存"""
    apps_dir = get_apps_dir()
    app_path = str(apps_dir / app_name)
    
    if (apps_dir / app_name).exists():
        if app_path not in sys.path:
            sys.path.insert(0, app_path)
            logger.info(f"已添加 app 路径到 sys.path: {app_path}")
        _loaded_app_paths.add(app_path)
        
        # 清除该 app 的模块缓存，确保能重新导入
        _clear_app_modules(app_name)


def _remove_app_from_sys_path(app_name: str):
    """从 sys.path 移除 app 路径并清除相关缓存"""
    apps_dir = get_apps_dir()
    app_path = str(apps_dir / app_name)
    
    # 从 sys.path 移除
    if app_path in sys.path:
        sys.path.remove(app_path)
        logger.info(f"已从 sys.path 移除: {app_path}")
    
    # 从已加载集合移除
    _loaded_app_paths.discard(app_path)
    
    # 清除模块缓存，确保卸载后无法访问
    _clear_app_modules(app_name)


def _clear_app_modules(app_name: str):
    """清除 app 相关的所有模块缓存"""
    modules_to_remove = [
        name for name in list(sys.modules.keys())
        if name == app_name or name.startswith(f"{app_name}.")
    ]
    
    for module_name in modules_to_remove:
        try:
            del sys.modules[module_name]
            logger.debug(f"已清除模块缓存: {module_name}")
        except KeyError:
            pass
    
    if modules_to_remove:
        logger.info(f"已清除 {len(modules_to_remove)} 个 {app_name} 相关模块缓存")


def get_apps_from_txt() -> Set[str]:
    """从 apps.txt 读取已注册的 app 列表"""
    apps_txt = get_apps_txt_path()
    if not apps_txt.exists():
        return set()
    
    return {ln.strip() for ln in apps_txt.read_text(encoding='utf-8').splitlines() if ln.strip()}


def is_app_registered(app_name: str) -> bool:
    """检查 app 是否已注册到 apps.txt"""
    return app_name in get_apps_from_txt()
