# -*- coding: utf-8 -*-
"""
虚拟环境类型检测模块
Author  : CraftPilot

支持的虚拟环境类型：
- conda: Anaconda/Miniconda 环境
- venv: Python 内置虚拟环境
- virtualenv: 第三方虚拟环境工具
- poetry: Poetry 包管理工具
- pipenv: Pipfile 风格的包管理工具
"""

import os
from typing import Optional, Dict, Any
from enum import Enum


class EnvType(Enum):
    """虚拟环境类型枚举"""
    CONDA = "conda"
    VENV = "venv"
    VIRTUALENV = "virtualenv"
    POETRY = "poetry"
    PIPENV = "pipenv"
    
    @classmethod
    def from_string(cls, value: str) -> 'EnvType':
        """从字符串转换为枚举值"""
        value_lower = value.lower()
        for env_type in cls:
            if env_type.value == value_lower:
                return env_type
        raise ValueError(f"未知的虚拟环境类型: {value}")


def detect_env_type(project_path: str) -> Optional[EnvType]:
    """
    根据项目文件自动检测虚拟环境类型
    
    检测逻辑优先级：
    1. pyproject.toml + [tool.poetry] → poetry
    2. Pipfile → pipenv
    3. venv/ 或 .venv/ 目录 → venv
    4. environment.yaml 或 environment.yml → conda
    5. 无法检测 → 返回 None
    
    Args:
        project_path: 项目根目录路径
        
    Returns:
        检测到的环境类型，如果无法检测则返回 None
    """
    if not os.path.isdir(project_path):
        return None
    
    # 1. 检测 Poetry 项目
    pyproject_path = os.path.join(project_path, 'pyproject.toml')
    if os.path.isfile(pyproject_path):
        try:
            with open(pyproject_path, 'r', encoding='utf-8') as f:
                content = f.read()
                if '[tool.poetry]' in content:
                    return EnvType.POETRY
        except Exception:
            pass
    
    # 2. 检测 pipenv 项目
    pipfile_path = os.path.join(project_path, 'Pipfile')
    if os.path.isfile(pipfile_path):
        return EnvType.PIPENV
    
    # 3. 检测 venv/virtualenv 项目
    venv_paths = ['venv', '.venv', 'env', '.env']
    for venv_name in venv_paths:
        venv_path = os.path.join(project_path, venv_name)
        if os.path.isdir(venv_path):
            # 检查是否包含虚拟环境的标志文件
            if _is_venv_directory(venv_path):
                return EnvType.VENV
    
    # 4. 检测 Conda 项目
    conda_files = ['environment.yaml', 'environment.yml']
    for conda_file in conda_files:
        conda_path = os.path.join(project_path, conda_file)
        if os.path.isfile(conda_path):
            return EnvType.CONDA
    
    # 无法自动检测
    return None


def _is_venv_directory(venv_path: str) -> bool:
    """
    检查目录是否是有效的虚拟环境目录
    
    Args:
        venv_path: 待检查的目录路径
        
    Returns:
        是否是有效的虚拟环境目录
    """
    # Windows 下检查 Scripts/python.exe
    if os.name != 'posix':
        python_path = os.path.join(venv_path, 'Scripts', 'python.exe')
        activate_path = os.path.join(venv_path, 'Scripts', 'activate.bat')
    else:
        # Linux/macOS 下检查 bin/python
        python_path = os.path.join(venv_path, 'bin', 'python')
        activate_path = os.path.join(venv_path, 'bin', 'activate')
    
    return os.path.isfile(python_path) or os.path.isfile(activate_path)


def get_env_config(config: Dict[str, Any], project_path: str) -> Dict[str, Any]:
    """
    合并自动检测结果与手动配置
    
    优先使用手动配置，如果未配置则使用自动检测结果。
    如果都没有则默认使用 conda（向后兼容）。
    
    Args:
        config: dson_config.yaml 中的配置字典
        project_path: 项目根目录路径
        
    Returns:
        包含环境配置的字典，包含以下字段：
        - env_type: EnvType 枚举值
        - env_path: 虚拟环境路径（venv/virtualenv 专用）
        - conda_env: Conda 环境名称（conda 专用）
    """
    result = {}
    
    # 获取手动配置的 env_type
    manual_env_type = config.get('env_type')
    
    if manual_env_type:
        # 使用手动配置
        result['env_type'] = EnvType.from_string(manual_env_type)
    else:
        # 尝试自动检测
        detected_type = detect_env_type(project_path)
        if detected_type:
            result['env_type'] = detected_type
        else:
            # 默认使用 conda（向后兼容）
            result['env_type'] = EnvType.CONDA
    
    # 根据环境类型设置其他配置
    env_type = result['env_type']
    
    if env_type == EnvType.CONDA:
        # Conda 环境：读取 conda_env 字段
        result['conda_env'] = config.get('conda_env', config.get('name', 'base'))
    
    elif env_type in (EnvType.VENV, EnvType.VIRTUALENV):
        # venv/virtualenv：读取 env_path 字段
        env_path = config.get('env_path')
        if not env_path:
            # 默认路径
            env_path = _detect_venv_path(project_path)
        result['env_path'] = env_path
    
    elif env_type == EnvType.POETRY:
        # Poetry：不需要额外配置，使用 poetry run
        pass
    
    elif env_type == EnvType.PIPENV:
        # pipenv：不需要额外配置，使用 pipenv run
        pass
    
    return result


def _detect_venv_path(project_path: str) -> str:
    """
    检测项目中的虚拟环境路径
    
    Args:
        project_path: 项目根目录路径
        
    Returns:
        虚拟环境的相对路径
    """
    venv_paths = ['venv', '.venv', 'env', '.env']
    for venv_name in venv_paths:
        venv_path = os.path.join(project_path, venv_name)
        if os.path.isdir(venv_path) and _is_venv_directory(venv_path):
            return './' + venv_name
    
    # 默认返回 ./venv
    return './venv'


def get_env_type_description(env_type: EnvType) -> str:
    """
    获取环境类型的描述文本
    
    Args:
        env_type: 环境类型枚举值
        
    Returns:
        环境类型的中文描述
    """
    descriptions = {
        EnvType.CONDA: "Conda (Anaconda/Miniconda)",
        EnvType.VENV: "venv (Python 内置虚拟环境)",
        EnvType.VIRTUALENV: "virtualenv (第三方虚拟环境)",
        EnvType.POETRY: "Poetry (现代 Python 包管理)",
        EnvType.PIPENV: "pipenv (Pipfile 风格包管理)",
    }
    return descriptions.get(env_type, "未知环境类型")


def validate_env_config(env_config: Dict[str, Any], project_path: str) -> tuple:
    """
    验证环境配置是否有效
    
    Args:
        env_config: get_env_config 返回的配置字典
        project_path: 项目根目录路径
        
    Returns:
        (is_valid, error_message) 元组
    """
    env_type = env_config.get('env_type')
    
    if not env_type:
        return False, "未指定虚拟环境类型"
    
    if env_type == EnvType.CONDA:
        conda_env = env_config.get('conda_env')
        if not conda_env:
            return False, "Conda 环境未指定环境名称 (conda_env)"
        # 注意：这里不检查 conda 环境是否存在，因为启动脚本会处理
        return True, None
    
    elif env_type in (EnvType.VENV, EnvType.VIRTUALENV):
        env_path = env_config.get('env_path')
        if not env_path:
            return False, "venv/virtualenv 环境未指定路径 (env_path)"
        
        # 解析相对路径
        if not os.path.isabs(env_path):
            full_path = os.path.join(project_path, env_path)
        else:
            full_path = env_path
        
        if not os.path.isdir(full_path):
            return False, f"虚拟环境目录不存在: {env_path}，请先创建虚拟环境"
        
        if not _is_venv_directory(full_path):
            return False, f"目录不是有效的虚拟环境: {env_path}"
        
        return True, None
    
    elif env_type == EnvType.POETRY:
        # 检查 pyproject.toml 是否存在
        pyproject_path = os.path.join(project_path, 'pyproject.toml')
        if not os.path.isfile(pyproject_path):
            return False, "Poetry 项目缺少 pyproject.toml 文件"
        return True, None
    
    elif env_type == EnvType.PIPENV:
        # 检查 Pipfile 是否存在
        pipfile_path = os.path.join(project_path, 'Pipfile')
        if not os.path.isfile(pipfile_path):
            return False, "pipenv 项目缺少 Pipfile 文件"
        return True, None
    
    return False, f"不支持的环境类型: {env_type}"
