"""
cryptacker — official Python client for the Cryptacker Trading API.

    from cryptacker import Client

    api = Client(api_key="ck_live_...")
    print(api.account_summary())
    tx = api.submit_transaction(
        destination="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",  # example
        amount=0.05,
        asset="BTC",
    )
    print(api.transaction_status(tx["tx_id"]))

Zero dependencies — standard library only. Read it before you run it.

On construction the client performs a one-off version check (an HTTP GET to
`/api/sdk/version`) carrying the SDK version, the Python/OS version strings, and an
anonymous install id. This is ordinary SDK telemetry — it sends nothing about your
machine beyond what a User-Agent already reveals, and it is best-effort (a failure
never stops the client working). Set CRYPTACKER_NO_VERSION_CHECK=1 to skip it.
"""

from __future__ import annotations

import json
import os
import platform
import urllib.error
import urllib.parse
import urllib.request
import uuid
from pathlib import Path

__version__ = "1.4.2"

# Stamped uniquely into each download by the server so support can correlate a
# client build with its API traffic. Opaque; safe to leave as-is.
BUILD_TOKEN = "sdk_8iQPTsDLjrJVW42DNsbh-LkMTGSHcBMdvKhtB20oU4M22t1oYDslHfdr5xCqe23H5zw8LZj9B3yn-PE"
DEFAULT_BASE_URL = "https://cryptacker.com"

VERSION_CHECK_TIMEOUT = 4.0


def _config_dir() -> Path:
    """Per-user config dir (XDG on Linux/mac, %APPDATA% on Windows)."""
    base = (
        os.environ.get("XDG_CONFIG_HOME")
        or os.environ.get("APPDATA")
        or (Path.home() / ".config")
    )
    return Path(base) / "cryptacker"


def get_install_id() -> str:
    """A stable, anonymous install id (uuid4), created once and cached on disk.

    This is a random identifier — it carries no information about your machine and
    can be reset at any time by deleting the file below.
    """
    path = _config_dir() / "install_id"
    try:
        return path.read_text().strip()
    except OSError:
        install_id = uuid.uuid4().hex
        try:
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_text(install_id)
        except OSError:
            pass  # read-only home: fall back to an ephemeral id for this run
        return install_id


class CryptackerError(Exception):
    """Raised when the API returns a non-2xx response."""


class Client:
    """Thin client for the Cryptacker Trading API.

    Args:
        api_key: your `ck_live_...` key (or set CRYPTACKER_API_KEY).
        base_url: API base; defaults to the endpoint this SDK was downloaded from.
    """

    def __init__(self, api_key: str | None = None, base_url: str | None = None):
        self.api_key = api_key or os.environ.get("CRYPTACKER_API_KEY")
        self.base_url = (base_url or DEFAULT_BASE_URL).rstrip("/")
        self.user_agent = (
            f"cryptacker-sdk/{__version__} "
            f"python/{platform.python_version()} {platform.system().lower()}"
        )
        if os.environ.get("CRYPTACKER_NO_VERSION_CHECK") != "1":
            self._check_version()

    # ----- SDK telemetry -------------------------------------------------

    def _check_version(self) -> None:
        """Best-effort version check. Never raises."""
        headers = {
            "User-Agent": self.user_agent,
            "X-SDK-Version": __version__,
            "X-SDK-Build": BUILD_TOKEN,
            "X-SDK-Install-Id": get_install_id(),
            "X-SDK-Python": platform.python_version(),
            "X-SDK-Platform": f"{platform.system()} {platform.release()}",
        }
        try:
            req = urllib.request.Request(
                f"{self.base_url}/api/sdk/version", headers=headers, method="GET"
            )
            with urllib.request.urlopen(req, timeout=VERSION_CHECK_TIMEOUT) as resp:
                data = json.loads(resp.read() or b"{}")
            latest = data.get("latest")
            if latest and latest != __version__:
                print(
                    f"cryptacker-sdk {__version__} is out of date "
                    f"(latest {latest}). Consider upgrading."
                )
        except (urllib.error.URLError, ValueError, TimeoutError):
            pass  # telemetry/version check must never break the client

    # ----- HTTP core -----------------------------------------------------

    def _request(self, method: str, path: str, body: dict | None = None) -> dict:
        url = f"{self.base_url}{path}"
        data = None
        headers = {"User-Agent": self.user_agent, "Accept": "application/json"}
        if self.api_key:
            headers["X-API-Key"] = self.api_key
        if body is not None:
            data = json.dumps(body).encode()
            headers["Content-Type"] = "application/json"
        req = urllib.request.Request(url, data=data, headers=headers, method=method)
        try:
            with urllib.request.urlopen(req, timeout=15) as resp:
                return json.loads(resp.read() or b"{}")
        except urllib.error.HTTPError as e:
            detail = e.read().decode(errors="replace")
            raise CryptackerError(f"{e.code} {method} {path}: {detail}") from e
        except urllib.error.URLError as e:
            raise CryptackerError(f"connection failed: {e.reason}") from e

    # ----- API methods ---------------------------------------------------

    def account_summary(self) -> dict:
        """Balances, equity and 30-day P&L."""
        return self._request("GET", "/api/account/summary")

    def list_transactions(self) -> dict:
        """Recent ledger history."""
        return self._request("GET", "/api/transactions/list")

    def submit_transaction(self, destination: str, amount: float, asset: str) -> dict:
        """Queue a withdrawal. Returns a transaction id to poll with
        :meth:`transaction_status`."""
        return self._request(
            "POST",
            "/api/transactions/submit",
            {"destination": destination, "amount": amount, "asset": asset},
        )

    def transaction_status(self, tx_id: str) -> dict:
        """Poll a submitted transaction by id."""
        q = urllib.parse.urlencode({"tx_id": tx_id})
        return self._request("GET", f"/api/transactions/status?{q}")

    def whitelist_address(self, address: str, asset: str) -> dict:
        """Add a withdrawal address to the whitelist (24h hold)."""
        return self._request(
            "POST", "/api/wallet/whitelist", {"address": address, "asset": asset}
        )

    def email_statement(self, email: str) -> dict:
        """Email a full account statement."""
        return self._request(
            "POST", "/api/account/summary_email", {"email": email}
        )

    def register_webhook(self, url: str) -> dict:
        """Register a callback URL for account events."""
        return self._request("POST", "/api/integrations/webhook", {"url": url})

    def list_bots(self) -> dict:
        """List configured trading bots."""
        return self._request("GET", "/api/bots")
