#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "pandas",
#     "openpyxl",
# ]
# ///

import subprocess
import os
from pathlib import Path
from datetime import datetime
import pandas as pd
import sys

def get_git_sha():
    """Get the current git SHA."""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--short", "HEAD"],
            capture_output=True,
            text=True,
            check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError:
        return "unknown"

def is_gitignored(path, repo_root):
    """Check if a path is gitignored."""
    try:
        result = subprocess.run(
            ["git", "check-ignore", str(path)],
            cwd=repo_root,
            capture_output=True,
            timeout=5
        )
        # Exit code 0 means the path is ignored
        return result.returncode == 0
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        return False

def should_exclude(path, repo_root):
    """Check if path should be excluded."""
    exclude_patterns = ['.venv', 'node_modules', '.next', '__pycache__']
    if any(pattern in str(path) for pattern in exclude_patterns):
        return True
    return is_gitignored(path, repo_root)

def find_python_projects(root_dir):
    """Find all Python projects with pyproject.toml and uv.lock."""
    projects = []
    for pyproject in Path(root_dir).rglob("pyproject.toml"):
        if should_exclude(pyproject, root_dir):
            continue
        uv_lock = pyproject.parent / "uv.lock"
        if uv_lock.exists():
            projects.append(pyproject.parent)
    return projects

def find_nodejs_projects(root_dir):
    """Find all Node.js projects with package.json."""
    projects = []
    for package_json in Path(root_dir).rglob("package.json"):
        if should_exclude(package_json, root_dir):
            continue
        projects.append(package_json.parent)
    return projects

def check_pip_licenses_installed(project_dir):
    """Check if pip-licenses is available in the project."""
    try:
        result = subprocess.run(
            ["uv", "run", "pip-licenses", "--version"],
            cwd=project_dir,
            capture_output=True,
            text=True,
            timeout=30
        )
        return result.returncode == 0
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        return False

def install_pip_licenses(project_dir):
    """Install pip-licenses as a dev dependency."""
    print(f"  Installing pip-licenses in {project_dir.name}...")
    try:
        subprocess.run(
            ["uv", "add", "--dev", "pip-licenses"],
            cwd=project_dir,
            check=True,
            capture_output=True
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"  Error installing pip-licenses: {e}")
        return False

def extract_python_licenses(project_dir, output_dir, date_str, git_sha):
    """Extract licenses from a Python project."""
    folder_name = project_dir.name
    output_file = output_dir / f"{date_str}-{folder_name}-{git_sha}-py-licenses.csv"

    print(f"Processing Python project: {project_dir.relative_to(Path.cwd())}")

    # Check if pip-licenses is installed
    if not check_pip_licenses_installed(project_dir):
        print(f"  pip-licenses not found, installing...")
        if not install_pip_licenses(project_dir):
            print(f"  Skipping {folder_name} due to installation failure")
            return None

    try:
        result = subprocess.run(
            ["uv", "run", "pip-licenses", "--format=csv", "--with-urls", "--with-description"],
            cwd=project_dir,
            capture_output=True,
            text=True,
            check=True,
            timeout=120
        )

        output_file.write_text(result.stdout)
        print(f"  ✓ Saved to {output_file.name}")
        return output_file
    except subprocess.CalledProcessError as e:
        print(f"  Error extracting Python licenses: {e}")
        return None
    except subprocess.TimeoutExpired:
        print(f"  Timeout extracting Python licenses")
        return None

def extract_nodejs_licenses(project_dir, output_dir, date_str, git_sha):
    """Extract licenses from a Node.js project."""
    folder_name = project_dir.name
    output_file = output_dir / f"{date_str}-{folder_name}-{git_sha}-node-licenses.csv"

    print(f"Processing Node.js project: {project_dir.relative_to(Path.cwd())}")

    try:
        result = subprocess.run(
            ["pnpx", "license-checker", "--csv"],
            cwd=project_dir,
            capture_output=True,
            text=True,
            check=True,
            timeout=120
        )

        output_file.write_text(result.stdout)
        print(f"  ✓ Saved to {output_file.name}")
        return output_file
    except subprocess.CalledProcessError as e:
        print(f"  Error extracting Node.js licenses: {e}")
        return None
    except subprocess.TimeoutExpired:
        print(f"  Timeout extracting Node.js licenses")
        return None

def compile_to_excel(csv_files, output_dir, date_str):
    """Compile all CSV files into a single Excel file with multiple sheets."""
    if not csv_files:
        print("No CSV files to compile")
        return

    excel_file = output_dir / f"{date_str}-compiled-licenses.xlsx"
    print(f"\nCompiling results to {excel_file.name}...")

    with pd.ExcelWriter(excel_file, engine='openpyxl') as writer:
        for csv_file in csv_files:
            try:
                df = pd.read_csv(csv_file)
                # Create sheet name from filename (remove date and git sha prefix)
                sheet_name = csv_file.stem
                # Truncate sheet name if too long (Excel limit is 31 chars)
                if len(sheet_name) > 31:
                    sheet_name = sheet_name[:31]
                df.to_excel(writer, sheet_name=sheet_name, index=False)
                print(f"  ✓ Added sheet: {sheet_name}")
            except Exception as e:
                print(f"  Error adding {csv_file.name}: {e}")

    print(f"\n✓ Compilation complete: {excel_file}")

def main():
    # Setup
    repo_root = Path.cwd()
    output_dir = repo_root / "licenses" / "results"
    output_dir.mkdir(parents=True, exist_ok=True)

    date_str = datetime.now().strftime("%Y%m%d")
    git_sha = get_git_sha()

    print(f"License Audit")
    print(f"Date: {date_str}")
    print(f"Git SHA: {git_sha}")
    print(f"Repository: {repo_root}\n")

    # Find projects
    print("Discovering projects...")
    python_projects = find_python_projects(repo_root)
    nodejs_projects = find_nodejs_projects(repo_root)

    print(f"Found {len(python_projects)} Python projects")
    print(f"Found {len(nodejs_projects)} Node.js projects\n")

    # Extract licenses
    csv_files = []

    print("=" * 60)
    print("Extracting Python licenses...")
    print("=" * 60)
    for project in python_projects:
        result = extract_python_licenses(project, output_dir, date_str, git_sha)
        if result:
            csv_files.append(result)

    print("\n" + "=" * 60)
    print("Extracting Node.js licenses...")
    print("=" * 60)
    for project in nodejs_projects:
        result = extract_nodejs_licenses(project, output_dir, date_str, git_sha)
        if result:
            csv_files.append(result)

    # Compile results
    print("\n" + "=" * 60)
    compile_to_excel(csv_files, output_dir, date_str)

    print(f"\n✓ License audit complete!")
    print(f"  CSV files: {len(csv_files)}")
    print(f"  Results directory: {output_dir}")

if __name__ == "__main__":
    main()
