import json
import time
import urllib.error
import urllib.request


class Sender:
    def __init__(self, config):
        self.config = config
        self.endpoint = config.get_endpoint_url()
        self.max_retries = 3
        self.timeout_seconds = 5
        self.max_total_timeout_seconds = 10

    def send(self, data):
        if not self.config.is_tracking_enabled():
            return {"success": False, "reason": "disabled"}

        if self.config.is_top_level_install():
            return {"success": False, "reason": "top-level-install"}

        payload = data if isinstance(data, str) else json.dumps(data)
        started = time.monotonic()

        try:
            return self._send_with_retry(payload, started)
        except Exception as error:
            if self.config.is_verbose():
                print(f"[package-tracker] Failed to send: {error}")
            return {"success": False, "error": str(error)}

    def _send_with_retry(self, payload, started):
        attempt = 0
        while True:
            elapsed = time.monotonic() - started
            if elapsed > self.max_total_timeout_seconds:
                raise TimeoutError("Maximum timeout exceeded")

            try:
                response = self._make_request(payload)
                return {"success": True, "statusCode": response}
            except Exception:
                if attempt >= self.max_retries:
                    raise
                delay = 2 ** attempt
                time.sleep(delay)
                attempt += 1

    def _make_request(self, payload):
        request = urllib.request.Request(
            self.endpoint,
            data=payload.encode("utf-8"),
            method="POST",
            headers={
                "Content-Type": "application/json",
                "User-Agent": f"package-tracker/{self.config.get_package_version()}",
            },
        )

        try:
            with urllib.request.urlopen(request, timeout=self.timeout_seconds) as response:
                status = getattr(response, "status", 200)
                if 200 <= status < 300:
                    return status
                raise RuntimeError(f"Request failed with status {status}")
        except urllib.error.HTTPError as error:
            raise RuntimeError(f"Request failed with status {error.code}") from error
