import sys
from concurrent.futures import ThreadPoolExecutor, as_completed

import click
from tqdm import tqdm


def run_batch(items, process_fn, desc, workers, log_path=None):
    """Run process_fn(item) over items in parallel.

    process_fn must return (path, success: bool, err: str | None).
    Errors go to log_path if set, otherwise stderr.
    Returns (ok_count, failed_count).
    """
    ok = failed = 0
    log_fh = open(log_path, "a", encoding="utf-8") if log_path else None
    try:
        with ThreadPoolExecutor(max_workers=workers) as executor:
            futures = {executor.submit(process_fn, item): item for item in items}
            with tqdm(total=len(items), desc=desc, file=sys.stderr) as pbar:
                for fut in as_completed(futures):
                    try:
                        path, success, err = fut.result()
                    except Exception as e:
                        path, success, err = futures[fut], False, str(e)
                    if success:
                        ok += 1
                    else:
                        failed += 1
                        msg = f"{path}\t{err}"
                        if log_fh:
                            log_fh.write(msg + "\n")
                            log_fh.flush()
                        else:
                            click.echo(msg, err=True)
                    pbar.update(1)
    finally:
        if log_fh:
            log_fh.close()
    return ok, failed
