import logging
from datetime import datetime
from threading import Thread
from time import sleep

import pytz
from websocket import create_connection

from core.constants import HTTP_HEADERS
from providers.spot.spot_provider import SpotProvider


class WebsocketSpotProvider(SpotProvider):
    """Spot provider using websockets."""

    def __init__(self, name, provider_config, url):
        super().__init__(name, provider_config)
        self._url = url
        self._ws = None
        self._thread = None
        self._stopped = False
        self._last_event_id = None

    def start(self):
        logging.info("Set up websocket connection to " + self.name + " spot API.")
        self._stopped = False
        self._thread = Thread(target=self._run, name=f"WebsocketSpotProvider-{self.name}")
        self._thread.daemon = True
        self._thread.start()

    def stop(self):
        self._stopped = True
        if self._ws:
            self._ws.close()
        if self._thread:
            self._thread.join()

    def _on_open(self):
        self.status = "Waiting for Data"

    def _on_error(self):
        self.status = "Connecting"

    def _run(self):
        while not self._stopped:
            try:
                logging.debug("Connecting to " + self.name + " spot API...")
                self.status = "Connecting"
                self._ws = create_connection(self._url, header=HTTP_HEADERS)
                self.status = "Connected"
                data = self._ws.recv()
                if data:
                    try:
                        new_spot = self._ws_message_to_spot(data)
                        if new_spot:
                            self._submit(new_spot)

                        self.status = "OK"
                        self.last_update_time = datetime.now(pytz.UTC)
                        logging.debug("Received data from " + self.name + " spot API.")

                    except Exception:
                        logging.exception(
                            "Exception processing message from Websocket Spot Provider (" + self.name + ")")

            except Exception as e:
                self.status = "Error"
                logging.exception("Exception in Websocket Spot Provider (" + self.name + ")", e)
            else:
                self.status = "Disconnected"
            sleep(5)  # Wait before trying to reconnect

    def _ws_message_to_spot(self, b):
        """Convert a WS message received from the API into a spot. The exact message data (in bytes) is provided here so the
        subclass implementations can handle the message as string, JSON, XML, whatever the API actually provides."""

        raise NotImplementedError("Subclasses must implement this method")
