import asyncio
import warnings
import functools
from typing import Callable, Any, AsyncIterator

from anyrun import RunTimeException


def execute_async_iterator(async_iterator: AsyncIterator[dict]) -> Any:
    """ Provides an async iterator management for the synchronous code """
    event_loop = get_running_loop()

    while True:
        try:
            yield event_loop.run_until_complete(async_iterator.__anext__())
        except StopAsyncIteration:
            break


def execute_synchronously(function: Callable, *args, **kwargs) -> Any:
    """ Creates a coroutine function using the given parameters and then executes it in the event loop """
    event_loop = get_running_loop()
    return event_loop.run_until_complete(function(*args, **kwargs))


def get_running_loop() -> asyncio.AbstractEventLoop:
    """ Tries to get a running event loop. If not found, creates a new one and returns it """
    try:
        return asyncio.get_event_loop()
    except RuntimeError:
        event_loop = asyncio.new_event_loop()
        asyncio.set_event_loop(event_loop)
        return event_loop


def check_if_analysis_initialized(func):
    """ Checks if taskid in Sandbox API response JSON """
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        params = await func(*args, **kwargs)

        if params is None:
            raise RunTimeException('Analysis is not started.', 500)

        if 'queueTaskId' in params.get('data'):
            raise RunTimeException(f'Analysis is not started. Queue ID: {params.get("data").get("queueTaskId")}', 500)

        return params.get('data').get('taskid')

    return wrapper


def deprecated(reason: str = ""):
    def decorator(func):
        message = f"{func.__name__} is deprecated."
        if reason:
            message += f" {reason}"

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            warnings.warn(message, DeprecationWarning, stacklevel=2)
            return func(*args, **kwargs)
        return wrapper
    return decorator
