# metapkg/req_writer.py
"""
Module for generating and updating requirements.txt files based on 
installed packages in the current environment or from imports in project files.

This module allows users to generate a requirements.txt file without needing
to maintain it manually, regardless of whether the project uses pyproject.toml
or not.
"""

import sys
from importlib.metadata import distributions
from pathlib import Path
from typing import List, Tuple

# Only import importlib_metadata for Python < 3.9
if sys.version_info < (3, 9):
    try:
        from importlib_metadata import distributions
    except ImportError:
        raise ImportError("importlib_metadata is required for Python < 3.9. Install it with 'pip install importlib_metadata'")

def get_installed_packages() -> List[Tuple[str, str]]:
    """
    Retrieve a list of installed packages and their versions using importlib.metadata.
    
    Returns:
        List of tuples containing (package_name, version).
    """
    try:
        return [(dist.metadata['Name'].lower(), dist.version) for dist in distributions()]
    except Exception as e:
        raise RuntimeError(f"Failed to retrieve installed packages: {str(e)}")

def generate_requirements_txt(
    output_path: str = "requirements.txt",
    exclude_stdlib: bool = True
) -> None:
    """
    Generate a requirements.txt file with installed packages and their versions.
    
    Args:
        output_path: Path to write the requirements.txt file.
        exclude_stdlib: If True, excludes standard library packages.
    
    Raises:
        PermissionError: If writing to the output file fails.
        RuntimeError: If package retrieval fails.
    """
    # Standard library packages to exclude
    stdlib_packages = {
        'argparse', 'ast', 'asyncio', 'base64', 'bisect', 'builtins',
        'calendar', 'collections', 'contextlib', 'copy', 'csv', 'datetime',
        'decimal', 'enum', 'functools', 'hashlib', 'heapq', 'io', 'itertools',
        'json', 'logging', 'math', 'os', 'pathlib', 'random', 're', 'shutil',
        'socket', 'stat', 'string', 'subprocess', 'sys', 'tempfile', 'textwrap',
        'threading', 'time', 'traceback', 'types', 'typing', 'urllib', 'uuid',
        'warnings', 'weakref', 'zlib'
    }

    try:
        packages = get_installed_packages()
        filtered_packages = []

        for pkg_name, version in sorted(packages):
            # Normalize package name (e.g., 'scikit-learn' -> 'scikit_learn' for comparison)
            normalized_name = pkg_name.replace('-', '_').lower()
            # Skip standard library packages or pip/setuptools if exclude_stdlib is True
            if exclude_stdlib and (normalized_name in stdlib_packages or
                                 pkg_name.lower() in {'pip', 'setuptools', 'wheel', 'importlib_metadata'}):
                continue
            # Format package name to match PyPI conventions (e.g., scikit_learn -> scikit-learn)
            pypi_name = pkg_name.replace('_', '-')
            filtered_packages.append(f"{pypi_name}=={version}")

        # Ensure the output directory exists
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)

        # Write to requirements.txt
        try:
            with output_file.open('w', encoding='utf-8') as f:
                f.write("# Generated by metapkg\n")
                for pkg in filtered_packages:
                    f.write(f"{pkg}\n")
        except PermissionError as e:
            raise PermissionError(f"Cannot write to {output_path}: {str(e)}")
        except Exception as e:
            raise RuntimeError(f"Failed to write requirements.txt: {str(e)}")

    except Exception as e:
        raise RuntimeError(f"Error generating requirements.txt: {str(e)}")

def integrate_with_cli(app):
    """
    Integrate the requirements.txt generation with the Typer CLI.
    
    Args:
        app: Typer app instance to register the command.
    """
    import typer

    @app.command(name="reqs")
    def reqs(
        output: str = typer.Option(
            "requirements.txt",
            "--output", "-o",
            help="Path to the output requirements.txt file"
        ),
        include_stdlib: bool = typer.Option(
            False,
            "--include-stdlib",
            help="Include standard library packages in requirements.txt"
        )
    ):
        """Generate a requirements.txt file from installed packages."""
        try:
            generate_requirements_txt(output_path=output, exclude_stdlib=not include_stdlib)
            typer.echo(f"Successfully generated {output}")
        except (PermissionError, RuntimeError) as e:
            typer.echo(f"Error: {str(e)}", err=True)
            raise typer.Exit(code=1)