#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Puente de Impresoras - Mi Restaurante
======================================
Corre en la laptop de caja. Cada pocos segundos pregunta al sistema web si
hay tickets pendientes de imprimir, y los manda directo por red a la
impresora que corresponda (caja, cocina o barra), usando sus direcciones
IP configuradas en el sistema (Configuracion -> Impresoras).

No necesita instalar la impresora en Windows: envia los datos crudos por
el puerto 9100 (estandar en impresoras termicas de red tipo Epson).

Requisitos: Python 3.8 o mas nuevo (ya viene en Windows 10/11 recientes,
o se descarga gratis en https://www.python.org/downloads/).

Como usarlo:
  1. Doble clic en este archivo (o "python puente_impresoras.py" en la consola).
  2. Dejalo abierto mientras el local esta atendiendo.
  3. Si se cierra o la laptop se reinicia, hay que volver a abrirlo
     (se puede configurar para que inicie solo con Windows, avisale a soporte).
"""

import base64
import socket
import sys
import time
import urllib.request
import urllib.error
import json

BASE_URL = "__BASE_URL__"
TOKEN = "__TOKEN__"
POLL_SECONDS = 4
PRINT_TIMEOUT = 5
PRINT_PORT = 9100


def log(msg):
    print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)


def obtener_trabajos_pendientes():
    url = f"{BASE_URL}/api/print-bridge/jobs"
    req = urllib.request.Request(url, headers={"X-Bridge-Token": TOKEN})
    with urllib.request.urlopen(req, timeout=10) as resp:
        data = json.loads(resp.read().decode("utf-8"))
        return data.get("jobs", [])


def imprimir_en_ip(ip, contenido_bytes):
    with socket.create_connection((ip, PRINT_PORT), timeout=PRINT_TIMEOUT) as s:
        s.sendall(contenido_bytes)


def confirmar_trabajo(job_id, ok=True, mensaje_error=None):
    accion = "done" if ok else "error"
    url = f"{BASE_URL}/api/print-bridge/jobs/{job_id}/{accion}"
    body = json.dumps({"message": mensaje_error or ""}).encode("utf-8")
    req = urllib.request.Request(
        url, data=body, method="POST",
        headers={"X-Bridge-Token": TOKEN, "Content-Type": "application/json"},
    )
    urllib.request.urlopen(req, timeout=10)


def procesar_trabajo(job):
    job_id = job["id"]
    ip = job.get("ip")
    if not ip:
        log(f"Trabajo #{job_id} sin IP configurada para '{job['station']}', se omite.")
        return

    try:
        contenido = base64.b64decode(job["content"])
        imprimir_en_ip(ip, contenido)
        confirmar_trabajo(job_id, ok=True)
        log(f"Trabajo #{job_id} ({job['kind']} -> {job['station']} @ {ip}) impreso OK.")
    except Exception as e:
        mensaje = str(e)
        log(f"ERROR imprimiendo trabajo #{job_id} en {ip}: {mensaje}")
        try:
            confirmar_trabajo(job_id, ok=False, mensaje_error=mensaje)
        except Exception:
            pass  # si ni siquiera se pudo avisar el error, seguimos igual


def main():
    log("Puente de impresoras iniciado.")
    log(f"Conectando a: {BASE_URL}")
    while True:
        try:
            trabajos = obtener_trabajos_pendientes()
            for job in trabajos:
                procesar_trabajo(job)
        except urllib.error.HTTPError as e:
            if e.code == 401:
                log("Token invalido. Vuelve a descargar el puente desde Configuracion -> Impresoras.")
            else:
                log(f"Error HTTP {e.code} al consultar trabajos.")
        except urllib.error.URLError as e:
            log(f"Sin conexion al sistema: {e.reason}")
        except Exception as e:
            log(f"Error inesperado: {e}")

        time.sleep(POLL_SECONDS)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        log("Puente detenido.")
        sys.exit(0)
