"""
MySender client for Python 3.7+ (no packages needed).
Send WhatsApp messages from YOUR software through YOUR OWN WhatsApp number linked in your MySender account.

    from mysender_client import MySender
    ms = MySender("YOUR_AUTH_KEY", "YOUR_APP_KEY")
    r = ms.send_text("9876543210", "Hello from my software")      # 10-digit Indian numbers get 91 added
    if not r["ok"]: print(r["error"])

Full guide: https://mysender.mawanta.com/developers
"""
import json, random, re, time, urllib.parse, urllib.request, urllib.error

MAX_CHARS = 4000


class MySender:
    def __init__(self, auth_key, app_key, base_url="https://mysender.mawanta.com", default_country="91"):
        self.auth, self.app = auth_key.strip(), app_key.strip()
        self.base, self.country, self._last = base_url.rstrip("/"), default_country, 0.0

    def number(self, raw):
        d = re.sub(r"\D+", "", str(raw or "")).lstrip("0")
        if len(d) == 10:
            d = self.country + d
        return d if 11 <= len(d) <= 13 else None

    def send_text(self, to, message):
        return self.send(to, {"message": message})

    def send_file(self, to, message, public_file_url):
        return self.send(to, {"message": message, "file": public_file_url})

    def send_template(self, to, template_id, variables=None):
        """variables: {'{name}': 'Ramesh', '{amount}': '1500'} (keys exactly as written in the template)"""
        return self.send(to, {"template_id": template_id, "variables": variables or {}})

    def send(self, to, payload):
        num = self.number(to)
        if not num:
            return {"ok": False, "status": 0, "error": "Invalid mobile number: %s" % to}
        parts = [payload]
        msg = payload.get("message")
        if msg and len(msg) > MAX_CHARS and not payload.get("file"):
            parts = [{"message": msg[i:i + MAX_CHARS]} for i in range(0, len(msg), MAX_CHARS)]
        last = None
        for i, p in enumerate(parts):
            if i:
                time.sleep(random.randint(10, 20))
            last = self._post(dict(p, to=num))
            if not last["ok"]:
                return last
        return last

    def send_many(self, rows, on_result=None, min_gap=10, max_gap=20):
        """one at a time, never faster than one message every min_gap seconds (random up to max_gap): keeps your number safe"""
        out = []
        for row in rows:
            wait = self._last + random.randint(min_gap, max_gap) - time.time()
            if wait > 0:
                time.sleep(wait)
            row = dict(row)
            to = row.pop("to")
            res = self.send(to, row)
            self._last = time.time()
            out.append(res)
            if on_result:
                on_result(dict(row, to=to), res)
        return out

    def _post(self, data):
        form = [("authkey", self.auth), ("appkey", self.app)]
        for k, v in data.items():
            if k == "variables" and isinstance(v, dict):
                form += [("variables[%s]" % vk, str(vv)) for vk, vv in v.items()]
            elif v is not None:
                form.append((k, str(v)))
        body = urllib.parse.urlencode(form).encode()
        status, j, err = 0, {}, ""
        for t in range(1, 5):
            try:
                req = urllib.request.Request(self.base + "/api/create-message", data=body, headers={"Accept": "application/json"})
                try:
                    with urllib.request.urlopen(req, timeout=90) as r:
                        status, raw = r.status, r.read().decode("utf-8", "replace")
                except urllib.error.HTTPError as e:
                    status, raw = e.code, e.read().decode("utf-8", "replace")
                try:
                    j = json.loads(raw)
                except ValueError:
                    j = {}
                if status == 200 and j.get("message_status") == "Success":
                    return {"ok": True, "status": 200, "data": j.get("data", {}), "error": None}
                if status == 429 or status >= 500:
                    time.sleep(random.randint(15, 40) * t)
                    continue
                break
            except Exception as e:  # network trouble: wait and try again
                err, status = str(e), 0
                time.sleep(random.randint(15, 40) * t)
        msg = j.get("error") or j.get("message") or err or "Unknown error"
        if isinstance(j.get("data"), dict):
            msg += " " + json.dumps(j["data"])
        return {"ok": False, "status": status, "error": msg.strip()}
