#!/usr/bin/env python3
"""
Minimal generic SMS gateway shim — same behaviour as sms_gw.pl.
Stdlib only (no pip packages). Not for production without hardening.
See README.md in this directory.
"""
import base64
import json
import ssl
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Any, Dict, Optional
from urllib.error import HTTPError, URLError
from urllib.parse import parse_qs, urlencode, urlparse
from urllib.request import Request, urlopen

DEBUG = True

SID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

DOMAIN_API_SMS_SERVICE = "xxxxxx.pbxmanager.com"
URI_API_SMS_SERVICE_INBOUND = "/sms/generic_xxxxxx/generic"
URI_API_SMS_SERVICE_STATUS = "/sms/generic_xxxxxx/generic/status"

LISTEN_HOST = "127.0.0.1"
LISTEN_PORT = 65533

# Match Perl/LWP: TLS verification disabled (lab use only).
SSL_CTX = ssl.create_default_context()
SSL_CTX.check_hostname = False
SSL_CTX.verify_mode = ssl.CERT_NONE


def _parse_qs_flat(qs: str) -> Dict[str, str]:
    out: Dict[str, str] = {}
    for k, v in parse_qs(qs, keep_blank_values=True).items():
        out[k] = v[0] if len(v) == 1 else ",".join(v)
    return out


def send_sms(account_sid: str, auth_token: str, data: Dict[str, str]) -> Optional[Any]:
    if DEBUG:
        print("Sending SMS:", json.dumps(data))

    body = urlencode(data).encode("utf-8")
    url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
    req = Request(url, data=body, method="POST")
    req.add_header("Content-Type", "application/x-www-form-urlencoded")
    auth = base64.b64encode(f"{account_sid}:{auth_token}".encode()).decode()
    req.add_header("Authorization", f"Basic {auth}")

    try:
        with urlopen(req, context=SSL_CTX) as res:
            raw = res.read().decode("utf-8")
            if DEBUG:
                print("Sending SMS result:", raw)
            return json.loads(raw)
    except HTTPError as e:
        print(e.code, e.reason, file=sys.stderr)
    except URLError as e:
        print(e.reason, file=sys.stderr)
    return None


def post_form(url: str, data: Dict[str, str]) -> bool:
    body = urlencode(data).encode("utf-8")
    req = Request(url, data=body, method="POST")
    req.add_header("Content-Type", "application/x-www-form-urlencoded")
    try:
        with urlopen(req, context=SSL_CTX) as res:
            return 200 <= res.status < 300
    except HTTPError as e:
        print(e.code, e.reason, file=sys.stderr)
    except URLError as e:
        print(e.reason, file=sys.stderr)
    return False


def handle_inbound(data: Dict[str, str]) -> None:
    if DEBUG:
        print("Inbound:", json.dumps(data))
    url = f"https://{DOMAIN_API_SMS_SERVICE}{URI_API_SMS_SERVICE_INBOUND}"
    post_form(url, data)


def handle_status(data: Dict[str, str]) -> None:
    if DEBUG:
        print("Status:", json.dumps(data))
    url = f"https://{DOMAIN_API_SMS_SERVICE}{URI_API_SMS_SERVICE_STATUS}"
    post_form(url, data)


def sms(form: Dict[str, str]) -> Optional[Any]:
    payload = {
        "From": form["from"],
        "To": form["to"],
        "Body": form["body"],
        "StatusCallback": f"https://{DOMAIN_API_SMS_SERVICE}/testsms/generic/status",
    }
    if form.get("mediaUrl"):
        payload["MediaUrl"] = form["mediaUrl"]
    return send_sms(SID, TOKEN, payload)


class Handler(BaseHTTPRequestHandler):
    def log_message(self, format: str, *args: object) -> None:
        if DEBUG:
            super().log_message(format, *args)

    def _read_form(self) -> Dict[str, str]:
        parsed = urlparse(self.path)
        if self.command == "GET":
            return _parse_qs_flat(parsed.query) if parsed.query else {}
        length = int(self.headers.get("Content-Length", 0))
        ct = (self.headers.get("content-type") or "").lower()
        if length and "application/x-www-form-urlencoded" in ct:
            body = self.rfile.read(length).decode("utf-8")
            return _parse_qs_flat(body)
        return {}

    def do_GET(self) -> None:  # noqa: N802
        self._dispatch()

    def do_POST(self) -> None:  # noqa: N802
        self._dispatch()

    def _dispatch(self) -> None:
        path = urlparse(self.path).path
        form = self._read_form()

        if path == "/testsms/generic/send":
            result = sms(form)
            body = json.dumps(result) if result is not None else "null"
            self.send_response(200)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.end_headers()
            self.wfile.write(body.encode("utf-8"))
            return

        if path == "/testsms/generic":
            handle_inbound(form)
            self.send_response(200)
            self.end_headers()
            return

        if path == "/testsms/generic/status":
            handle_status(form)
            self.send_response(200)
            self.end_headers()
            return

        msg = b"custom sms gateway httpd service"
        self.send_response(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.end_headers()
        self.wfile.write(msg)


def main() -> None:
    server = HTTPServer((LISTEN_HOST, LISTEN_PORT), Handler)
    print(f"Upstream: http://{LISTEN_HOST}:{LISTEN_PORT}/")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\nShutting down.")
        server.server_close()


if __name__ == "__main__":
    main()
