#!/usr/bin/env python3
"""
Download GitHub Actions artifacts and rename them with version numbers.

Usage:
    python download_github_runs.py 2.0.4 https://github.com/78/xiaozhi-esp32/actions/runs/18866246016

Output:
    Files are downloaded to releases/<version>/ directory relative to the project root.
    Example: releases/2.0.4/v2.0.4_atk-dnesp32s3-box0.zip
"""

import argparse
import os
import re
import sys
import zipfile
from pathlib import Path
from urllib.parse import urlparse

import requests
from dotenv import load_dotenv


def parse_github_run_url(url: str) -> tuple[str, str, str]:
    """
    Parse GitHub Actions run URL to extract owner, repo, and run_id.
    
    Args:
        url: GitHub Actions run URL
        
    Returns:
        Tuple of (owner, repo, run_id)
    """
    # Example: https://github.com/78/xiaozhi-esp32/actions/runs/18866246016
    pattern = r'github\.com/([^/]+)/([^/]+)/actions/runs/(\d+)'
    match = re.search(pattern, url)
    
    if not match:
        raise ValueError(f"Invalid GitHub Actions URL: {url}")
    
    owner, repo, run_id = match.groups()
    return owner, repo, run_id


def get_artifacts(owner: str, repo: str, run_id: str, token: str) -> list[dict]:
    """
    Get all artifacts for a specific workflow run (with pagination support).
    
    Args:
        owner: Repository owner
        repo: Repository name
        run_id: Workflow run ID
        token: GitHub personal access token
        
    Returns:
        List of artifact dictionaries
    """
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json",
        "X-GitHub-Api-Version": "2022-11-28"
    }
    
    all_artifacts = []
    page = 1
    per_page = 100  # Maximum allowed by GitHub API
    
    while True:
        url = f"https://api.github.com/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts"
        params = {
            "page": page,
            "per_page": per_page
        }
        
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()
        
        data = response.json()
        artifacts = data.get("artifacts", [])
        
        if not artifacts:
            break
        
        all_artifacts.extend(artifacts)
        
        # Check if there are more pages
        total_count = data.get("total_count", 0)
        if len(all_artifacts) >= total_count:
            break
        
        page += 1
    
    return all_artifacts


def download_artifact(artifact_url: str, token: str, output_path: Path) -> None:
    """
    Download an artifact from GitHub.
    
    Args:
        artifact_url: Artifact download URL
        token: GitHub personal access token
        output_path: Path to save the downloaded artifact
    """
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json",
        "X-GitHub-Api-Version": "2022-11-28"
    }
    
    response = requests.get(artifact_url, headers=headers, stream=True)
    response.raise_for_status()
    
    # Create parent directory if it doesn't exist
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    # Download the file
    with open(output_path, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                f.write(chunk)


def rename_artifact(original_name: str, version: str) -> str:
    """
    Rename artifact according to the specified rules.
    
    Rules:
    - Remove "xiaozhi_" prefix
    - Remove hash suffix (underscore followed by hex string)
    - Add version prefix (e.g., "v2.0.4_")
    - Add .zip extension
    
    Examples:
        xiaozhi_atk-dnesp32s3-box0_43ef2f4e7f0957dc62ec7d628ac2819d226127b8
        -> v2.0.4_atk-dnesp32s3-box0.zip
        
        xiaozhi_waveshare-esp32-p4-nano-10.1-a_43ef2f4e7f0957dc62ec7d628ac2819d226127b8
        -> v2.0.4_waveshare-esp32-p4-nano-10.1-a.zip
    
    Args:
        original_name: Original artifact name
        version: Version string (e.g., "2.0.4")
        
    Returns:
        New filename
    """
    # Remove "xiaozhi_" prefix
    name = original_name
    if name.startswith("xiaozhi_"):
        name = name[len("xiaozhi_"):]
    
    # Remove known extensions only (not using splitext to avoid issues with
    # names containing dots like "esp32-s3-touch-amoled-2.06")
    known_extensions = ('.bin', '.zip')
    for ext in known_extensions:
        if name.endswith(ext):
            name = name[:-len(ext)]
            break
    
    # Remove hash suffix (pattern: underscore followed by 40+ hex characters)
    # This matches Git commit hashes and similar identifiers
    name_without_hash = re.sub(r'_[a-f0-9]{40,}$', '', name)
    
    # Add version prefix and .zip extension
    new_name = f"v{version}_{name_without_hash}.zip"
    
    return new_name


def get_default_releases_dir() -> Path:
    """
    Get the default releases directory path relative to this script's location.
    
    Returns:
        Path to the releases directory (script_dir/../releases)
    """
    script_dir = Path(__file__).resolve().parent
    return script_dir.parent / "releases"


def main():
    """Main function to download and rename GitHub Actions artifacts."""
    parser = argparse.ArgumentParser(
        description="Download GitHub Actions artifacts and rename them with version numbers."
    )
    parser.add_argument(
        "version",
        help="Version number (e.g., 2.0.4)"
    )
    parser.add_argument(
        "url",
        help="GitHub Actions run URL (e.g., https://github.com/owner/repo/actions/runs/12345)"
    )
    parser.add_argument(
        "--output-dir",
        default=None,
        help="Output directory for downloaded artifacts (default: releases/<version> relative to project root)"
    )
    
    args = parser.parse_args()
    
    # Load GitHub token from .env file
    load_dotenv()
    github_token = os.getenv("GITHUB_TOKEN")
    
    if not github_token:
        print("Error: GITHUB_TOKEN not found in environment variables.", file=sys.stderr)
        print("Please create a .env file with GITHUB_TOKEN=your_token_here", file=sys.stderr)
        sys.exit(1)
    
    try:
        # Parse the GitHub URL
        owner, repo, run_id = parse_github_run_url(args.url)
        print(f"Repository: {owner}/{repo}")
        print(f"Run ID: {run_id}")
        print(f"Version: {args.version}")
        print()
        
        # Get artifacts
        print("Fetching artifacts...")
        artifacts = get_artifacts(owner, repo, run_id, github_token)
        
        if not artifacts:
            print("No artifacts found for this run.")
            return
        
        print(f"Found {len(artifacts)} artifact(s):")
        for artifact in artifacts:
            print(f"  - {artifact['name']}")
        print()
        
        # Determine output directory
        if args.output_dir:
            # User specified custom output directory
            output_dir = Path(args.output_dir) / args.version
        else:
            # Default: releases/<version> relative to script location
            output_dir = get_default_releases_dir() / args.version
        
        # Create output directory
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Download and rename each artifact
        downloaded_count = 0
        skipped_count = 0
        
        for artifact in artifacts:
            original_name = artifact['name']
            new_name = rename_artifact(original_name, args.version)
            final_path = output_dir / new_name
            
            # Check if file already exists
            if final_path.exists():
                print(f"Skipping (already exists): {original_name}")
                print(f"  -> {new_name}")
                print(f"  File: {final_path}")
                print()
                skipped_count += 1
                continue
            
            print(f"Downloading: {original_name}")
            print(f"  -> {new_name}")
            
            # Download to temporary path first
            temp_path = output_dir / f"{original_name}.zip"
            download_artifact(
                artifact['archive_download_url'],
                github_token,
                temp_path
            )
            
            # Rename to final name
            temp_path.rename(final_path)
            
            print(f"  Saved to: {final_path}")
            print()
            downloaded_count += 1
        
        print(f"Summary:")
        print(f"  Downloaded: {downloaded_count} artifact(s)")
        print(f"  Skipped: {skipped_count} artifact(s)")
        print(f"  Total: {len(artifacts)} artifact(s)")
        print(f"  Output directory: {output_dir.absolute()}")
        
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

