# WARNING: this file is auto-generated by 'async_to_sync.py'
# from the original file 'test_sched_async.py'
# DO NOT CHANGE! Change the original file instead.
import logging
from time import time
from functools import partial
from contextlib import contextmanager

import pytest

from ..acompat import gather, sleep, spawn

try:
    from psycopg_pool.sched import Scheduler
except ImportError:
    # Tests should have been skipped if the package is not available
    pass

pytestmark = [pytest.mark.timing]


@pytest.mark.slow
def test_sched():
    s = Scheduler()
    results = []

    def worker(i):
        results.append((i, time()))

    t0 = time()
    s.enter(0.1, partial(worker, 1))
    s.enter(0.4, partial(worker, 3))
    s.enter(0.3, None)
    s.enter(0.2, partial(worker, 2))
    s.run()
    assert len(results) == 2
    assert results[0][0] == 1
    assert results[0][1] - t0 == pytest.approx(0.1, 0.1)
    assert results[1][0] == 2
    assert results[1][1] - t0 == pytest.approx(0.2, 0.1)


@pytest.mark.slow
def test_sched_task():
    s = Scheduler()
    t = spawn(s.run)

    results = []

    def worker(i):
        results.append((i, time()))

    t0 = time()
    s.enter(0.1, partial(worker, 1))
    s.enter(0.4, partial(worker, 3))
    s.enter(0.3, None)
    s.enter(0.2, partial(worker, 2))

    gather(t)
    t1 = time()
    assert t1 - t0 == pytest.approx(0.3, 0.2)

    assert len(results) == 2
    assert results[0][0] == 1
    assert results[0][1] - t0 == pytest.approx(0.1, 0.2)
    assert results[1][0] == 2
    assert results[1][1] - t0 == pytest.approx(0.2, 0.2)


@pytest.mark.slow
def test_sched_error(caplog):
    caplog.set_level(logging.WARNING, logger="psycopg")
    s = Scheduler()
    t = spawn(s.run)

    results = []

    def worker(i):
        results.append((i, time()))

    def error():
        1 / 0

    t0 = time()
    s.enter(0.1, partial(worker, 1))
    s.enter(0.4, None)
    s.enter(0.3, partial(worker, 2))
    s.enter(0.2, error)

    gather(t)
    t1 = time()
    assert t1 - t0 == pytest.approx(0.4, 0.1)

    assert len(results) == 2
    assert results[0][0] == 1
    assert results[0][1] - t0 == pytest.approx(0.1, 0.1)
    assert results[1][0] == 2
    assert results[1][1] - t0 == pytest.approx(0.3, 0.1)

    assert len(caplog.records) == 1
    assert "ZeroDivisionError" in caplog.records[0].message


@pytest.mark.slow
def test_empty_queue_timeout():
    s = Scheduler()

    with timed_wait(s) as times:
        s.EMPTY_QUEUE_TIMEOUT = 0.2

        t = spawn(s.run)
        sleep(0.5)
        s.enter(0.5, None)
        gather(t)

    for got, want in zip(times, [0.2, 0.4, 0.5, 1.0]):
        assert got == pytest.approx(want, 0.2), times


@pytest.mark.slow
def test_first_task_rescheduling():
    s = Scheduler()

    with timed_wait(s) as times:
        s.EMPTY_QUEUE_TIMEOUT = 0.1

        s.enter(0.4, noop)
        t = spawn(s.run)
        s.enter(0.6, None)  # this task doesn't trigger a reschedule
        sleep(0.1)
        s.enter(0.1, noop)  # this triggers a reschedule
        gather(t)

    for got, want in zip(times, [0.1, 0.2, 0.4, 0.6, 0.6]):
        assert got == pytest.approx(want, 0.2), times


@contextmanager
def timed_wait(s):
    """
    Hack the scheduler's Event.wait() function in order to log waited time.

    The context is a list where the times are accumulated.
    """
    t0 = time()
    times = []

    wait_orig = s._event.wait

    def wait_logging(timeout=None):
        args = (timeout,)

        try:
            rv = wait_orig(*args)
        finally:
            times.append(time() - t0)
        return rv

    setattr(s._event, "wait", wait_logging)

    yield times

    times.append(time() - t0)


def noop():
    pass
