import pytest

import concurrent.futures as futures
import time
from datetime import datetime, timedelta

from aircox.management.sound_monitor import (
    ModifiedHandler,
    MonitorHandler,
    NotifyHandler,
)


class FakeEvent:
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)


class WaitHandler(NotifyHandler):
    def __call__(self, timeout=0.5, *args, **kwargs):
        # using time.sleep make the future done directly, don't know why
        start = datetime.now()
        while datetime.now() - start < timedelta(seconds=4):
            pass


@pytest.fixture
def monitor():
    pool = futures.ThreadPoolExecutor(2)
    return MonitorHandler("archives", pool)


class TestNotifyHandler:
    pass


class TestMoveHandler:
    pass


class TestModifiedHandler:
    def test_wait(self):
        handler = ModifiedHandler()
        handler.timeout_delta = timedelta(seconds=0.1)
        start = datetime.now()
        handler.wait()
        delta = datetime.now() - start
        assert delta < handler.timeout_delta + timedelta(seconds=0.1)

    def test_wait_ping(self):
        pool = futures.ThreadPoolExecutor(1)
        handler = ModifiedHandler()
        handler.timeout_delta = timedelta(seconds=0.5)

        future = pool.submit(handler.wait)
        time.sleep(0.3)
        handler.ping()
        time.sleep(0.3)
        assert future.running()


class TestMonitorHandler:
    def test_submit_new_job(self, monitor):
        event = FakeEvent(src_path="dummy_src")
        handler = NotifyHandler()
        result, _ = monitor._submit(handler, event, "up")
        assert handler == result
        assert isinstance(handler.future, futures.Future)
        monitor.pool.shutdown()

    def test_submit_job_exists(self, monitor):
        event = FakeEvent(src_path="dummy_src")

        job_1, new_1 = monitor._submit(WaitHandler(), event, "up")
        job_2, new_2 = monitor._submit(NotifyHandler(), event, "up")
        assert job_1 == job_2
        assert new_1
        assert not new_2
        monitor.pool.shutdown()
