import subprocess
import sys
import os
from pathlib import Path
from typing import Dict, Any, Optional
from getpass import getpass

from leaf_publisher.toml_utils import dump_toml, load_toml
from leaf_publisher.git_utils import get_git_config, get_git_version
from leaf_publisher.deps import generate_dependencies
from leaf_publisher.config import get_publisher_config


def check_readme(project_dir: Path) -> None:
    """强制要求项目根目录包含 README.md，否则报错退出"""
    if not (project_dir / 'README.md').exists():
        print("❌ 错误：项目目录中缺少 README.md 文件。", file=sys.stderr)
        print("   请先创建 README.md 后再进行发布。", file=sys.stderr)
        sys.exit(1)
    print("✅ README.md 存在。")


def create_pyproject_toml(project_dir: Path,
                          scan_deps: bool = True,
                          version_policy: str = 'minimum',
                          use_git_version: bool = False,
                          use_cache: bool = True) -> Dict[str, Any]:
    """
    创建全新的 pyproject.toml，包含基础元数据，并根据配置扫描依赖。
    返回写入的 pyproject_data 字典。
    """
    proj_name = project_dir.name
    author_name = get_git_config('user.name')
    author_email = get_git_config('user.email')
    authors = []
    if author_name:
        author_entry = {"name": author_name}
        if author_email:
            author_entry["email"] = author_email
        authors.append(author_entry)

    pyproject_data = {
        "build-system": {
            "requires": ["setuptools>=61.0"],
            "build-backend": "setuptools.build_meta"
        },
        "project": {
            "name": proj_name,
            "version": "0.1.0",
            "authors": authors,
            "description": "",
            "readme": "README.md",
            "requires-python": ">=3.7",
            "classifiers": [
                "Programming Language :: Python :: 3",
                "License :: OSI Approved :: MIT License",
                "Operating System :: OS Independent",
            ],
        },
        "tool": {}
    }
    if not authors:
        del pyproject_data["project"]["authors"]

    # 若启用 Git 版本标签
    if use_git_version:
        git_ver = get_git_version(project_dir)
        if git_ver:
            pyproject_data["project"]["version"] = git_ver
            print(f"✅ 从 Git 标签读取版本：{git_ver}")

    if scan_deps:
        print("🔍 正在扫描项目中的第三方依赖...")
        deps = generate_dependencies(project_dir, version_policy, use_cache=use_cache, source='scan')
        if deps:
            pyproject_data["project"]["dependencies"] = deps
            print(f"✅ 已发现 {len(deps)} 个依赖，已写入 pyproject.toml")
        else:
            print("ℹ️ 未发现可识别的第三方依赖。")

    pyproject_path = project_dir / 'pyproject.toml'
    dump_toml(pyproject_data, pyproject_path)
    print(f"✅ 已创建 pyproject.toml：{pyproject_path}")
    return pyproject_data


def ensure_pyproject_toml(project_dir: Path,
                          scan_deps: bool = True,
                          version_policy: str = 'minimum',
                          use_git_version: bool = False,
                          use_cache: bool = True) -> Dict[str, Any]:
    """
    确保项目存在 pyproject.toml，若不存在则创建（含依赖扫描）。
    返回 pyproject 字典。
    """
    pyproject_path = project_dir / 'pyproject.toml'
    if not pyproject_path.exists():
        return create_pyproject_toml(project_dir, scan_deps, version_policy, use_git_version, use_cache)

    # 已有文件，加载并做版本提醒
    pyproject_data = load_toml(pyproject_path)
    project_meta = pyproject_data.get("project", {})
    if "version" in project_meta and project_meta["version"] in ("0.1.0", "0.0.1", "0.0.0"):
        print(f"💡 当前项目版本为 {project_meta['version']}，发布前请确认是否已更新。")
    return pyproject_data


def merge_requirements_to_dependencies(project_dir: Path,
                                       pyproject_data: Dict[str, Any]) -> bool:
    """合并 requirements.txt 到 dependencies（若存在且不重复）"""
    req_file = project_dir / "requirements.txt"
    if not req_file.exists():
        return False

    deps = []
    with open(req_file, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line and not line.startswith('#'):
                deps.append(line)

    if not deps:
        return False

    project = pyproject_data.setdefault("project", {})
    existing = project.get("dependencies", [])
    new_deps = list(set(existing + deps))
    if set(existing) == set(new_deps):
        return False

    project["dependencies"] = new_deps
    return True


def build_package(project_dir: Path) -> None:
    """执行 python -m build 构建分发包"""
    print("📦 正在构建分发包...")
    try:
        subprocess.run(
            [sys.executable, '-m', 'build'],
            cwd=project_dir,
            check=True,
            capture_output=True,
            text=True
        )
        print("✅ 构建成功。")
    except subprocess.CalledProcessError as e:
        print(f"❌ 构建失败：\n{e.stderr}", file=sys.stderr)
        sys.exit(1)


def upload_to_pypi(project_dir: Path, repository: str, token: str = None,
                   repository_url: str = None, username: str = None, password: str = None) -> None:
    """
    上传到 PyPI。
    如果提供了 repository_url，则使用该 URL（忽略 repository 名称）。
    如果提供了 username，则使用用户名+密码方式；否则使用 token（默认用户名 __token__）。
    token/密码可以通过参数传入，也可以通过环境变量 TWINE_PASSWORD 或 PYPI_TOKEN 传入。
    """
    cmd = ['twine', 'upload']
    if repository_url:
        cmd.extend(['--repository-url', repository_url])
    else:
        cmd.extend(['--repository', repository])

    # 处理认证
    if username:
        cmd.extend(['--username', username])
        # 密码从参数或环境变量获取
        if password is None:
            password = os.environ.get('TWINE_PASSWORD') or os.environ.get('PYPI_PASSWORD')
        if password is None:
            password = getpass("🔑 请输入密码: ")
        input_data = password + '\n'
    else:
        # 使用 token
        cmd.extend(['--username', '__token__'])
        if token is None:
            token = os.environ.get('TWINE_PASSWORD') or os.environ.get('PYPI_TOKEN')
        if token is None:
            token = getpass("🔑 请输入 PyPI API token: ")
        input_data = token + '\n'

    cmd.append('dist/*')

    print(f"⬆️  正在上传...")
    try:
        proc = subprocess.Popen(
            cmd,
            cwd=project_dir,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        stdout, stderr = proc.communicate(input=input_data)
        if proc.returncode != 0:
            print(f"❌ 上传失败：\n{stderr}", file=sys.stderr)
            sys.exit(1)
        print("✅ 上传成功。")
        print(stdout)
    except FileNotFoundError:
        print("❌ 未找到 twine 命令，请安装 twine：pip install twine", file=sys.stderr)
        sys.exit(1)


# ========== 新增功能 ==========

def install_local(project_dir: Path, editable: bool = False) -> None:
    """将当前项目安装到本地 Python 环境"""
    cmd = [sys.executable, '-m', 'pip', 'install']
    if editable:
        cmd.append('-e')
    cmd.append('.')
    print(f"🔧 正在安装到本地Python环境（{'可编辑模式' if editable else '普通模式'}）...")
    try:
        subprocess.run(cmd, cwd=project_dir, check=True, capture_output=True, text=True)
        print("✅ 本地安装成功。")
    except subprocess.CalledProcessError as e:
        print(f"❌ 本地安装失败：\n{e.stderr}", file=sys.stderr)
        sys.exit(1)


def run_tests(project_dir: Path, test_command: Optional[str] = None) -> None:
    """运行测试，默认使用 pytest"""
    cmd = test_command.split() if test_command else [sys.executable, '-m', 'pytest']
    print(f"🧪 正在运行测试：{' '.join(cmd)}")
    try:
        subprocess.run(cmd, cwd=project_dir, check=True, capture_output=True, text=True)
        print("✅ 测试通过。")
    except subprocess.CalledProcessError as e:
        print(f"❌ 测试失败：\n{e.stderr}", file=sys.stderr)
        sys.exit(1)