import asyncio
import time
from test_framework.portseed import (
    electrum_rpc_ssl_port,
    electrum_ws_port,
    electrum_ws_ssl_port,
    electrum_https_port,
)
from test_framework.portseed import electrum_rpc_port, electrum_http_port
from test_framework.connectrum.client import StratumClient
from test_framework.connectrum.svr_info import ServerInfo
from test_framework.environment import (
    testing_tcp_ssl,
    testing_websocket,
    testing_http,
    testing_websocket_ssl,
    testing_https,
)


class TestClient(StratumClient):
    is_connected = False

    def connection_lost(self, protocol):
        self.is_connected = False
        super().connection_lost(protocol)


class ElectrumConnection:
    protocol = "tcp"

    def __init__(self, loop=None, *, protocol=None, warn_on_connection_loss=False):
        self.cli = TestClient(loop, warn_on_connection_loss)
        if protocol is not None:
            self.protocol = protocol
        else:
            if testing_websocket():
                self.protocol = "ws"
            elif testing_http():
                self.protocol = "http"
            elif testing_tcp_ssl():
                self.protocol = "tcp-ssl"
            elif testing_websocket_ssl():
                self.protocol = "wss"
            elif testing_https():
                self.protocol = "https"
            else:
                # default to tcp
                pass

    async def connect(self, node_index=0, retry=True, edition=None):
        connect_timeout = 120

        start = time.time()

        while True:
            try:
                if self.protocol == "tcp":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            ip_addr="127.0.0.1",
                            ports=await electrum_rpc_port(n=node_index),
                        )
                    )
                elif self.protocol == "ws":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            hostname="localhost",
                            ip_addr="127.0.0.1",
                            ports=f"g{await electrum_ws_port(n=node_index)}",
                        ),
                        proto_code="g",
                    )

                elif self.protocol == "wss":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            hostname="localhost",
                            ip_addr="127.0.0.1",
                            ports=f"w{await electrum_ws_ssl_port(n=node_index)}",
                        ),
                        proto_code="w",
                    )

                elif self.protocol == "http":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            hostname="127.0.0.1",
                            ports=f"h{await electrum_http_port(n=node_index)}",
                        ),
                        proto_code="h",
                    )
                elif self.protocol == "tcp-ssl":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            hostname="127.0.0.1",
                            ports=f"s{await electrum_rpc_ssl_port(n=node_index)}",
                        ),
                        proto_code="s",
                    )
                elif self.protocol == "https":
                    await self.cli.connect(
                        ServerInfo(
                            None,
                            hostname="127.0.0.1",
                            ports=f"H{await electrum_https_port(n=node_index)}",
                        ),
                        proto_code="H",
                    )

                else:
                    raise Exception(f"Unsupported protocol {self.protocol}")

                self.cli.is_connected = True

                if edition is not None:
                    await self.call("server.version", "test-client", "1.4", edition)

                break

            except Exception as e:

                if not retry:
                    raise e

                if time.time() >= (start + connect_timeout):
                    raise Exception(
                        f"Failed to connect to electrum server within {connect_timeout} seconds. Error '{e}'"
                    ) from e
                print(".", end="", flush=True)

            await asyncio.sleep(1)

    def disconnect(self):
        self.cli.close()

    async def call(self, method, *args):
        if not self.cli.is_connected:
            raise Exception("not connected")

        ok = await self.cli.RPC(method, *args)
        return ok

    async def subscribe(self, method, *args):
        if not self.cli.is_connected:
            raise Exception("not connected")
        future, queue = self.cli.subscribe(method, *args)
        result = await future
        return result, queue

    def is_connected(self):
        return self.cli.is_connected
