"""AuthGuard client SDK for Python. Requires: pip install requests cryptography"""

import hashlib
import json
import platform
import secrets
import subprocess
import time
import uuid

import requests
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey


class AuthGuardError(Exception):
    def __init__(self, message, response=None):
        super().__init__(message)
        self.response = response or {}


def get_hwid() -> str:
    raw = ""
    try:
        if platform.system() == "Windows":
            import winreg

            with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Cryptography", 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY) as k:
                raw = winreg.QueryValueEx(k, "MachineGuid")[0]
        elif platform.system() == "Darwin":
            out = subprocess.check_output(["ioreg", "-rd1", "-c", "IOPlatformExpertDevice"], text=True)
            raw = next(line.split('"')[-2] for line in out.splitlines() if "IOPlatformUUID" in line)
        else:
            with open("/etc/machine-id") as f:
                raw = f.read().strip()
    except Exception:
        raw = str(uuid.getnode())
    return hashlib.sha256(raw.encode()).hexdigest()


class AuthGuard:
    # Some hosts' firewalls block the default python-requests user agent, so send our own.
    HEADERS = {"User-Agent": "AuthGuard-SDK/1.0"}

    def __init__(self, app_id: str, version: str, public_key: str, url: str = "https://authguard.dk/api/client"):
        self.app_id = app_id
        self.version = version
        self.url = url
        self.hwid = get_hwid()
        self.session_id = None
        self.info = {}
        self._key = Ed25519PublicKey.from_public_bytes(bytes.fromhex(public_key))

    def _request(self, type_: str, **fields) -> dict:
        nonce = secrets.token_hex(16)
        body = {"type": type_, "app_id": self.app_id, "nonce": nonce, **fields}
        if self.session_id and type_ != "init":
            body["sessionid"] = self.session_id
        try:
            r = requests.post(self.url, json=body, headers=self.HEADERS, timeout=15)
            outer = r.json()
            payload, signature = outer["payload"], outer["signature"]
        except (requests.RequestException, ValueError, KeyError, TypeError) as e:
            raise AuthGuardError(f"Could not reach the license server ({e.__class__.__name__})")
        try:
            self._key.verify(bytes.fromhex(signature), payload.encode())
        except (InvalidSignature, ValueError):
            raise AuthGuardError("Invalid server signature")
        data = json.loads(payload)
        if data.get("nonce") != nonce or abs(time.time() - data.get("timestamp", 0)) > 300:
            raise AuthGuardError("Invalid server response")
        if not data.get("success"):
            raise AuthGuardError(data.get("message", "Request failed"), data)
        return data

    def init(self):
        data = self._request("init", version=self.version, hwid=self.hwid)
        self.session_id = data["sessionid"]

    def license(self, key: str) -> dict:
        self.info = self._request("license", key=key.strip(), hwid=self.hwid)["info"]
        return self.info

    def register(self, username: str, password: str, key: str) -> dict:
        self.info = self._request("register", username=username, password=password, key=key.strip(), hwid=self.hwid)["info"]
        return self.info

    def login(self, username: str, password: str) -> dict:
        self.info = self._request("login", username=username, password=password, hwid=self.hwid)["info"]
        return self.info

    def var(self, name: str) -> str:
        return self._request("var", name=name)["value"]

    def check(self) -> bool:
        try:
            self._request("check")
            return True
        except AuthGuardError:
            return False
