"""Generate sync API test cases from async API test cases."""
# Many of the async test cases can be transformed into valid
# sync API test cases via relatively naive text transforms.
# Test cases where that is the case are stored in the `async` folder.
# This script takes those test cases and emits sync API equivalents.

import re
import subprocess
from pathlib import Path
from os.path import commonpath

GENERATED_FILE_HEADER = """\
#####################################################################
# This files has been automatically generated from:
#   ../{}
#
# DO NOT EDIT THIS FILE! Edit the async test case listed above,
# and regenerate the synchronous test cases with async2sync.py
#####################################################################
"""


def convert_file(input_file: Path, sync_dir: Path) -> None:
    output_file = sync_dir / re.sub("_async", "_sync", input_file.name)
    print(f"Converting {input_file} to {output_file}")

    content = input_file.read_text()

    # Note: some replacements will only be relevant after dropping 3.10 support
    #       (as the native asyncio API were only added in 3.11)

    replacements = [
        (r"AsyncPredictionC", "SyncPredictionC"),  # Channel & CM
        (r"AsyncSession", "SyncSession"),
        (r"@pytest\.mark\.asyncio\n*", ""),
        (r"async def ([^(]*?)_async", r"def \1_sync"),
        (r"async def", "def"),
        (r"async for", "for"),
        (r"async with", "with"),
        (r"import asyncio\n", ""),
        (r"asyncio\.timeout\([^)]*\)", "nullcontext()"),
        (r"asyncio\.", ""),
        (r"aconnect_ws", "connect_ws"),
        (r"aclose", "close"),
        (r"aenter", "enter"),
        (r"aexit", "exit"),
        (r"anext", "next"),
        (r"Async", ""),
        (r"await +", ""),
        # Switch to native asyncio APIs when dropping Python 3.10 support
        (r"import anyio\n", ""),
        (r"anyio\.fail_after\([^)]*\)", "nullcontext()"),
    ]

    for pattern in replacements:
        content = re.sub(pattern[0], pattern[1], content)

    # need to import from contextlib if we use nullcontext
    if "nullcontext()" in content:
        # add it after the logging import, this is always present
        content = re.sub(
            r"import logging\n",
            "import logging\nfrom contextlib import nullcontext\n",
            content,
        )

    # Use commonpath, as there's no `walk_up` parameter on `relative_to`` in Python 3.11
    base_path = commonpath((input_file, output_file))
    relative_input_path = str(input_file.relative_to(base_path).as_posix())
    output_header = GENERATED_FILE_HEADER.format(relative_input_path)
    output_text = output_header + content
    output_file.write_text(output_text)


def main() -> None:
    async_dir = Path(__file__).parent / "async"
    sync_dir = Path(__file__).parent / "sync"
    async_files = async_dir.glob("test_*_async.py")

    for input_file in async_files:
        convert_file(input_file, sync_dir)
    print("Running automatic formatter after file conversion")
    subprocess.run(["tox", "-e", "format"])


if __name__ == "__main__":
    main()
