import os
import random
import threading
from datetime import datetime
from multiprocessing.managers import BaseManager
from typing import Optional

from ._Clock import Clock


class _ClockState:
    def __init__(self):
        self._value: Optional[str] = None

    def get(self) -> Optional[str]:
        return self._value

    def set(self, v: Optional[str]) -> None:
        self._value = v


class ClockMock(Clock):
    def __init__(self):
        self._port = random.randint(40000, 59999)
        self._authkey = os.urandom(20)
        self._state = _ClockState()

        class _Server(BaseManager):
            pass
        _Server.register("clock_state", callable=lambda: self._state, exposed=["get", "set"])

        thread = threading.Thread(
            target=_Server(("127.0.0.1", self._port), self._authkey).get_server().serve_forever,
            daemon=True,
        )
        thread.start()

    @property
    def server_info(self) -> dict:
        return {
            "host": "127.0.0.1",
            "port": self._port,
            "authkey": self._authkey.hex(),
        }

    def freeze(self, dt: datetime) -> None:
        self._state.set(dt.isoformat())

    def unfreeze(self) -> None:
        self._state.set(None)

    def now(self) -> datetime:
        mock = self._state.get()
        if mock:
            return datetime.fromisoformat(mock)
        return datetime.now()

    def utc_now(self) -> datetime:
        mock = self._state.get()
        if mock:
            return datetime.fromisoformat(mock)
        return datetime.utcnow()
