import asyncio
import socket
from collections import defaultdict

from aiohttp import web
from aiohttp.test_utils import unused_port


class FakeResolver:

    _LOCAL_HOST = {
        0: '127.0.0.1',
        socket.AF_INET: '127.0.0.1',
        socket.AF_INET6: '::1',
    }

    def __init__(self, port):
        self.port = port

    @asyncio.coroutine
    def resolve(self, host, port=0, family=socket.AF_INET):
        return [
            {
                'hostname': host,
                'host': self._LOCAL_HOST[family],
                'port': self.port,
                'family': family,
                'proto': 0,
                'flags': socket.AI_NUMERICHOST,
            },
        ]


class FakeServer:

    app = None
    handler = None
    server = None

    host = '127.0.0.1'

    def __init__(self, *, side_effect=None, loop):
        self.loop = loop

        if side_effect is None:
            side_effect = {
                'status': 200,
            }

        self._side_effect = side_effect
        self._slop_factor = 0

        self.app = web.Application(loop=loop)
        self.setup_routes()

        self.port = unused_port()

        self.hits = defaultdict(lambda: 0)

    @property
    def side_effect(self):
        return self._side_effect

    def _get_slop_factor(self):
        return self._slop_factor

    def _set_slop_factor(self, value):
        self._slop_factor = value

    slop_factor = property(_get_slop_factor, _set_slop_factor)

    @asyncio.coroutine
    def start(self):
        self.handler = self.app.make_handler(loop=self.loop)
        self.server = yield from self.loop.create_server(
            self.handler,
            self.host,
            self.port,
        )

    def setup_routes(self):
        self.app.router.add_post('/api/1/store/', self.store)

    @asyncio.coroutine
    def store(self, request):
        yield from asyncio.sleep(self.slop_factor, loop=self.loop)

        self.hits[self.side_effect['status']] += 1
        return web.Response(**self.side_effect)

    @asyncio.coroutine
    def close(self):
        if self.server is not None:
            self.server.close()
            yield from self.server.wait_closed()

        if self.app is not None:
            yield from self.app.shutdown()

        if self.handler is not None:
            yield from self.handler.shutdown()

        if self.app is not None:
            yield from self.app.cleanup()