import datetime
import os
import re
import zipfile
from pathlib import Path


EXCLUDED_DIRS = {
    ".claude",
    ".codex",
    ".coverage",
    ".cache",
    ".git",
    ".idea",
    ".mypy_cache",
    ".next",
    ".pytest_cache",
    ".ruff_cache",
    ".venv",
    ".vscode",
    "__pycache__",
    "backups",
    "bin",
    "bin 2",
    "node_modules",
    "runtime",
    "volumes",
    "build",
    "dist",
    "htmlcov",
    "logs",
    "playwright-report",
    "screenshots",
    "temp",
    "tmp",
    "uploads",
}

EXCLUDED_PATH_PREFIXES = {
    ("docs", "design"),
    ("export_assets",),
    ("frontend", ".next"),
    ("MOMO Pro",),
}

EXCLUDED_FILES = {
    ".DS_Store",
    ".env",
    "google_credentials.json",
    "google_token.json",
    "google_token.pickle",
}

EXCLUDED_SUFFIXES = (".pyc", ".pyo", ".sqlite", ".sqlite3", ".tsbuildinfo")


def _resolve_project_root() -> Path:
    configured = os.getenv("MOMO_BACKUP_ROOT")
    if configured:
        return Path(configured).expanduser().resolve()
    return Path(__file__).resolve().parents[2]


def _resolve_backup_folder(project_root: Path) -> Path:
    configured = os.getenv("MOMO_BACKUP_DIR")
    if configured:
        return Path(configured).expanduser().resolve()
    return project_root / "backups"


def _read_system_version(project_root: Path) -> str:
    version = "Unknown"
    config_path = project_root / "config.py"
    try:
        if config_path.exists():
            content = config_path.read_text(encoding="utf-8")
            match = re.search(r'SYSTEM_VERSION\s*=\s*["\']([^"\']+)["\']', content)
            if match:
                version = match.group(1)
    except Exception as e:
        print(f"⚠️ 無法讀取版本號: {e}")
    return version


def _should_skip_file(file_path: Path, project_root: Path, backup_folder: Path) -> bool:
    if file_path.is_symlink():
        return True
    if file_path.name in EXCLUDED_FILES:
        return True
    if file_path.name.startswith(".env."):
        return True
    if file_path.suffix in EXCLUDED_SUFFIXES:
        return True

    try:
        file_path.resolve().relative_to(backup_folder)
        return True
    except ValueError:
        pass

    rel_parts = file_path.relative_to(project_root).parts
    if _is_excluded_path(rel_parts):
        return True
    return any(_is_excluded_dir_name(part) for part in rel_parts[:-1])


def _is_excluded_dir_name(dirname: str) -> bool:
    return dirname in EXCLUDED_DIRS or dirname.startswith("production_v")


def _is_excluded_path(rel_parts: tuple[str, ...]) -> bool:
    for prefix in EXCLUDED_PATH_PREFIXES:
        if rel_parts[:len(prefix)] == prefix:
            return True
    return False


def _prune_dirs(dirs: list[str], root: Path, project_root: Path, backup_folder: Path) -> None:
    kept = []
    for dirname in dirs:
        candidate = root / dirname
        try:
            rel_parts = candidate.relative_to(project_root).parts
        except ValueError:
            rel_parts = ()
        if rel_parts and _is_excluded_path(rel_parts):
            continue
        if _is_excluded_dir_name(dirname):
            continue
        if candidate.is_symlink():
            continue
        try:
            candidate.resolve().relative_to(backup_folder)
            continue
        except ValueError:
            pass
        kept.append(dirname)
    dirs[:] = kept

def create_backup():
    """
    建立系統完整備份 (Zip 壓縮檔)
    檔名格式: momo_pro_system_backup_YYYYMMDD_HHMMSS_V{version}.zip
    """
    project_root = _resolve_project_root()
    backup_folder = _resolve_backup_folder(project_root)
    
    if not backup_folder.exists():
        backup_folder.mkdir(parents=True)
        print(f"📂 已建立備份目錄: {backup_folder}")

    version = _read_system_version(project_root)

    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    base_name = f"momo_pro_system_backup_{timestamp}_{version}.zip"
    output_path = backup_folder / base_name

    print(f"📦 正在打包專案目錄: {project_root}")
    print(f"🎯 目標檔案: {output_path}")

    try:
        with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(project_root):
                root_path = Path(root)
                _prune_dirs(dirs, root_path, project_root, backup_folder)
                for file in files:
                    file_path = root_path / file
                    if _should_skip_file(file_path, project_root, backup_folder):
                        continue
                    arcname = file_path.relative_to(project_root)
                    zipf.write(file_path, arcname)
        print(f"✅ 備份完成！")
        return str(output_path)
    except Exception as e:
        print(f"❌ 備份失敗: {e}")
        return None

if __name__ == "__main__":
    create_backup()
