import logging
import unittest
from unittest import mock
from unittest.mock import AsyncMock

from kingpin.actors import base, exceptions, misc, utils

log = logging.getLogger(__name__)


class FakeActor(base.BaseActor):
    """Fake Actor use for Unit Tests"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.conn = mock.MagicMock()
        self.conn.call = AsyncMock(return_value=None)
        self.conn.call.__name__ = "test_call"

    @utils.dry("Would have done {0}")
    async def do_thing(self, thing):
        await self.conn.call(thing)

    async def execute(self):
        return self.options["return_value"]


class TestUtils(unittest.IsolatedAsyncioTestCase):
    def test_get_actor(self):
        actor_return_true = {
            "desc": "returns true",
            "actor": "kingpin.actors.test.test_utils.FakeActor",
            "options": {"return_value": True},
        }
        ret = utils.get_actor(actor_return_true, dry=True)
        self.assertEqual(True, ret._options["return_value"])
        self.assertEqual(FakeActor, type(ret))

    def test_get_actor_class(self):
        actor_string = "misc.Sleep"
        ret = utils.get_actor_class(actor_string)
        self.assertEqual(type(misc.Sleep), type(ret))

    def test_get_actor_class_direct(self):
        actor_string = "kingpin.actors.test.test_utils.FakeActor"
        ret = utils.get_actor_class(actor_string)
        self.assertEqual(type(FakeActor), type(ret))

    def test_get_actor_class_bogus_actor(self):
        actor_string = "bogus.actor"
        with self.assertRaises(exceptions.InvalidActor):
            utils.get_actor_class(actor_string)

    async def test_dry_decorator_with_dry_true(self):
        actor = FakeActor("Fake", options={}, dry=True)
        await actor.do_thing("my thing string")
        self.assertFalse(actor.conn.called)

    async def test_dry_decorator_with_dry_false(self):
        actor = FakeActor("Fake", options={}, dry=False)
        await actor.do_thing("my thing string")
        actor.conn.call.assert_has_calls([mock.call("my thing string")])
