#!/usr/bin/env python3
"""
PizzaPOS print agent — runs on the shop counter PC.
Polls the server for new orders and prints each one to a thermal (ESC/POS)
printer automatically, so dockets appear in the kitchen with no click.

Config via environment variables:
  POS_TOKEN    your shop token (POS → Setup tab)            [required]
  POS_PRINTER  "192.168.1.50"  network printer (port 9100)  [required]
               "192.168.1.50:9100"  explicit port
               "lp"            default CUPS/macOS printer (raw)
               "lp:Kitchen"    a named CUPS printer
  POS_SERVER   default https://pizza.aydayazdani.com
  POS_POLL     seconds between polls (default 3)

Run:  POS_TOKEN=xxxx POS_PRINTER=192.168.1.50 python3 agent.py
Dependency-free (standard library only).
"""
import os, sys, time, json, socket, subprocess, datetime, urllib.request, urllib.parse

SERVER  = os.environ.get("POS_SERVER", "https://pizza.aydayazdani.com").rstrip("/")
TOKEN   = os.environ.get("POS_TOKEN", "").strip()
PRINTER = os.environ.get("POS_PRINTER", "").strip()
POLL    = float(os.environ.get("POS_POLL", "3"))

if not TOKEN:   sys.exit("Set POS_TOKEN to your shop token (POS → Setup tab).")
if not PRINTER: sys.exit("Set POS_PRINTER (e.g. 192.168.1.50, or 'lp' for the default printer).")

ESC, GS = b"\x1b", b"\x1d"
W = 32  # chars per line on an 80mm printer

def line(s):  # encode a text line. CP1252 so £ (0xA3) etc. print correctly
    return (s + "\n").encode("cp1252", "replace")

def docket(o, cur):
    b = bytearray()
    b += ESC + b"@"                       # init
    b += ESC + b"t" + b"\x10"             # select code page 16 (WPC1252) so £ prints
    b += ESC + b"a" + b"\x01"             # centre
    b += GS + b"!" + b"\x11"              # double size
    b += b"KITCHEN ORDER\n"
    b += GS + b"!" + b"\x00"              # normal
    b += line((o.get("order_type") or "").upper())
    b += ESC + b"a" + b"\x00"             # left
    b += line("-" * W)
    b += line(datetime.datetime.now().strftime("%d/%m  %H:%M"))
    b += ESC + b"E" + b"\x01" + line(o.get("name") or "Walk-in") + ESC + b"E" + b"\x00"
    if o.get("phone"):   b += line(o["phone"])
    if o.get("address"): b += line(o["address"])
    if o.get("notes"):   b += line("! " + o["notes"])
    b += line("-" * W)
    for i in o.get("items", []):
        d = (" " + i["detail"]) if i.get("detail") else ""
        b += line(f"{i['qty']}x {i['name']}{d}")
    b += line("-" * W)
    total = f"{cur}{o.get('total_pence', 0) / 100:.2f}"
    b += ESC + b"E" + b"\x01" + line(f"TOTAL {total}") + ESC + b"E" + b"\x00"
    b += line(f"{(o.get('payment_method') or '').upper()} {'PAID' if o.get('paid') else 'UNPAID'}")
    b += line("#" + str(o.get("id", ""))[:8])
    b += b"\n\n\n" + GS + b"V" + b"\x00"  # feed + full cut
    return bytes(b)

def send(data):
    if PRINTER.startswith("lp"):
        name = PRINTER.split(":", 1)[1] if ":" in PRINTER else None
        cmd = ["lp", "-o", "raw"] + (["-d", name] if name else [])
        subprocess.run(cmd, input=data, check=True)
    else:
        host, _, port = PRINTER.partition(":")
        with socket.create_connection((host, int(port or 9100)), timeout=8) as s:
            s.sendall(data)

def api(path, body=None):
    if body is None:
        url = f"{SERVER}/{path}?token={urllib.parse.quote(TOKEN)}"
        req = urllib.request.Request(url, headers={"X-POS-Secret": TOKEN})
    else:
        req = urllib.request.Request(f"{SERVER}/{path}", data=json.dumps(body).encode(),
                                     headers={"X-POS-Secret": TOKEN, "Content-Type": "application/json"}, method="POST")
    with urllib.request.urlopen(req, timeout=15) as r:
        return json.load(r)

print(f"PizzaPOS print agent → {SERVER} → printer {PRINTER}, every {POLL}s. Ctrl-C to stop.")
while True:
    try:
        res = api("print_queue.php")
        cur = res.get("currency", "GBP")
        for o in res.get("orders", []):
            try:
                send(docket(o, cur))
                api("print_queue.php", {"action": "ack", "id": o["id"]})
                print("printed", o["id"][:8], o.get("name"))
            except Exception as e:
                print("print failed (will retry):", e)   # not acked -> retried next loop
                break
    except Exception as e:
        print("poll error:", e)
    time.sleep(POLL)
