import random
import re
import subprocess
from typing import Optional

from envied.core import binaries
from envied.core.proxies.proxy import Proxy


class Hola(Proxy):
    def __init__(self):
        """
        Proxy Service using Hola's direct connections via the hola-proxy project.
        https://github.com/Snawoot/hola-proxy
        """
        self.binary = binaries.HolaProxy
        if not self.binary:
            raise EnvironmentError("hola-proxy executable not found but is required for the Hola proxy provider.")

        self.countries = self.get_countries()

    def __repr__(self) -> str:
        countries = len(self.countries)

        return f"{countries} Countr{['ies', 'y'][countries == 1]}"

    def get_proxy(self, query: str) -> Optional[str]:
        """
        Get an HTTP proxy URI for a Datacenter ('direct') or Residential ('lum') Hola server.

        TODO: - Add ability to select 'lum' proxies (residential proxies).
              - Return and use Proxy Authorization
        """
        query = query.lower()

        p = subprocess.check_output(
            [self.binary, "-country", query, "-list-proxies"], stderr=subprocess.STDOUT
        ).decode()

        if "Transaction error: temporary ban detected." in p:
            raise ConnectionError("Hola banned your IP temporarily from it's services. Try change your IP.")

        username, password, proxy_authorization = re.search(
            r"Login: (.*)\nPassword: (.*)\nProxy-Authorization: (.*)", p
        ).groups()

        servers = re.findall(r"(zagent.*)", p)
        proxies = []
        for server in servers:
            host, ip_address, direct, peer, hola, trial, trial_peer, vendor = server.split(",")
            proxies.append(f"http://{username}:{password}@{ip_address}:{peer}")

        proxy = random.choice(proxies)
        return proxy

    def get_countries(self) -> list[dict[str, str]]:
        """Get a list of available Countries."""
        p = subprocess.check_output([self.binary, "-list-countries"]).decode("utf8")

        return [{code: name} for country in p.splitlines() for (code, name) in [country.split(" - ", maxsplit=1)]]
