"""
SABAI Print Agent - Agente de Impresion Local
==============================================
Instala este script en la PC del restaurante conectada a la red local.
Actua como puente entre el VPS (internet) y las impresoras termicas locales.

Soporta dos modos de impresora:
  - RED  : impresora con IP propia en la red (WiFi o Ethernet) - recomendado
  - USB  : impresora conectada por USB a esta PC (requiere pywin32)

Requisitos minimos:
  pip install flask

Para impresoras USB adicionalmente:
  pip install pywin32

Uso:
  # Impresora de RED (mas comun):
  python sabai_print_server.py --modo red --ip 192.168.1.50 --puerto-impresora 9100

  # Impresora USB:
  python sabai_print_server.py --modo usb --printer "EPSON TM-T20"

  # Ver impresoras USB disponibles en este PC:
  python sabai_print_server.py --listar

Puerto del agente: 8765 (configura esto en "URL Agente Local" del panel)
  Ejemplo URL Agente Local: http://192.168.1.10:8765
"""

import argparse
import base64
import json
import socket
import sys
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer


# ---------------------------------------------------------------------------
# Envio a impresora de RED (TCP directo al puerto 9100)
# ---------------------------------------------------------------------------

def enviar_a_red(data: bytes, ip: str, puerto: int):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(5)
        s.connect((ip, puerto))
        s.sendall(data)
    print(f"  -> Enviado {len(data)} bytes a {ip}:{puerto}")


# ---------------------------------------------------------------------------
# Envio a impresora USB (Windows, requiere pywin32)
# ---------------------------------------------------------------------------

def enviar_a_usb(data: bytes, printer_name: str):
    try:
        import win32print
    except ImportError:
        raise RuntimeError(
            "Falta pywin32. Instala con: pip install pywin32"
        )
    handle = win32print.OpenPrinter(printer_name)
    try:
        win32print.StartDocPrinter(handle, 1, ("Sabai ESC/POS", None, "RAW"))
        win32print.StartPagePrinter(handle)
        win32print.WritePrinter(handle, data)
        win32print.EndPagePrinter(handle)
        win32print.EndDocPrinter(handle)
    finally:
        win32print.ClosePrinter(handle)
    print(f"  -> Impreso {len(data)} bytes en '{printer_name}'")


def listar_usb():
    try:
        import win32print
        impresoras = [p[2] for p in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL)]
        print("\nImpresoras USB/locales disponibles en este PC:")
        for i, nombre in enumerate(impresoras, 1):
            print(f"  [{i}] {nombre}")
        print()
    except ImportError:
        print("ERROR: pywin32 no instalado. Ejecuta: pip install pywin32")


# ---------------------------------------------------------------------------
# Servidor HTTP - recibe POST /print del VPS
# ---------------------------------------------------------------------------

class PrintHandler(BaseHTTPRequestHandler):

    # Configuracion inyectada al iniciar
    modo = "red"
    printer_ip = "192.168.1.50"
    printer_puerto = 9100
    printer_usb = "EPSON TM-T20"

    def do_GET(self):
        if self.path == "/status":
            self._responder(200, {"status": "ok", "modo": self.modo})
        else:
            self._responder(404, {"error": "ruta no encontrada"})

    def do_POST(self):
        if self.path != "/print":
            self._responder(404, {"error": "ruta no encontrada"})
            return

        length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(length)

        try:
            payload = json.loads(body)
            data = base64.b64decode(payload["data"])
        except Exception as e:
            self._responder(400, {"error": f"payload invalido: {e}"})
            return

        try:
            if self.modo == "red":
                enviar_a_red(data, self.printer_ip, self.printer_puerto)
            else:
                enviar_a_usb(data, self.printer_usb)
            self._responder(200, {"ok": True, "bytes": len(data)})
        except Exception as e:
            print(f"  ERROR al imprimir: {e}")
            self._responder(500, {"error": str(e)})

    def _responder(self, code: int, body: dict):
        payload = json.dumps(body).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(payload)))
        self.end_headers()
        self.wfile.write(payload)

    def log_message(self, fmt, *args):
        print(f"  [{self.address_string()}] {fmt % args}")


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def get_local_ip():
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect(("8.8.8.8", 80))
            return s.getsockname()[0]
    except Exception:
        return socket.gethostbyname(socket.gethostname())


def iniciar(args):
    PrintHandler.modo = args.modo
    PrintHandler.printer_ip = args.ip
    PrintHandler.printer_puerto = args.puerto_impresora
    PrintHandler.printer_usb = args.printer

    local_ip = get_local_ip()

    print("=" * 52)
    print("  SABAI Print Agent")
    print("=" * 52)
    print(f"  Modo         : {args.modo.upper()}")
    if args.modo == "red":
        print(f"  Impresora IP : {args.ip}:{args.puerto_impresora}")
    else:
        print(f"  Impresora USB: {args.printer}")
    print(f"  Agente puerto: {args.agente_puerto}")
    print(f"  IP de esta PC: {local_ip}")
    print()
    print(f"  --> En el panel, campo 'URL Agente Local':")
    print(f"      http://{local_ip}:{args.agente_puerto}")
    print()
    print("  Esperando trabajos de impresion... (Ctrl+C para detener)")
    print("=" * 52)

    server = HTTPServer(("0.0.0.0", args.agente_puerto), PrintHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\nAgente detenido.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="SABAI Print Agent - Agente de impresion local"
    )
    parser.add_argument(
        "--modo",
        choices=["red", "usb"],
        default="red",
        help="'red' = impresora con IP (default) | 'usb' = impresora USB"
    )
    parser.add_argument(
        "--ip",
        default="192.168.1.50",
        help="IP de la impresora de red (solo modo red)"
    )
    parser.add_argument(
        "--puerto-impresora",
        type=int,
        default=9100,
        help="Puerto TCP de la impresora de red (default: 9100)"
    )
    parser.add_argument(
        "--printer",
        default="",
        help="Nombre exacto de la impresora USB en Windows (solo modo usb)"
    )
    parser.add_argument(
        "--agente-puerto",
        type=int,
        default=8765,
        help="Puerto HTTP de este agente (default: 8765)"
    )
    parser.add_argument(
        "--listar",
        action="store_true",
        help="Lista las impresoras USB disponibles y sale"
    )

    args = parser.parse_args()

    if args.listar:
        listar_usb()
        sys.exit(0)

    if args.modo == "usb" and not args.printer:
        print("ERROR: En modo USB debes indicar el nombre de la impresora.")
        print("  Usa --listar para ver las disponibles.")
        print("  Ejemplo: --printer \"EPSON TM-T20\"")
        sys.exit(1)

    iniciar(args)
