import boto3
import os
import tempfile
import json
from tqdm import tqdm


def download_s3_folder(bucket_name, s3_folder, profile_name="default"):
    """
    Download all files from an S3 folder to a temporary directory

    Args:
        bucket_name (str): Name of the S3 bucket
        s3_folder (str): Path of the folder in S3 to download
        profile_name (str): AWS profile name stored in ~/.aws/credentials

    Returns:
        str: Path to the temporary directory where files were downloaded
    """
    # Create a temporary directory to store the downloaded files
    temp_dir = tempfile.mkdtemp()
    print(f"Created temporary directory: {temp_dir}")

    # Create a session using your AWS profile
    session = boto3.Session(profile_name=profile_name)
    s3_client = session.client("s3")

    # List all objects in the folder
    if not s3_folder.endswith("/"):
        s3_folder += "/"

    try:
        # Get list of all objects in the folder
        paginator = s3_client.get_paginator("list_objects_v2")
        pages = paginator.paginate(Bucket=bucket_name, Prefix=s3_folder)

        # First, collect all objects to download
        objects_to_download = []
        for page in pages:
            if "Contents" not in page:
                print(f"No objects found in {s3_folder}")
                continue

            for obj in page["Contents"]:
                # Get the relative path by removing the folder prefix
                s3_key = obj["Key"]

                # Skip if it's a "folder" (empty object that ends with '/')
                if s3_key.endswith("/"):
                    continue

                objects_to_download.append(s3_key)

        # Download each object with progress bar
        download_count = 0
        for s3_key in tqdm(objects_to_download, desc="Downloading files", unit="file"):
            # Create the local file path
            relative_path = s3_key[len(s3_folder) :]
            local_file_path = os.path.join(temp_dir, relative_path)

            # Create directory if it doesn't exist
            os.makedirs(os.path.dirname(local_file_path), exist_ok=True)

            # Download the file
            s3_client.download_file(bucket_name, s3_key, local_file_path)
            download_count += 1

        print(f"Successfully downloaded {download_count} files to {temp_dir}")
        return temp_dir

    except Exception as e:
        print(f"Error downloading from S3: {e}")
        return None


def merge_jsonl_files(directory, output_file=None):
    """
    Merge all JSONL files in the directory into a single JSONL file

    Args:
        directory (str): Directory containing JSONL files
        output_file (str, optional): Path for the merged output file.
                                    If None, creates a file in the same directory.

    Returns:
        str: Path to the merged file
    """
    if output_file is None:
        output_file = os.path.join(directory, "merged_output.jsonl")

    # Find all JSONL files in the directory and subdirectories
    jsonl_files = []
    for root, _, files in os.walk(directory):
        for file in files:
            if file.endswith(".jsonl"):
                jsonl_files.append(os.path.join(root, file))

    if not jsonl_files:
        print(f"No JSONL files found in {directory}")
        return None

    print(f"Found {len(jsonl_files)} JSONL files to merge")

    # Merge the files with progress bar
    line_count = 0
    with open(output_file, "w") as outfile:
        for jsonl_file in tqdm(jsonl_files, desc="Merging JSONL files", unit="file"):
            try:
                with open(jsonl_file, "r") as infile:
                    for line in infile:
                        # Validate that each line is valid JSON
                        try:
                            json.loads(line.strip())
                            outfile.write(line)
                            line_count += 1
                        except json.JSONDecodeError:
                            # Just log to stderr to not interfere with progress bar
                            import sys

                            print(
                                f"Skipping invalid JSON line in {jsonl_file}",
                                file=sys.stderr,
                            )
            except Exception as e:
                import sys

                print(f"Error processing {jsonl_file}: {e}", file=sys.stderr)

    print(f"Successfully merged {line_count} lines into {output_file}")
    return output_file


if __name__ == "__main__":
    # Example usage:
    bucket_name = "webdataset"
    s3_folder = "bundles/v0/discogs_subset_50k/paint_stems/metas"
    profile_name = "oracle"  # The profile name in ~/.aws/credentials
    output_file = (
        "merged_data.jsonl"  # Path where you want to save the final merged file
    )

    # Download the S3 folder
    download_dir = download_s3_folder(bucket_name, s3_folder, profile_name)

    if download_dir:
        print(f"Files are available at: {download_dir}")

        # Merge all JSONL files to the output location (outside temp dir)
        merged_file = merge_jsonl_files(download_dir, output_file)

        if merged_file:
            print(f"All JSONL files have been merged into: {merged_file}")

        # Clean up the temporary directory
        import shutil

        shutil.rmtree(download_dir)
        print(f"Temporary directory {download_dir} has been deleted")
