import http.server
import socketserver
import json
import sqlite3
import os
import time

# Global in-memory registry for monitors (Digital Signage)
MONITORS = {} 
MONITORS_FILE = 'monitors.json'

def load_monitors():
    global MONITORS
    if os.path.exists(MONITORS_FILE):
        try:
            with open(MONITORS_FILE, 'r') as f:
                MONITORS = json.load(f)
        except Exception:
            MONITORS = {}

def save_monitors():
    try:
        with open(MONITORS_FILE, 'w') as f:
            json.dump(MONITORS, f, indent=2)
    except Exception as e:
        print(f"Errore salvataggio monitors: {e}")

load_monitors()
# Format: "CODE": { "status": "pending|active", "last_seen": timestamp, "config": { mode: "static", category: "...", categories: [...] } }

# Registro in-memory per le connessioni attive dei device (Monitor App Indipendente)
ACTIVE_CONNECTIONS = {}


import urllib.parse
import sys
import os

# Assicura che la directory dello script sia sempre nel path di ricerca moduli
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if SCRIPT_DIR not in sys.path:
    sys.path.insert(0, SCRIPT_DIR)

import db
import threading
import urllib.request
import urllib.error
import ssl
import re
import datetime
import shutil
import hmac
import hashlib

# Stripe Helper autonomo integrato (zero dipendenze, garantisce funzionamento anche se il file stripe_helper.py non è presente su server remoti)
class InternalStripeHelper:
    STRIPE_API_BASE = "https://api.stripe.com/v1"

    @classmethod
    def _make_stripe_request(cls, endpoint, secret_key, data=None, method="POST"):
        url = f"{cls.STRIPE_API_BASE}/{endpoint.lstrip('/')}"
        headers = {
            "Authorization": f"Bearer {secret_key.strip()}",
            "User-Agent": "Sagra-Stripe-Helper/1.0"
        }
        encoded_data = None
        if data is not None:
            headers["Content-Type"] = "application/x-www-form-urlencoded"
            encoded_data = urllib.parse.urlencode(data).encode("utf-8")
        req = urllib.request.Request(url, data=encoded_data, headers=headers, method=method)
        try:
            with urllib.request.urlopen(req, timeout=15) as response:
                res_body = response.read().decode("utf-8")
                return {
                    "success": True,
                    "status_code": response.status,
                    "data": json.loads(res_body)
                }
        except urllib.error.HTTPError as e:
            err_body = e.read().decode("utf-8")
            try:
                err_json = json.loads(err_body)
                error_message = err_json.get("error", {}).get("message", err_body)
            except Exception:
                error_message = err_body
            return {
                "success": False,
                "status_code": e.code,
                "error": error_message
            }
        except Exception as e:
            return {
                "success": False,
                "status_code": 500,
                "error": str(e)
            }

    @classmethod
    def test_stripe_credentials(cls, secret_key):
        if not secret_key or not secret_key.strip():
            return {"success": False, "error": "Chiave segreta Stripe non configurata"}
        res = cls._make_stripe_request("balance", secret_key, method="GET")
        if res["success"]:
            return {
                "success": True,
                "livemode": res["data"].get("livemode", False),
                "message": "Credenziali Stripe convalidate con successo"
            }
        return res

    @classmethod
    def create_payment_intent(cls, secret_key, amount_cents, currency="eur", metadata=None, description=None):
        if not secret_key or not secret_key.strip():
            return {"success": False, "error": "Chiave segreta Stripe mancante"}
        post_data = {
            "amount": int(amount_cents),
            "currency": currency.lower(),
            "automatic_payment_methods[enabled]": "true",
        }
        if description:
            post_data["description"] = description
        if metadata and isinstance(metadata, dict):
            for k, v in metadata.items():
                post_data[f"metadata[{k}]"] = str(v)
        return cls._make_stripe_request("payment_intents", secret_key, data=post_data, method="POST")

    @classmethod
    def retrieve_payment_intent(cls, secret_key, payment_intent_id):
        if not secret_key or not secret_key.strip():
            return {"success": False, "error": "Chiave segreta Stripe mancante"}
        return cls._make_stripe_request(f"payment_intents/{payment_intent_id}", secret_key, method="GET")

    @classmethod
    def verify_webhook_signature(cls, payload_bytes, sig_header, webhook_secret, tolerance=300):
        if not webhook_secret or not sig_header:
            return False, "Webhook secret o header firma mancante"
        pairs = dict(item.strip().split("=", 1) for item in sig_header.split(",") if "=" in item)
        timestamp = pairs.get("t")
        v1_signature = pairs.get("v1")
        if not timestamp or not v1_signature:
            return False, "Header Stripe-Signature non valido o incompleto"
        if tolerance > 0:
            current_ts = int(time.time())
            try:
                event_ts = int(timestamp)
                if abs(current_ts - event_ts) > tolerance:
                    return False, f"Timestamp firma scaduto o non sincronizzato ({abs(current_ts - event_ts)}s > {tolerance}s)"
            except ValueError:
                return False, "Timestamp firma non numerico"
        signed_payload = f"{timestamp}.".encode("utf-8") + payload_bytes
        expected_signature = hmac.new(
            webhook_secret.encode("utf-8"),
            signed_payload,
            hashlib.sha256
        ).hexdigest()
        if hmac.compare_digest(expected_signature, v1_signature):
            return True, "Firma valida"
        return False, "Firma non corrispondente"

# Assegna il modulo helper Stripe autonomo
stripe_helper = InternalStripeHelper


class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        import datetime
        if isinstance(obj, datetime.datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, datetime.date):
            return obj.strftime('%Y-%m-%d')
        return super().default(obj)

PORT = 8000
DIRECTORY = "public"

# Impostazioni globali per il Sync Ibrido
RUN_MODE = "local"
REMOTE_URL = "http://31.14.139.127:8082"
SYNC_TOKEN = "YroFFngDygKHhPMFWKkB"
MENU_CHANGED = True
SETTINGS_CHANGED = True

# Identificativo unico di questo server per prevenire la sincronizzazione concorrente da PC diversi
SERVER_ID = None
SERVER_ID_FILE = ".server_id"

def get_server_id():
    global SERVER_ID
    if SERVER_ID:
        return SERVER_ID
    if os.path.exists(SERVER_ID_FILE):
        try:
            with open(SERVER_ID_FILE, "r", encoding="utf-8") as f:
                SERVER_ID = f.read().strip()
                if SERVER_ID:
                    return SERVER_ID
        except Exception:
            pass
    import socket
    import secrets
    import re
    try:
        hostname = socket.gethostname()
    except Exception:
        hostname = "unknown-host"
    # Mantieni solo caratteri alfanumerici e trattini
    hostname = re.sub(r'[^a-zA-Z0-9\-]', '', hostname)
    SERVER_ID = f"{hostname}-{secrets.token_hex(2)}"
    try:
        with open(SERVER_ID_FILE, "w", encoding="utf-8") as f:
            f.write(SERVER_ID)
    except Exception as e:
        print(f"[Config] Impossibile salvare .server_id: {e}")
    return SERVER_ID

def get_app_version_info():
    version = "latest"
    build_date = ""
    description = ""
    if os.path.exists("version.txt"):
        try:
            with open("version.txt", "r", encoding="utf-8") as f:
                for line in f:
                    if line.startswith("Sagra Version:"):
                        version = line.split(":", 1)[1].strip()
                    elif line.startswith("Build Date:"):
                        build_date = line.split(":", 1)[1].strip()
                    elif line.startswith("Description:"):
                        description = line.split(":", 1)[1].strip()
        except Exception:
            pass
    return {
        "version": version,
        "build_date": build_date,
        "description": description
    }

# Stato di conflitto sync del server locale
SYNC_CONFLICT_ACTIVE = False
SYNC_CONFLICT_DETAILS = ""
FORCE_SYNC_ONCE = False

# Registrazione dei client locali attivi per prevenire la sincronizzazione da più PC concorrenti
# Struttura: { sync_token: { "server_id": id, "timestamp": epoch_time } }
ACTIVE_SYNC_CLIENTS = {}
SYNC_CONFLICT_TIMEOUT = 45  # 45 secondi di tolleranza prima di consentire un cambio di server

def check_sync_conflict(handler, sync_token):
    global ACTIVE_SYNC_CLIENTS
    
    # Controlla se è stata inviata una richiesta di override forzato (Forced Takeover)
    force_takeover = handler.headers.get('X-Force-Sync') == 'true'
    
    server_id = handler.headers.get('X-Server-ID')
    if not server_id:
        # Se manca l'header, per retrocompatibilità non blocchiamo
        return False
        
    current_time = time.time()
    active_client = ACTIVE_SYNC_CLIENTS.get(sync_token)
    
    if active_client and not force_takeover:
        if active_client["server_id"] != server_id:
            elapsed = current_time - active_client["timestamp"]
            if elapsed < SYNC_CONFLICT_TIMEOUT:
                # Conflitto! Un altro server locale è ancora attivo
                return True
                
    # Nessun conflitto, timeout scaduto o override forzato: aggiorna il client attivo
    if force_takeover:
        print(f"[Cloud Sync] Override forzato registrato per server_id: {server_id}")
        
    ACTIVE_SYNC_CLIENTS[sync_token] = {
        "server_id": server_id,
        "timestamp": current_time
    }
    return False

def fetch_and_sync_remote_order_on_demand(pin_or_code):
    """
    Attempts to fetch a single order from Remote Cloud on-demand if missing locally.
    Returns the locally inserted/updated order, or None.
    """
    if RUN_MODE != "local" or not REMOTE_URL or not pin_or_code:
        return None
    try:
        import urllib.parse
        import urllib.request
        import ssl
        ssl_context = ssl._create_unverified_context()
        clean_param = urllib.parse.quote(str(pin_or_code).strip())
        fetch_url = f"{REMOTE_URL}/api/sync/fetch-single?pin={clean_param}"
        req = urllib.request.Request(fetch_url)
        req.add_header('X-Sync-Token', SYNC_TOKEN)
        req.add_header('X-Server-ID', get_server_id())
        with urllib.request.urlopen(req, timeout=4, context=ssl_context) as resp:
            if resp.status == 200:
                ro = json.loads(resp.read().decode('utf-8'))
                if ro and ro.get('id'):
                    print(f"[Totem On-Demand] Ordine remoto #{ro.get('id')} (PIN {ro.get('pickup_pin')}) trovato su Cloud. Inserimento locale immediato...")
                    local_order = db.insert_synced_order(
                        remote_id=ro['id'],
                        order_number=ro['order_number'],
                        customer_name=ro['customer_name'],
                        customer_phone=ro['customer_phone'],
                        table_number=ro['table_number'],
                        total_price=ro['total_price'],
                        status=ro['status'],
                        created_at=ro['created_at'],
                        items=ro['items'],
                        pickup_pin=ro.get('pickup_pin'),
                        is_printed=ro.get('is_printed', 0),
                        printed_at=ro.get('printed_at')
                    )
                    # Invia conferma asincrona al cloud in background thread
                    def send_confirm(rem_id, loc_num):
                        try:
                            c_url = f"{REMOTE_URL}/api/sync/confirm"
                            c_data = json.dumps([{"remote_id": rem_id, "local_order_number": loc_num}]).encode('utf-8')
                            c_req = urllib.request.Request(c_url, data=c_data, method='POST')
                            c_req.add_header('Content-Type', 'application/json')
                            c_req.add_header('X-Sync-Token', SYNC_TOKEN)
                            c_req.add_header('X-Server-ID', get_server_id())
                            with urllib.request.urlopen(c_req, timeout=5, context=ssl_context) as _:
                                pass
                        except Exception as e:
                            print(f"[Totem On-Demand] Errore conferma sync: {e}")
                    threading.Thread(target=send_confirm, args=(ro['id'], local_order['order_number']), daemon=True).start()
                    return db.get_totem_order(str(pin_or_code)) or local_order
    except Exception as e:
        print(f"[Totem On-Demand] Nessun ordine trovato o errore verso {REMOTE_URL}: {e}")
    return None

class MyTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True
    daemon_threads = True  # I thread figli terminano con il processo principale

class MyHandler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        # Ensure we serve files from the public directory
        super().__init__(*args, directory=DIRECTORY, **kwargs)

    def get_authenticated_user(self):
        token = self.headers.get('X-Auth-Token')
        if not token:
            return None
        return db.get_user_by_token(token)

    def track_connection(self, path):
        client_ip = self.client_address[0]
        user_agent = self.headers.get('User-Agent', 'Sconosciuto')
        
        # Ignora i test automatici o chiamate da terminale (curl, wget)
        if 'curl/' in user_agent or 'wget' in user_agent.lower():
            return
            
        # Inferisci il ruolo dal path API chiamato (escludendo file statici)
        if not path.startswith('/api/'):
            return
            
        role = "Sconosciuto"
        if path.startswith('/api/kitchen'):
            role = "Cucina"
        elif path.startswith('/api/orders/pending') or path.startswith('/api/cashier') or path.startswith('/api/checkout'):
            role = "Cassa"
        elif path.startswith('/api/public/display') or path.startswith('/api/monitors'):
            role = "Tabellone Ordini"
        elif path.startswith('/api/customer') or path.startswith('/api/orders/public'):
            role = "Cliente Web"
        elif path.startswith('/api/admin') or path.startswith('/api/settings'):
            role = "Amministratore"
        elif path.startswith('/api/sync'):
            role = "Server Locale (Sync)"
            
        conn_id = f"{client_ip}|{user_agent}"
        
        user = self.get_authenticated_user()
        username = user['username'] if user else "Ospite"
        
        ACTIVE_CONNECTIONS[conn_id] = {
            "ip": client_ip,
            "user_agent": user_agent,
            "role": role,
            "username": username,
            "last_seen": time.time(),
            "last_path": path
        }


    def end_headers(self):
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        # Disabilita la cache del browser per garantire che tutti i dispositivi
        # (cassa, cucina, clienti) ricevano sempre la versione aggiornata dei file
        self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
        self.send_header('Pragma', 'no-cache')
        self.send_header('Expires', '0')
        super().end_headers()

    def do_OPTIONS(self):
        self.send_response(200)
        self.end_headers()


    def format_orders(self, orders):
        import db
        if not orders: return orders
        if isinstance(orders, dict):
            if 'order_number' in orders:
                is_web = db.DB_TYPE == 'mysql' or orders.get('remote_id') is not None
                orders['display_number'] = f"{orders['order_number']}W" if is_web else str(orders['order_number'])
            # for get_order_status
            if 'items' in orders and isinstance(orders['items'], list):
                for item in orders['items']:
                    if 'order_number' in item:
                        item['display_number'] = orders.get('display_number', str(item['order_number']))
            return orders
        elif isinstance(orders, list):
            for o in orders:
                if isinstance(o, dict) and 'order_number' in o:
                    is_web = db.DB_TYPE == 'mysql' or o.get('remote_id') is not None
                    o['display_number'] = f"{o['order_number']}W" if is_web else str(o['order_number'])
            return orders
        return orders

    def do_GET(self):
        parsed_path = urllib.parse.urlparse(self.path)
        path = parsed_path.path
        query = urllib.parse.parse_qs(parsed_path.query)
        
        parts = [p for p in path.split("/") if p]
        
        self.track_connection(path)

        
        # Route API requests
        if len(parts) > 0 and parts[0] == 'api':
            self.handle_api_get(parts, query)
        else:
            # Fallback to serving static files
            if path == "/" or path == "":
                self.path = "/dashboard.html"
            super().do_GET()

    def do_POST(self):
        parsed_path = urllib.parse.urlparse(self.path)
        path = parsed_path.path
        parts = [p for p in path.split("/") if p]
        
        self.track_connection(path)

        
        if len(parts) > 0 and parts[0] == 'api':
            self.handle_api_post(parts)
        else:
            self.send_error(404, "Not Found")

    def do_PUT(self):
        parsed_path = urllib.parse.urlparse(self.path)
        path = parsed_path.path
        query = urllib.parse.parse_qs(parsed_path.query)
        parts = [p for p in path.split("/") if p]
        
        if len(parts) > 0 and parts[0] == 'api':
            self.handle_api_put(parts, query)
        else:
            self.send_error(404, "Not Found")

    def do_DELETE(self):
        parsed_path = urllib.parse.urlparse(self.path)
        path = parsed_path.path
        parts = [p for p in path.split("/") if p]
        
        if len(parts) > 0 and parts[0] == 'api':
            self.handle_api_delete(parts)
        else:
            self.send_error(404, "Not Found")

    # API GET Request handlers
    def handle_api_get(self, parts, query):
        try:
            # GET /api/categories
            if parts == ['api', 'categories']:
                try:
                    if not os.path.exists("categories.json"):
                        default_categories = [
                            {"id": "PIZZERIA", "name": "Pizzeria", "emoji": "🍕"},
                            {"id": "GRIGLIA", "name": "Griglia", "emoji": "🥩"},
                            {"id": "CUCINA", "name": "Cucina Calda", "emoji": "🍝"},
                            {"id": "BEVANDE", "name": "Bar / Bevande", "emoji": "🥤", "no_kitchen": True},
                            {"id": "DOLCI", "name": "Dolci", "emoji": "🍰"}
                        ]
                        with open("categories.json", "w", encoding="utf-8") as f:
                            json.dump(default_categories, f, indent=4, ensure_ascii=False)
                    with open("categories.json", "r", encoding="utf-8") as f:
                        cats = json.load(f)
                    
                    # Calcola is_closed in base all'ora corrente locale del server
                    import datetime
                    now_str = datetime.datetime.now().strftime("%H:%M")
                    for cat in cats:
                        is_manual_closed = cat.get("is_closed_manual", False) or cat.get("is_closed", False)
                        closing = cat.get("closing_time")
                        if closing:
                            if closing >= "12:00":
                                cat["is_closed"] = is_manual_closed or now_str >= closing or now_str < "06:00"
                            else:
                                cat["is_closed"] = is_manual_closed or (now_str >= closing and now_str < "06:00")
                        else:
                            cat["is_closed"] = is_manual_closed
                            
                    self.send_json_response(200, cats)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/menu
            elif parts == ['api', 'menu']:
                include_unavailable = query.get('include_unavailable', ['true'])[0].lower() == 'true'
                menu = db.get_menu(include_unavailable)
                self.send_json_response(200, menu)
                return

            # GET /api/me
            elif parts == ['api', 'me']:
                user = self.get_authenticated_user()
                if user:
                    self.send_json_response(200, user)
                else:
                    self.send_json_response(401, {"error": "Sessione non valida"})
                return

            # GET /api/admin/users
            elif parts == ['api', 'admin', 'users']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                users = db.get_all_users()
                self.send_json_response(200, users)
                return

            # GET /api/orders/pending
            elif parts == ['api', 'orders', 'pending']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                orders = self.format_orders(db.get_pending_orders())
                self.send_json_response(200, orders)
                return

            # GET /api/kitchen/items
            elif parts == ['api', 'kitchen', 'items']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                category = query.get('category', [None])[0]
                if user['role'] == 'CUCINA':
                    category = user['sector'] # override! force to user's assigned sector
                items = self.format_orders(db.get_kitchen_items(category))
                self.send_json_response(200, items)
                return

            # GET /api/kitchen/hold
            elif parts == ['api', 'kitchen', 'hold']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                category = query.get('category', [None])[0]
                if user['role'] == 'CUCINA':
                    category = user['sector']
                orders = self.format_orders(db.get_hold_orders(category))
                self.send_json_response(200, orders)
                return

            # GET /api/public/display-orders
            elif parts == ['api', 'public', 'display-orders']:
                category = query.get('category', [None])[0]
                pending_mode = "hide"
                if os.path.exists("settings.json"):
                    with open("settings.json", "r", encoding="utf-8") as f:
                        try:
                            sett = json.load(f)
                            pending_mode = sett.get("public_display_pending_mode", "hide")
                        except:
                            pass
                display_data = db.get_public_display_orders(category, pending_mode=pending_mode)
                self.send_json_response(200, display_data)
                return

            # GET /api/public/monitor-config
            elif parts == ['api', 'public', 'monitor-config']:
                code = query.get('code', [''])[0].upper()
                if not code:
                    self.send_json_response(400, {"error": "Manca il codice"})
                    return
                
                # Check or register
                now = time.time()
                if code not in MONITORS:
                    MONITORS[code] = {"status": "pending", "last_seen": now, "config": None, "name": f"TV-{code}"}
                
                # Update last seen
                MONITORS[code]["last_seen"] = now
                
                self.send_json_response(200, MONITORS[code])
                return

            # GET /api/monitors (Admin only)
            elif parts == ['api', 'monitors']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                
                # Cleanup dead pending monitors (> 1 hour unseen)
                now = time.time()
                dead = [c for c, m in MONITORS.items() if m.get("status") == "pending" and now - m.get("last_seen", 0) > 3600]
                if dead:
                    for c in dead:
                        del MONITORS[c]
                    save_monitors()
                    
                self.send_json_response(200, MONITORS)
                return

            # GET /api/orders/ready-pickup
            elif parts == ['api', 'orders', 'ready-pickup']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                orders = self.format_orders(db.get_ready_orders())
                self.send_json_response(200, orders)
                return

            # GET /api/orders/delivered
            elif parts == ['api', 'orders', 'delivered']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                orders = self.format_orders(db.get_delivered_orders())
                self.send_json_response(200, orders)
                return

            # GET /api/orders/cashier-history
            elif parts == ['api', 'orders', 'cashier-history']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                orders = self.format_orders(db.get_cashier_history())
                self.send_json_response(200, orders)
                return

            # GET /api/orders/<id>/status
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'status':
                try:
                    order_id = int(parts[2])
                    order_status = self.format_orders(db.get_order_status(order_id))
                    if order_status:
                        self.send_json_response(200, order_status)
                    else:
                        self.send_json_response(404, {"error": "Order not found"})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # GET /api/totem/order/<code_or_number>
            elif len(parts) == 4 and parts[1] == 'totem' and parts[2] == 'order':
                raw_code = parts[3].strip()
                totem_order = db.get_totem_order(raw_code)
                if not totem_order:
                    # Fallback On-Demand: Interroga subito il Cloud se siamo in locale
                    totem_order = fetch_and_sync_remote_order_on_demand(raw_code)
                if totem_order:
                    self.send_json_response(200, self.format_orders(totem_order))
                else:
                    self.send_json_response(404, {"error": "Order not found"})
                return

            # GET /api/settings
            elif parts == ['api', 'settings']:
                try:
                    if not os.path.exists("settings.json"):
                        default_settings = {
                            "whatsapp_sim": "+39 328 123 4567",
                            "whatsapp_name": "Estate Bedulitese"
                        }
                        with open("settings.json", "w", encoding="utf-8") as f:
                            json.dump(default_settings, f, indent=4, ensure_ascii=False)
                    with open("settings.json", "r", encoding="utf-8") as f:
                        settings = json.load(f)
                    
                    # Filtering settings based on role for security/permissions
                    user = self.get_authenticated_user()
                    if not user:
                        # Public customer request - expose only public details
                        settings = {
                            "whatsapp_sim": settings.get("whatsapp_sim", ""),
                            "whatsapp_name": settings.get("whatsapp_name", ""),
                            "wifi_ssid": settings.get("wifi_ssid", ""),
                            "wifi_password": settings.get("wifi_password", ""),
                            "menu_url": settings.get("menu_url", ""),
                            "auto_print_enabled": settings.get("auto_print_enabled", True),
                            "kitchen_ui_mode": settings.get("kitchen_ui_mode", "classic"),
                            "sagra_active": settings.get("sagra_active", True),
                            "print_delay_before": settings.get("print_delay_before", 300),
                            "print_delay_after": settings.get("print_delay_after", 800),
                            "print_sequential": settings.get("print_sequential", False),
                            "payment_mode": settings.get("payment_mode", "SANDBOX"),
                            "enable_pay_at_cashier": settings.get("enable_pay_at_cashier", True),
                            "enable_stripe": settings.get("enable_stripe", True),
                            "enable_satispay": settings.get("enable_satispay", True),
                            "stripe_publishable_key": settings.get("stripe_publishable_key", "")
                        }
                    else:
                        # Garantiamo il caricamento dei default anche per gli amministratori
                        settings["print_delay_before"] = settings.get("print_delay_before", 300)
                        settings["print_delay_after"] = settings.get("print_delay_after", 800)
                        settings["print_sequential"] = settings.get("print_sequential", False)
                        settings["payment_mode"] = settings.get("payment_mode", "SANDBOX")
                        settings["enable_pay_at_cashier"] = settings.get("enable_pay_at_cashier", True)
                        settings["enable_stripe"] = settings.get("enable_stripe", True)
                        settings["enable_satispay"] = settings.get("enable_satispay", True)
                        settings["stripe_publishable_key"] = settings.get("stripe_publishable_key", "")

                    if user and user['role'] == 'ADMIN':
                        # Junior Admin - strip DB and sync settings fields
                        keys_to_remove = [
                            "run_mode", "db_type", "db_file", "db_host", "db_port", "db_user", "db_password", "db_name",
                            "sync_token", "sync_target", "synology_remote_url", "synology_sync_token", "synology_port",
                            "synology_db_host", "synology_db_port", "synology_db_user", "synology_db_password", "synology_db_name",
                            "aruba_remote_url", "aruba_sync_token", "aruba_port", "aruba_db_host", "aruba_db_port", 
                            "aruba_db_user", "aruba_db_password", "aruba_db_name", "local_port", "remote_url",
                            "stripe_secret_key", "stripe_webhook_secret", "satispay_token"
                        ]
                        for key in keys_to_remove:
                            settings.pop(key, None)
                    # SUPER_ADMIN gets the full settings list
                    
                    self.send_json_response(200, settings)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/payments/config
            elif parts == ['api', 'payments', 'config']:
                s_data = {}
                if os.path.exists("settings.json"):
                    try:
                        with open("settings.json", "r", encoding="utf-8") as f:
                            s_data = json.load(f)
                    except Exception:
                        pass
                self.send_json_response(200, {
                    "payment_mode": s_data.get("payment_mode", "SANDBOX"),
                    "enable_pay_at_cashier": s_data.get("enable_pay_at_cashier", True),
                    "enable_stripe": s_data.get("enable_stripe", True),
                    "enable_satispay": s_data.get("enable_satispay", True),
                    "stripe_publishable_key": s_data.get("stripe_publishable_key", "")
                })
                return

            # GET /api/server-info
            elif parts == ['api', 'server-info']:
                try:
                    import socket
                    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                    try:
                        s.connect(('10.255.255.255', 1))
                        ip = s.getsockname()[0]
                    except Exception:
                        ip = '127.0.0.1'
                    finally:
                        s.close()

                    version_info = get_app_version_info()
                    remote_info = None

                    # Se siamo in locale e abbiamo configurato un server remoto, interroghiamo il server remoto
                    if RUN_MODE == "local" and REMOTE_URL and query.get('check_remote', ['1'])[0] != '0':
                        try:
                            ssl_context = ssl._create_unverified_context()
                            # check_remote=0 per evitare loop
                            req = urllib.request.Request(f"{REMOTE_URL}/api/server-info?check_remote=0")
                            req.add_header('X-Sync-Token', SYNC_TOKEN)
                            with urllib.request.urlopen(req, timeout=3, context=ssl_context) as resp:
                                if resp.status == 200:
                                    remote_info = json.loads(resp.read().decode('utf-8'))
                        except Exception as e:
                            remote_info = {"error": f"Server Cloud non raggiungibile: {str(e)}"}

                    self.send_json_response(200, {
                        "ip": ip,
                        "port": PORT,
                        "sync_warning": SYNC_CONFLICT_DETAILS if SYNC_CONFLICT_ACTIVE else None,
                        "run_mode": RUN_MODE,
                        "has_remote": bool(REMOTE_URL),
                        "remote_url": REMOTE_URL if RUN_MODE == "local" else None,
                        "app_version": version_info["version"],
                        "build_date": version_info["build_date"],
                        "description": version_info["description"],
                        "remote_server_info": remote_info
                    })
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/stats
            elif parts == ['api', 'stats']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    stats = db.get_sagra_stats()
                    self.send_json_response(200, stats)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/sync/pull
            elif parts == ['api', 'sync', 'pull']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                if check_sync_conflict(self, sync_t):
                    self.send_json_response(409, {"error": "Duplicate server detected. Another instance is active."})
                    return
                orders = db.get_unpulled_remote_orders()
                self.send_json_response(200, orders)
                return

            # GET /api/sync/fetch-single
            elif parts == ['api', 'sync', 'fetch-single']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                pin = query.get('pin', [''])[0].strip() or query.get('code', [''])[0].strip()
                if not pin:
                    self.send_json_response(400, {"error": "Parametro pin mancante"})
                    return
                order = db.get_remote_order_by_pin(pin)
                if not order:
                    self.send_json_response(404, {"error": "Order not found on remote"})
                    return
                self.send_json_response(200, order)
                return

            # GET /api/sync/pull-menu-config
            elif parts == ['api', 'sync', 'pull-menu-config']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                try:
                    config = db.get_menu_config()
                    self.send_json_response(200, config)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/ingredients
            elif parts == ['api', 'ingredients']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    ingredients = db.get_ingredients()
                    self.send_json_response(200, ingredients)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/ingredients/purchases
            elif parts == ['api', 'ingredients', 'purchases']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    purchases = db.get_all_ingredient_purchases()
                    self.send_json_response(200, purchases)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/ingredients/purchases/total
            elif parts == ['api', 'ingredients', 'purchases', 'total']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    total = db.get_total_purchases_sum()
                    self.send_json_response(200, {"total_purchases_sum": total})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return


            # GET /api/foodcost/simulated-sales
            elif parts == ['api', 'foodcost', 'simulated-sales']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    sales = db.get_simulated_sales()
                    self.send_json_response(200, sales)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/ingredients/<id>/purchases
            elif len(parts) == 4 and parts[1] == 'ingredients' and parts[3] == 'purchases':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    ing_id = int(parts[2])
                    purchases = db.get_ingredient_purchases(ing_id)
                    self.send_json_response(200, purchases)
                except ValueError:
                    self.send_json_response(400, {"error": "ID ingrediente non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/menu/<id>/recipe
            elif len(parts) == 4 and parts[1] == 'menu' and parts[3] == 'recipe':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    item_id = int(parts[2])
                    recipe = db.get_recipe(item_id)
                    self.send_json_response(200, recipe)
                except ValueError:
                    self.send_json_response(400, {"error": "ID prodotto non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/sync/backup/download
            elif parts == ['api', 'sync', 'backup', 'download']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                try:
                    if not os.path.exists("cloud_backup.zip"):
                        self.send_json_response(404, {"error": "Nessun backup trovato sul server cloud"})
                        return
                    with open("cloud_backup.zip", "rb") as f:
                        zip_bytes = f.read()
                    self.send_response(200)
                    self.send_header('Content-Type', 'application/zip')
                    self.send_header('Content-Disposition', 'attachment; filename=cloud_backup.zip')
                    self.send_header('Content-Length', str(len(zip_bytes)))
                    self.end_headers()
                    self.wfile.write(zip_bytes)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/releases
            elif parts == ['api', 'releases']:
                # Se siamo in locale, proviamo prima a recuperare le release dal server remoto
                if RUN_MODE == "local" and REMOTE_URL:
                    try:
                        ssl_context = ssl._create_unverified_context()
                        req = urllib.request.Request(f"{REMOTE_URL}/api/releases")
                        req.add_header('X-Sync-Token', SYNC_TOKEN)
                        with urllib.request.urlopen(req, timeout=5, context=ssl_context) as response:
                            if response.status == 200:
                                remote_releases = json.loads(response.read().decode('utf-8'))
                                self.send_json_response(200, remote_releases)
                                return
                    except Exception as e:
                        print(f"[Releases] Impossibile recuperare release da remoto ({e}), utilizzo locale.")

                releases_path = os.path.join("build", "releases", "releases.json")
                if os.path.exists(releases_path):
                    try:
                        with open(releases_path, "r", encoding="utf-8") as f:
                            data = json.load(f)
                        self.send_json_response(200, data)
                        return
                    except Exception as e:
                        pass
                
                # Fallback se releases.json non esiste ancora
                fallback_data = {
                    "latest_version": "latest",
                    "updated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "releases": []
                }
                latest_zip = os.path.join("build", "Sagra_Portabile.zip")
                if os.path.exists(latest_zip):
                    size_mb = os.path.getsize(latest_zip) / (1024 * 1024)
                    fallback_data["releases"].append({
                        "version": "latest",
                        "filename": "Sagra_Portabile.zip",
                        "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                        "description": "Canale Latest (Rolling continuo)",
                        "size_mb": round(size_mb, 2)
                    })
                self.send_json_response(200, fallback_data)
                return

            # GET /api/update/download
            elif parts == ['api', 'update', 'download']:
                try:
                    req_version = query.get('version', ['latest'])[0]
                    if not req_version or req_version.lower() == 'latest':
                        zip_path = os.path.join("build", "Sagra_Portabile.zip")
                        filename = "Sagra_Portabile.zip"
                    else:
                        clean_ver = re.sub(r'[^a-zA-Z0-9._-]', '', req_version)
                        zip_path = os.path.join("build", "releases", f"Sagra_{clean_ver}.zip")
                        filename = f"Sagra_{clean_ver}.zip"

                    if os.path.exists(zip_path):
                        with open(zip_path, "rb") as f:
                            zip_bytes = f.read()
                    else:
                        if not req_version or req_version.lower() == 'latest':
                            zip_bytes = make_zip_archive()
                            filename = "Sagra.zip"
                        else:
                            self.send_json_response(404, {"error": f"Release '{req_version}' non trovata sul server"})
                            return
                        
                    self.send_response(200)
                    self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
                    self.send_header('Pragma', 'no-cache')
                    self.send_header('Expires', '0')
                    self.send_header('Content-Type', 'application/zip')
                    self.send_header('Content-Disposition', f'attachment; filename={filename}')
                    self.send_header('Content-Length', str(len(zip_bytes)))
                    self.end_headers()
                    self.wfile.write(zip_bytes)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/admin/backup/export
            elif parts == ['api', 'admin', 'backup', 'export']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    import io
                    import zipfile
                    # Esegui il checkpoint del database prima di esportarlo
                    db.checkpoint_db()
                    
                    zip_buffer = io.BytesIO()
                    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
                        if os.path.exists("settings.json"):
                            zip_file.write("settings.json", "settings.json")
                        if os.path.exists("categories.json"):
                            zip_file.write("categories.json", "categories.json")
                        if db.DB_TYPE == "sqlite" and db.DB_FILE and os.path.exists(db.DB_FILE):
                            zip_file.write(db.DB_FILE, os.path.basename(db.DB_FILE))
                            
                    zip_bytes = zip_buffer.getvalue()
                    self.send_response(200)
                    self.send_header('Content-Type', 'application/zip')
                    self.send_header('Content-Disposition', 'attachment; filename=sagra_backup.zip')
                    self.send_header('Content-Length', str(len(zip_bytes)))
                    self.end_headers()
                    self.wfile.write(zip_bytes)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # GET /api/checkout
            elif parts == ['api', 'checkout']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                date_val = query.get('date', [None])[0]
                if not date_val:
                    self.send_json_response(400, {"error": "Parametro data mancante"})
                    return
                checkout = db.get_daily_checkout(date_val)
                if checkout:
                    self.send_json_response(200, checkout)
                else:
                    self.send_json_response(404, {"error": "Nessun resoconto per questa data"})
                return

            # GET /api/checkout/summary
            elif parts == ['api', 'checkout', 'summary']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                summary = db.get_manifestation_summary()
                self.send_json_response(200, summary)
                return

            # GET /api/checkout/expenses
            elif parts == ['api', 'checkout', 'expenses']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                expenses = db.get_all_checkout_expenses()
                self.send_json_response(200, expenses)
                return
            # GET /api/reports/sales
            elif parts == ['api', 'reports', 'sales']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                start_date = query.get('start_date', [None])[0]
                end_date = query.get('end_date', [None])[0]
                category = query.get('category', [None])[0]
                
                sales_data = db.get_sales_analytics(start_date, end_date, category)
                self.send_json_response(200, sales_data)
                return

            # GET /api/reports/web-adoption
            elif parts == ['api', 'reports', 'web-adoption']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                start_date = query.get('start_date', [None])[0]
                end_date = query.get('end_date', [None])[0]
                
                web_data = db.get_web_adoption_analytics(start_date, end_date)
                self.send_json_response(200, web_data)
                return

            # GET /api/admin/web-orders-monitor
            elif parts == ['api', 'admin', 'web-orders-monitor']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                orders = db.get_web_orders_history(limit=200)
                self.send_json_response(200, orders)
                return

            # GET /api/foodcost/reports/sales
            elif parts == ['api', 'foodcost', 'reports', 'sales']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                start_date = query.get('start_date', [None])[0]
                end_date = query.get('end_date', [None])[0]
                category = query.get('category', [None])[0]
                
                sales_data = db.get_foodcost_sales_analytics(start_date, end_date, category)
                self.send_json_response(200, sales_data)
                return

            # GET /api/foodcost/checkout-expenses
            elif parts == ['api', 'foodcost', 'checkout-expenses']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                expenses = db.get_foodcost_checkout_expenses()
                self.send_json_response(200, expenses)
                return

            # GET /api/foodcost/checkout-summary
            elif parts == ['api', 'foodcost', 'checkout-summary']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                summary = db.get_foodcost_checkout_summaries()
                self.send_json_response(200, summary)
                return

            # Default
            self.send_json_response(404, {"error": "API Route Not Found"})

        except Exception as e:
            import traceback
            traceback.print_exc()
            self.send_json_response(500, {"error": str(e)})

    # API POST Request handlers
    def handle_api_post(self, parts):
        global MENU_CHANGED, SETTINGS_CHANGED
        import time
        import datetime
        try:
            # POST /api/login
            if parts == ['api', 'login']:
                body = self.read_json_body()
                username = body.get('username')
                password = body.get('password')
                if not username or not password:
                    self.send_json_response(400, {"error": "Username e password obbligatori"})
                    return
                user = db.validate_login(username.strip(), password)
                if user:
                    self.send_json_response(200, {"success": True, "token": user['token'], "user": {
                        "id": user['id'],
                        "username": user['username'],
                        "role": user['role'],
                        "sector": user['sector']
                    }})
                else:
                    self.send_json_response(401, {"error": "Credenziali non valide"})
                return

            # POST /api/logout
            elif parts == ['api', 'logout']:
                token = self.headers.get('X-Auth-Token')
                if token:
                    db.logout(token)
                self.send_json_response(200, {"success": True})
                return

            # POST /api/ingredients
            elif parts == ['api', 'ingredients']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    body = self.read_json_body()
                    name = body.get('name', '').strip()
                    unit = body.get('unit', '').strip()
                    package_size = float(body.get('package_size', 0.0))
                    package_price = float(body.get('package_price', 0.0))
                    initial_qty = float(body.get('initial_quantity', 1.0))
                    supplier = body.get('supplier', '').strip()
                    doc_number = body.get('doc_number', '').strip()
                    payment_status = body.get('payment_status', 'PAGATO').strip()
                    payment_date = body.get('payment_date', '').strip()
                    
                    iva = float(body.get('iva', 10.0))
                    
                    if not name or not unit or package_size <= 0 or package_price <= 0 or initial_qty < 0:
                        self.send_json_response(400, {"error": "Campi non validi o mancanti"})
                        return
                        
                    new_id = db.add_ingredient(name, unit, package_size, package_price, initial_qty, supplier=supplier, doc_number=doc_number, payment_status=payment_status, payment_date=payment_date, iva=iva)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True, "id": new_id})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return


            # POST /api/foodcost/simulated-sales
            elif parts == ['api', 'foodcost', 'simulated-sales']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    body = self.read_json_body()
                    menu_item_id = int(body.get('menu_item_id'))
                    quantity = float(body.get('quantity', 0.0))
                    db.save_simulated_sale(menu_item_id, quantity)
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/foodcost/simulated-sales/clear
            elif parts == ['api', 'foodcost', 'simulated-sales', 'clear']:
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    db.clear_simulated_sales()
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/ingredients/<id>/purchases
            elif len(parts) == 4 and parts[1] == 'ingredients' and parts[3] == 'purchases':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    ing_id = int(parts[2])
                    body = self.read_json_body()
                    qty = float(body.get('quantity', 0.0))
                    size = float(body.get('package_size', 0.0))
                    price = float(body.get('package_price', 0.0))
                    supplier = body.get('supplier', '').strip()
                    doc_number = body.get('doc_number', '').strip()
                    payment_status = body.get('payment_status', 'PAGATO').strip()
                    payment_date = body.get('payment_date', '').strip()
                    
                    iva = float(body.get('iva', 10.0))
                    
                    if qty == 0 or size <= 0 or price == 0:
                        self.send_json_response(400, {"error": "Parametri non validi o mancanti"})
                        return
                        
                    new_id = db.add_ingredient_purchase(ing_id, qty, size, price, supplier, doc_number, payment_status, payment_date, iva=iva)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True, "id": new_id})
                except ValueError:
                    self.send_json_response(400, {"error": "ID ingrediente non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return



            # POST /api/menu/<id>/recipe
            elif len(parts) == 4 and parts[1] == 'menu' and parts[3] == 'recipe':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    item_id = int(parts[2])
                    body = self.read_json_body()
                    ingredients_list = body.get('ingredients', [])
                    
                    for ing in ingredients_list:
                        if 'ingredient_id' not in ing or 'quantity' not in ing:
                            self.send_json_response(400, {"error": "Dati ingredienti ricetta non validi"})
                            return
                            
                    db.save_recipe(item_id, ingredients_list)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID prodotto non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/admin/force-sync-takeover
            elif parts == ['api', 'admin', 'force-sync-takeover']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                global FORCE_SYNC_ONCE
                FORCE_SYNC_ONCE = True
                print("[Sync Client] Richiesta di forzatura sincronizzazione (takeover) impostata.")
                self.send_json_response(200, {"success": True, "message": "Richiesta di forzatura registrata"})
                return

            # POST /api/admin/align-foodcost-sales
            elif parts == ['api', 'admin', 'align-foodcost-sales']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                if RUN_MODE == "remote":
                    self.send_json_response(403, {"error": "Questa operazione può essere avviata solo dall'applicativo locale della cassa (in modalità LOCAL)."})
                    return
                try:
                    db.align_foodcost_sales()
                    
                    remote_msg = ""
                    if RUN_MODE == "local" and SYNC_ENABLED and REMOTE_URL:
                        try:
                            # Pack sales data
                            sales_payload = db.get_foodcost_sales_payload()
                            
                            # Send via HTTP request
                            push_url = f"{REMOTE_URL}/api/sync/push-foodcost-sales"
                            req_data = json.dumps(sales_payload).encode('utf-8')
                            
                            req = urllib.request.Request(push_url, data=req_data, method='POST')
                            req.add_header('Content-Type', 'application/json')
                            req.add_header('X-Sync-Token', SYNC_TOKEN)
                            req.add_header('X-Server-ID', get_server_id())
                            
                            ssl_context = ssl._create_unverified_context()
                            with urllib.request.urlopen(req, timeout=15, context=ssl_context) as response:
                                if response.status == 200:
                                    remote_msg = " e sincronizzate con il server remoto."
                                else:
                                    remote_msg = f" (ma la sincronizzazione remota ha risposto con codice {response.status})."
                        except Exception as sync_err:
                            remote_msg = f" (ma errore sincronizzazione remota: {str(sync_err)})."
                            
                    self.send_json_response(200, {"success": True, "message": f"Vendite reali allineate a Food Cost con successo{remote_msg}"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/categories
            elif parts == ['api', 'categories']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                try:
                    with open("categories.json", "w", encoding="utf-8") as f:
                        json.dump(body, f, indent=4, ensure_ascii=False)
                    
                    MENU_CHANGED = True
                    
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/orders
            elif parts == ['api', 'orders']:
                body = self.read_json_body()
                
                name = body.get('customer_name')
                phone = body.get('customer_phone', '')
                table = body.get('table_number', '')
                items = body.get('items', [])
                wa_contact_saved = body.get('wa_contact_saved', 0)
                
                if not name or not items:
                    self.send_json_response(400, {"error": "Mancano campi obbligatori (nome, piatti)"})
                    return
                
                user = self.get_authenticated_user()
                is_cashier = (user is not None) and (user['role'] in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE'])
                
                if not is_cashier:
                    sagra_active = True
                    if os.path.exists("settings.json"):
                        try:
                            with open("settings.json", "r", encoding="utf-8") as f:
                                s_data = json.load(f)
                                sagra_active = s_data.get("sagra_active", True)
                        except Exception:
                            pass
                    if not sagra_active:
                        self.send_json_response(403, {"error": "Le ordinazioni online sono attualmente chiuse."})
                        return

                    # Verifichiamo se ci sono piatti appartenenti a reparti chiusi
                    try:
                        cats = []
                        if os.path.exists("categories.json"):
                            with open("categories.json", "r", encoding="utf-8") as f:
                                cats = json.load(f)
                        
                        import datetime
                        now_str = datetime.datetime.now().strftime("%H:%M")
                        closed_categories = set()
                        for cat in cats:
                            is_manual_closed = cat.get("is_closed_manual", False) or cat.get("is_closed", False)
                            closing = cat.get("closing_time")
                            is_closed = False
                            if closing:
                                if closing >= "12:00":
                                    is_closed = is_manual_closed or now_str >= closing or now_str < "06:00"
                                else:
                                    is_closed = is_manual_closed or (now_str >= closing and now_str < "06:00")
                            else:
                                is_closed = is_manual_closed
                            
                            if is_closed:
                                closed_categories.add(cat["id"])
                        
                        menu_items = db.get_menu(include_unavailable=True)
                        item_category_map = {item["id"]: item["category"] for item in menu_items}
                        
                        for order_item in items:
                            item_id = order_item.get("menu_item_id")
                            item_category = item_category_map.get(item_id)
                            if item_category in closed_categories:
                                cat_obj = next((c for c in cats if c["id"] == item_category), None)
                                cat_name = cat_obj["name"] if cat_obj else item_category
                                self.send_json_response(403, {"error": f"Il reparto '{cat_name}' è attualmente chiuso. Non è possibile ordinare questi piatti."})
                    except Exception as e:
                        print(f"Errore durante validazione categorie chiuse: {e}")
                
                payment_method = body.get('payment_method') or body.get('payment_type')
                print(f"[DEBUG_PAYMENT] Received payment_method: {payment_method}, body: {body}")
                new_order = db.create_order(name, phone, table, items, wa_contact_saved, is_cashier=is_cashier)
                if payment_method in ['ONLINE', 'DIGITAL', 'CARD', 'STRIPE', 'SATISPAY']:
                    try:
                        tx_id = body.get('transaction_id') or f"online_tx_{new_order['id']}_{int(time.time())}"
                        new_order = db.confirm_online_payment(new_order['id'], payment_method, tx_id, new_order['total_price'], {"method": payment_method})
                        print(f"[DEBUG_PAYMENT] Confirmed payment for order {new_order['id']}, new status: {new_order['status']}")
                    except Exception as ve:
                        import traceback
                        print(f"Errore durante convalida pagamento online: {ve}")
                        traceback.print_exc()

                self.send_json_response(201, self.format_orders(new_order))
                return

            # POST /api/payments/create-intent
            elif parts == ['api', 'payments', 'create-intent']:
                body = self.read_json_body()
                order_id = body.get('order_id')
                gateway = (body.get('gateway') or 'STRIPE').upper()
                if not order_id:
                    self.send_json_response(400, {"error": "Manca order_id"})
                    return
                order = db.get_order_status(order_id)
                if not order:
                    self.send_json_response(404, {"error": "Ordine non trovato"})
                    return

                s_data = {}
                if os.path.exists("settings.json"):
                    try:
                        with open("settings.json", "r", encoding="utf-8") as f:
                            s_data = json.load(f)
                    except Exception:
                        pass
                mode = s_data.get("payment_mode", "SANDBOX")
                stripe_sec_key = s_data.get("stripe_secret_key", "").strip()
                stripe_pub_key = s_data.get("stripe_publishable_key", "").strip()

                # Se Stripe è configurato con secret key e non siamo in pura simulazione SANDBOX
                if gateway == 'STRIPE' and stripe_sec_key and mode != 'SANDBOX':
                    amount_cents = int(round(float(order['total_price']) * 100))
                    disp_num = order.get('display_number') or order_id
                    res = stripe_helper.create_payment_intent(
                        secret_key=stripe_sec_key,
                        amount_cents=amount_cents,
                        currency="eur",
                        metadata={"order_id": str(order_id), "display_number": str(disp_num)},
                        description=f"Ordine #{disp_num} - Sagra"
                    )
                    if res["success"]:
                        self.send_json_response(200, {
                            "mode": "STRIPE_LIVE" if mode == "LIVE" else "STRIPE_TEST",
                            "order_id": order_id,
                            "amount": order['total_price'],
                            "gateway": "STRIPE",
                            "publishable_key": stripe_pub_key,
                            "client_secret": res["data"].get("client_secret"),
                            "payment_intent_id": res["data"].get("id")
                        })
                        return
                    else:
                        print(f"[STRIPE ERROR] create_payment_intent failed: {res.get('error')}")
                        self.send_json_response(502, {
                            "error": f"Errore gateway Stripe: {res.get('error')}",
                            "details": res
                        })
                        return

                # Modalità SANDBOX o chiavi non configurate (fallback trasparente)
                mock_tx_id = f"tx_{gateway.lower()}_{order_id}_{int(time.time())}"
                self.send_json_response(200, {
                    "mode": "SANDBOX",
                    "order_id": order_id,
                    "amount": order['total_price'],
                    "gateway": gateway,
                    "publishable_key": stripe_pub_key,
                    "transaction_id": mock_tx_id,
                    "client_secret": f"secret_{mock_tx_id}",
                    "payment_intent_id": mock_tx_id
                })
                return

            # POST /api/payments/stripe/verify-payment
            elif parts == ['api', 'payments', 'stripe', 'verify-payment']:
                body = self.read_json_body()
                order_id = body.get('order_id')
                payment_intent_id = body.get('payment_intent_id')
                if not order_id or not payment_intent_id:
                    self.send_json_response(400, {"error": "Mancano order_id o payment_intent_id"})
                    return
                order = db.get_order_status(order_id)
                if not order:
                    self.send_json_response(404, {"error": "Ordine non trovato"})
                    return

                s_data = {}
                if os.path.exists("settings.json"):
                    try:
                        with open("settings.json", "r", encoding="utf-8") as f:
                            s_data = json.load(f)
                    except Exception:
                        pass
                stripe_sec_key = s_data.get("stripe_secret_key", "").strip()

                if stripe_sec_key and not str(payment_intent_id).startswith("tx_"):
                    res = stripe_helper.retrieve_payment_intent(stripe_sec_key, payment_intent_id)
                    if res["success"]:
                        intent_status = res["data"].get("status")
                        if intent_status in ["succeeded", "processing"]:
                            amount = float(res["data"].get("amount", 0)) / 100.0
                            updated_order = db.confirm_online_payment(int(order_id), 'STRIPE', payment_intent_id, amount, res["data"])
                            self.send_json_response(200, {
                                "success": True,
                                "status": intent_status,
                                "order": self.format_orders(updated_order)
                            })
                            return
                        else:
                            self.send_json_response(400, {
                                "error": f"Stato pagamento non completato: {intent_status}",
                                "intent_status": intent_status
                            })
                            return
                    else:
                        self.send_json_response(502, {"error": f"Impossibile verificare su Stripe: {res.get('error')}"})
                        return

                # Sandbox verification
                updated_order = db.confirm_online_payment(
                    int(order_id),
                    'STRIPE',
                    payment_intent_id,
                    order['total_price'],
                    {"mock": True, "timestamp": datetime.datetime.now().isoformat()}
                )
                self.send_json_response(200, {
                    "success": True,
                    "status": "succeeded",
                    "order": self.format_orders(updated_order)
                })
                return

            # POST /api/payments/stripe/test-credentials
            elif parts == ['api', 'payments', 'stripe', 'test-credentials']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                sec_key = body.get('stripe_secret_key', '').strip()
                if not sec_key:
                    s_data = {}
                    if os.path.exists("settings.json"):
                        try:
                            with open("settings.json", "r", encoding="utf-8") as f:
                                s_data = json.load(f)
                        except Exception:
                            pass
                    sec_key = s_data.get("stripe_secret_key", "").strip()
                
                res = stripe_helper.test_stripe_credentials(sec_key)
                if res["success"]:
                    self.send_json_response(200, res)
                else:
                    self.send_json_response(400, res)
                return

            # POST /api/payments/mock-complete (Sandbox simulation)
            elif parts == ['api', 'payments', 'mock-complete']:
                body = self.read_json_body()
                order_id = body.get('order_id')
                gateway = (body.get('gateway') or 'STRIPE').upper()
                tx_id = body.get('transaction_id') or f"mock_tx_{order_id}_{int(time.time())}"
                if not order_id:
                    self.send_json_response(400, {"error": "Manca order_id"})
                    return
                order = db.get_order_status(order_id)
                if not order:
                    self.send_json_response(404, {"error": "Ordine non trovato"})
                    return
                updated_order = db.confirm_online_payment(
                    order_id, 
                    gateway, 
                    tx_id, 
                    order['total_price'], 
                    {"mock": True, "timestamp": datetime.datetime.now().isoformat()}
                )
                self.send_json_response(200, self.format_orders(updated_order))
                return

            # POST /api/payments/stripe/webhook
            elif parts == ['api', 'payments', 'stripe', 'webhook']:
                try:
                    raw_body = self.read_raw_body()
                    s_data = {}
                    if os.path.exists("settings.json"):
                        try:
                            with open("settings.json", "r", encoding="utf-8") as f:
                                s_data = json.load(f)
                        except Exception:
                            pass
                    webhook_secret = s_data.get("stripe_webhook_secret", "").strip()
                    
                    # Verifica firma crittografica se configurato webhook secret
                    if webhook_secret:
                        sig_header = self.headers.get('Stripe-Signature') or self.headers.get('stripe-signature', '')
                        is_valid, msg = stripe_helper.verify_webhook_signature(raw_body, sig_header, webhook_secret)
                        if not is_valid:
                            print(f"[STRIPE WEBHOOK REJECTED] {msg}")
                            self.send_json_response(400, {"error": f"Firma non valida: {msg}"})
                            return

                    event = json.loads(raw_body.decode('utf-8'))
                    event_type = event.get('type')
                    print(f"[STRIPE WEBHOOK] Received event: {event_type}")

                    if event_type == 'payment_intent.succeeded':
                        obj = event.get('data', {}).get('object', {})
                        meta = obj.get('metadata', {})
                        order_id = meta.get('order_id')
                        trans_id = obj.get('id')
                        amount = float(obj.get('amount', 0)) / 100.0
                        if order_id and trans_id:
                            db.confirm_online_payment(int(order_id), 'STRIPE', trans_id, amount, event)
                            print(f"[STRIPE WEBHOOK] Order #{order_id} successfully confirmed via transaction {trans_id}")
                    self.send_json_response(200, {"received": True})
                except Exception as e:
                    print(f"[STRIPE WEBHOOK ERROR] {e}")
                    self.send_json_response(400, {"error": str(e)})
                return

            # POST /api/payments/satispay/callback
            elif parts == ['api', 'payments', 'satispay', 'callback']:
                try:
                    raw_body = self.read_raw_body()
                    event = json.loads(raw_body.decode('utf-8'))
                    order_id = event.get('extra_charge_id') or event.get('metadata', {}).get('order_id')
                    trans_id = event.get('id')
                    amount = float(event.get('amount_unit', 0)) / 100.0
                    if order_id and trans_id:
                        db.confirm_online_payment(int(order_id), 'SATISPAY', trans_id, amount, event)
                    self.send_json_response(200, {"received": True})
                except Exception as e:
                    self.send_json_response(400, {"error": str(e)})
                return


            # POST /api/menu/reset-stock
            elif parts == ['api', 'menu', 'reset-stock']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    db.reset_all_product_stock()
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/menu
            elif parts == ['api', 'menu']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                name = body.get('name')
                description = body.get('description', '')
                price = float(body.get('price', 0))
                category = body.get('category')
                
                stock_limit = body.get('stock_limit')
                if stock_limit is not None and stock_limit != '':
                    stock_limit = int(stock_limit)
                else:
                    stock_limit = None
                
                if not name or not category:
                    self.send_json_response(400, {"error": "Mancano campi obbligatori (nome, categoria)"})
                    return
                
                new_id = db.create_menu_item(name, description, price, category, stock_limit=stock_limit)
                
                MENU_CHANGED = True
                
                self.send_json_response(201, {"id": new_id, "success": True})
                return

            # POST /api/settings
            elif parts == ['api', 'settings']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                try:
                    # If Junior Admin, merge to prevent changing DB/sync configuration keys
                    if user['role'] == 'ADMIN':
                        existing_settings = {}
                        if os.path.exists("settings.json"):
                            with open("settings.json", "r", encoding="utf-8") as f:
                                existing_settings = json.load(f)
                        allowed_keys = ["whatsapp_sim", "whatsapp_name", "wifi_ssid", "wifi_password", "menu_url", "auto_print_enabled", "kitchen_ui_mode", "sagra_active", "print_delay_before", "print_delay_after", "print_sequential"]
                        for key in allowed_keys:
                            if key in body:
                                existing_settings[key] = body[key]
                        body = existing_settings
                    
                    with open("settings.json", "w", encoding="utf-8") as f:
                        json.dump(body, f, indent=4, ensure_ascii=False)
                    
                    SETTINGS_CHANGED = True
                    
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return
            # POST /api/monitors
            elif parts == ['api', 'monitors']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                code = body.get('code', '').upper()
                if not code:
                    self.send_json_response(400, {"error": "Manca il codice"})
                    return
                if code not in MONITORS:
                    import time
                    MONITORS[code] = {"status": "pending", "last_seen": time.time(), "config": None, "name": f"TV-{code}"}
                
                MONITORS[code]["status"] = "active"
                if "name" in body: MONITORS[code]["name"] = body["name"]
                if "config" in body: MONITORS[code]["config"] = body["config"]
                
                save_monitors()
                self.send_json_response(200, MONITORS[code])
                return

            # POST /api/queue/reset — Reset queue counters for all categories
            elif parts == ['api', 'queue', 'reset']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    counters = db.reset_queue_counters()
                    print(f"[Queue] Contatori coda resettati da {user['username']}: {counters}")
                    self.send_json_response(200, {"success": True, "counters": counters})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/admin/users
            elif parts == ['api', 'admin', 'users']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                username = body.get('username')
                password = body.get('password')
                role = body.get('role')
                sector = body.get('sector')
                
                # Check permissions: ADMIN (Junior) cannot create SUPER_ADMIN or ADMIN
                if user['role'] == 'ADMIN' and role in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Permesso negato: solo il Super Amministratore può creare utenti amministrativi."})
                    return
                
                try:
                    new_id = db.create_user(username, password, role, sector)
                    self.send_json_response(201, {"success": True, "id": new_id})
                except ValueError as ve:
                    self.send_json_response(400, {"error": str(ve)})
                return

            # POST /api/debug/simulate
            elif parts == ['api', 'debug', 'simulate']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    import dev_simulate_orders
                    dev_simulate_orders.main()
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/debug/generate10
            elif parts == ['api', 'debug', 'generate10']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    import dev_generate_10_orders
                    dev_generate_10_orders.run()
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/sync/pull-menu-config
            elif parts == ['api', 'sync', 'pull-menu-config']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                if RUN_MODE == "remote":
                    self.send_json_response(400, {"error": "Questa operazione può essere eseguita solo sulle casse locali"})
                    return
                if not REMOTE_URL:
                    self.send_json_response(400, {"error": "URL remoto (REMOTE_URL) non configurato"})
                    return
                
                pull_url = f"{REMOTE_URL}/api/sync/pull-menu-config"
                req = urllib.request.Request(pull_url)
                req.add_header('X-Sync-Token', SYNC_TOKEN)
                req.add_header('X-Server-ID', get_server_id())
                
                ssl_context = ssl.create_default_context()
                ssl_context.check_hostname = False
                ssl_context.verify_mode = ssl.CERT_NONE
                
                try:
                    with urllib.request.urlopen(req, timeout=15, context=ssl_context) as response:
                        if response.status == 200:
                            config_data = json.loads(response.read().decode('utf-8'))
                            db.apply_synced_menu_config(config_data)
                            MENU_CHANGED = True
                            self.send_json_response(200, {
                                "success": True, 
                                "ingredients_count": len(config_data.get('ingredients', [])),
                                "recipes_count": len(config_data.get('recipes', []))
                            })
                        else:
                            self.send_json_response(response.status, {"error": f"Errore server remoto: HTTP {response.status}"})
                except urllib.error.URLError as e:
                    self.send_json_response(500, {"error": f"Errore di rete: {str(e.reason)}"})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante l'applicazione del menu: {str(e)}"})
                return

            # POST /api/sync/confirm
            elif parts == ['api', 'sync', 'confirm']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                if check_sync_conflict(self, sync_t):
                    self.send_json_response(409, {"error": "Duplicate server detected. Another instance is active."})
                    return
                body = self.read_json_body()
                db.confirm_remote_orders_synced(body)
                self.send_json_response(200, {"success": True})
                return

            # POST /api/sync/push-foodcost-sales
            elif parts == ['api', 'sync', 'push-foodcost-sales']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                body = self.read_json_body()
                try:
                    db.apply_synced_foodcost_sales(body)
                    self.send_json_response(200, {"success": True, "message": "Vendite allineate con successo sul server remoto."})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/sync/push
            elif parts == ['api', 'sync', 'push']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                if check_sync_conflict(self, sync_t):
                    self.send_json_response(409, {"error": "Duplicate server detected. Another instance is active."})
                    return
                body = self.read_json_body()
                for update in body:
                    db.update_remote_order_status(
                        update['remote_id'], 
                        update['status'], 
                        update.get('items'),
                        is_printed=update.get('is_printed'),
                        printed_at=update.get('printed_at')
                    )
                self.send_json_response(200, {"success": True})
                return

            # POST /api/sync/menu
            elif parts == ['api', 'sync', 'menu']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                if check_sync_conflict(self, sync_t):
                    self.send_json_response(409, {"error": "Duplicate server detected. Another instance is active."})
                    return
                body = self.read_json_body()
                
                # 1. Sync menu items
                menu_items = body.get('menu_items', [])
                db.sync_menu_items(menu_items)
                
                # 2. Sync categories if present
                categories = body.get('categories')
                if categories is not None:
                    try:
                        with open("categories.json", "w", encoding="utf-8") as f:
                            json.dump(categories, f, indent=4, ensure_ascii=False)
                    except Exception as cat_err:
                        print(f"[Sync Server] Errore salvataggio categorie: {cat_err}")
                
                self.send_json_response(200, {"success": True})
                return

            # POST /api/sync/connections
            elif parts == ['api', 'sync', 'connections']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                body = self.read_json_body()
                
                import time
                now = time.time()
                for k, v in body.items():
                    v['last_seen'] = now
                    ACTIVE_CONNECTIONS[k] = v
                    
                self.send_json_response(200, {"success": True})
                return

            # POST /api/sync/settings
            elif parts == ['api', 'sync', 'settings']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                if check_sync_conflict(self, sync_t):
                    self.send_json_response(409, {"error": "Duplicate server detected. Another instance is active."})
                    return
                body = self.read_json_body()
                
                try:
                    settings = {}
                    if os.path.exists("settings.json"):
                        try:
                            with open("settings.json", "r", encoding="utf-8") as f:
                                settings = json.load(f)
                        except Exception:
                            pass
                    public_keys = ["sagra_active", "whatsapp_sim", "whatsapp_name", "wifi_ssid", "wifi_password", "menu_url", "auto_print_enabled", "kitchen_ui_mode", "print_delay_before", "print_delay_after", "print_sequential"]
                    for key in public_keys:
                        if key in body:
                            settings[key] = body[key]
                    
                    with open("settings.json", "w", encoding="utf-8") as f:
                        json.dump(settings, f, indent=4, ensure_ascii=False)
                    
                    self.send_json_response(200, {"success": True})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/admin/update-software
            elif parts == ['api', 'admin', 'update-software']:
                user = self.get_authenticated_user()
                has_sync_token = (self.headers.get('X-Sync-Token') == SYNC_TOKEN and SYNC_TOKEN)
                if not has_sync_token and (not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']):
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return

                try:
                    req_data = {}
                    try:
                        req_data = self.read_json_body() or {}
                    except:
                        pass
                    target_env = req_data.get('target_env', 'local') # 'local' o 'remote'
                    target_version = req_data.get('version', 'latest')
                    clean_ver = re.sub(r'[^a-zA-Z0-9._-]', '', str(target_version)) if target_version else 'latest'

                    # Caso A: Siamo sul server locale e l'utente ha chiesto di aggiornare il server remoto (Cloud)
                    if RUN_MODE == "local" and target_env == "remote":
                        if not REMOTE_URL:
                            self.send_json_response(400, {"error": "Nessun server Cloud remoto configurato"})
                            return
                        if user and user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                            self.send_json_response(403, {"error": "Solo un Amministratore può aggiornare il server Cloud"})
                            return

                        print(f"[Update] Inoltro richiesta aggiornamento ({clean_ver}) al server Cloud {REMOTE_URL}...")
                        ssl_context = ssl._create_unverified_context()
                        forward_payload = json.dumps({"version": clean_ver, "target_env": "local"}).encode('utf-8')
                        forward_req = urllib.request.Request(
                            f"{REMOTE_URL}/api/admin/update-software",
                            data=forward_payload,
                            headers={
                                'Content-Type': 'application/json',
                                'X-Sync-Token': SYNC_TOKEN
                            },
                            method='POST'
                        )
                        with urllib.request.urlopen(forward_req, timeout=60, context=ssl_context) as f_resp:
                            resp_body = json.loads(f_resp.read().decode('utf-8'))
                            self.send_json_response(f_resp.status, resp_body)
                            return

                    # Caso B: Siamo su Cloud (RUN_MODE == "remote") che aggiorna se stesso, oppure locale che aggiorna se stesso
                    if RUN_MODE == "remote":
                        if clean_ver and clean_ver.lower() != 'latest':
                            zip_source_path = os.path.join("build", "releases", f"Sagra_{clean_ver}.zip")
                        else:
                            zip_source_path = os.path.join("build", "Sagra_Portabile.zip")

                        if not os.path.exists(zip_source_path):
                            self.send_json_response(404, {"error": f"Pacchetto '{clean_ver}' non trovato nell'archivio del server Cloud"})
                            return
                        with open(zip_source_path, "rb") as f:
                            zip_data = f.read()
                    else:
                        # Server locale che scarica da Cloud
                        ssl_context = ssl._create_unverified_context()
                        if clean_ver and clean_ver.lower() != 'latest':
                            update_url = f"{REMOTE_URL}/api/update/download?version={clean_ver}"
                        else:
                            update_url = f"{REMOTE_URL}/api/update/download"

                        print(f"[Update] Richiesta download aggiornamento ({clean_ver}) da {update_url}...")
                        req = urllib.request.Request(update_url)
                        req.add_header('X-Sync-Token', SYNC_TOKEN)
                        with urllib.request.urlopen(req, timeout=60, context=ssl_context) as response:
                            if response.status != 200:
                                raise Exception(f"Il server remoto ha risposto con codice {response.status}")
                            zip_data = response.read()

                    # 1. Backup di sicurezza DB prima dell'estrazione
                    try:
                        import shutil
                        os.makedirs("backups", exist_ok=True)
                        ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
                        if os.path.exists("sagra.db"):
                            shutil.copyfile("sagra.db", os.path.join("backups", f"sagra_pre_update_{clean_ver}_{ts}.db"))
                        if os.path.exists("foodcost.db"):
                            shutil.copyfile("foodcost.db", os.path.join("backups", f"foodcost_pre_update_{clean_ver}_{ts}.db"))
                    except Exception as bkp_err:
                        print(f"[Update] Avviso backup pre-update: {bkp_err}")

                    # 2. Scrittura ed estrazione ZIP
                    temp_zip_path = "sagra_update_temp.zip"
                    with open(temp_zip_path, "wb") as f:
                        f.write(zip_data)

                    import zipfile
                    with zipfile.ZipFile(temp_zip_path, 'r') as zip_ref:
                        new_files = set(zip_ref.namelist())

                        # Se stiamo eseguendo un rollback/cambio versione, rimuove file orfani in public/ non presenti nel pacchetto di destinazione
                        if os.path.exists("public"):
                            for existing_file in os.listdir("public"):
                                rel_path = os.path.join("public", existing_file)
                                if os.path.isfile(rel_path) and existing_file.endswith(('.html', '.js', '.css')):
                                    if rel_path not in new_files:
                                        try:
                                            os.remove(rel_path)
                                            print(f"[Update] Rimosso file orfano di versione diversa: {rel_path}")
                                        except Exception as rm_e:
                                            print(f"[Update] Avviso rimozione orfano {rel_path}: {rm_e}")

                        for member in zip_ref.infolist():
                            filename = member.filename
                            if filename.endswith(('.db', '.db-shm', '.db-wal', '.sqlite')):
                                if os.path.exists(filename):
                                    continue
                            if filename in ['settings.json', 'categories.json', 'monitors.json']:
                                if os.path.exists(filename):
                                    continue
                            if ".." in filename or filename.startswith("/"):
                                continue
                            zip_ref.extract(member, ".")

                    if os.path.exists(temp_zip_path):
                        os.remove(temp_zip_path)

                    # 3. Avvia spegnimento differito per consentire l'invio della risposta
                    env_name = "Cloud Aruba" if RUN_MODE == "remote" else "Locale"
                    def shutdown_soon():
                        time.sleep(1.5)
                        print(f"[Update] Arresto del server ({env_name}) completato per versione {clean_ver}. Riavviare il server.")
                        os._exit(0)

                    threading.Thread(target=shutdown_soon, daemon=True).start()

                    self.send_json_response(200, {
                        "success": True,
                        "target_env": target_env,
                        "version": clean_ver,
                        "message": f"Software {env_name} aggiornato con successo alla versione {clean_ver}! Il server si riavvierà a breve."
                    })
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante l'aggiornamento: {str(e)}"})
                return

            # POST /api/checkout
            elif parts == ['api', 'checkout']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                body = self.read_json_body()
                
                date_str = body.get('date')
                cassa_iniziale = float(body.get('cassa_iniziale', 0.0))
                pos_amount = float(body.get('pos_amount', 0.0))
                soldi_da_versare = float(body.get('soldi_da_versare', 0.0))
                quota_tombola = float(body.get('quota_tombola', 0.0))
                denominations = body.get('denominations', [])
                expenses = body.get('expenses', [])
                notes = body.get('notes', '')
                
                if not date_str:
                    self.send_json_response(400, {"error": "Manca il campo data"})
                    return
                    
                new_id = db.save_daily_checkout(
                    date_str, cassa_iniziale, pos_amount, soldi_da_versare, quota_tombola, denominations, expenses, notes
                )
                self.send_json_response(200, {"success": True, "id": new_id})
                return

            # POST /api/admin/clear-remote-db
            elif parts == ['api', 'admin', 'clear-remote-db']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato: solo il Super Amministratore può eseguire questa azione."})
                    return
                
                try:
                    ssl_context = ssl._create_unverified_context()
                    url = f"{REMOTE_URL}/api/sync/clear-database"
                    req = urllib.request.Request(url, method='POST')
                    req.add_header('X-Sync-Token', SYNC_TOKEN)
                    
                    with urllib.request.urlopen(req, timeout=10, context=ssl_context) as response:
                        if response.status == 200:
                            res_data = json.loads(response.read().decode('utf-8'))
                            self.send_json_response(200, res_data)
                        else:
                            self.send_json_response(response.status, {"error": f"Il server remoto ha risposto con codice {response.status}"})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore di connessione al server remoto: {str(e)}"})
                return

            # POST /api/admin/clear-local-db
            elif parts == ['api', 'admin', 'clear-local-db']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato: solo il Super Amministratore può eseguire questa azione."})
                    return
                
                try:
                    db.clear_all_orders()
                    self.send_json_response(200, {"success": True, "message": "Tutti gli ordini sul database locale sono stati eliminati."})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante l'eliminazione degli ordini sul DB locale: {str(e)}"})
                return

            # POST /api/admin/delete-orders-by-day
            elif parts == ['api', 'admin', 'delete-orders-by-day']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato: solo il Super Amministratore può eseguire questa azione."})
                    return
                
                try:
                    body = self.read_json_body()
                    date_str = body.get('date')
                    if not date_str:
                        self.send_json_response(400, {"error": "Parametro data ('date') mancante."})
                        return
                    
                    # Validate date format
                    import datetime
                    try:
                        datetime.datetime.strptime(date_str, "%Y-%m-%d")
                    except ValueError:
                        self.send_json_response(400, {"error": "Formato data non valido. Usa il formato YYYY-MM-DD."})
                        return

                    deleted_count = db.delete_orders_by_day(date_str)
                    self.send_json_response(200, {"success": True, "deleted_count": deleted_count, "message": f"Eliminati {deleted_count} ordini per la giornata del {date_str}."})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante l'eliminazione degli ordini: {str(e)}"})
                return


            # POST /api/admin/backup/import
            elif parts == ['api', 'admin', 'backup', 'import']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                
                try:
                    content_length = int(self.headers.get('Content-Length', 0))
                    if content_length == 0:
                        self.send_json_response(400, {"error": "Corpo della richiesta vuoto"})
                        return
                    
                    zip_data = self.rfile.read(content_length)
                    import io
                    import zipfile
                    
                    zip_buffer = io.BytesIO(zip_data)
                    if not zipfile.is_zipfile(zip_buffer):
                        self.send_json_response(400, {"error": "Il file caricato non è un archivio ZIP valido"})
                        return
                    
                    allowed_files = {"settings.json", "categories.json", os.path.basename(db.DB_FILE)}
                    
                    with zipfile.ZipFile(zip_buffer, 'r') as zip_file:
                        # Valida tutti i file all'interno dello ZIP prima di estrarli
                        for info in zip_file.infolist():
                            if info.filename not in allowed_files or "/" in info.filename or "\\" in info.filename:
                                self.send_json_response(400, {"error": f"File non consentito nel backup: {info.filename}"})
                                return
                        
                        # Estrai i file consentiti
                        for info in zip_file.infolist():
                            if info.filename == os.path.basename(db.DB_FILE):
                                target_path = db.DB_FILE + ".new"
                            else:
                                target_path = info.filename
                                
                            with open(target_path, "wb") as f:
                                f.write(zip_file.read(info.filename))
                                
                    self.send_json_response(200, {"success": True, "message": "Backup importato correttamente. Riavvio in corso..."})
                    
                    # Avvia thread di riavvio controllato
                    def restart_server():
                        time.sleep(1.5)
                        print("[Server] Riavvio richiesto dopo ripristino backup.")
                        os._exit(0)
                        
                    threading.Thread(target=restart_server).start()
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore importazione backup: {str(e)}"})
                return

            # POST /api/admin/backup/upload-to-cloud
            elif parts == ['api', 'admin', 'backup', 'upload-to-cloud']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    import io
                    import zipfile
                    # Esegui checkpoint del database prima di esportarlo
                    db.checkpoint_db()
                    
                    zip_buffer = io.BytesIO()
                    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
                        if os.path.exists("settings.json"):
                            zip_file.write("settings.json", "settings.json")
                        if os.path.exists("categories.json"):
                            zip_file.write("categories.json", "categories.json")
                        if db.DB_TYPE == "sqlite" and db.DB_FILE and os.path.exists(db.DB_FILE):
                            zip_file.write(db.DB_FILE, os.path.basename(db.DB_FILE))
                            
                    zip_bytes = zip_buffer.getvalue()
                    
                    # Esegui upload sul server remoto
                    if not REMOTE_URL or not SYNC_TOKEN:
                        self.send_json_response(400, {"error": "Configurazione cloud mancante o incompleta"})
                        return
                        
                    upload_url = f"{REMOTE_URL}/api/sync/backup/upload"
                    ssl_context = ssl._create_unverified_context()
                    
                    req = urllib.request.Request(upload_url, data=zip_bytes, method='POST')
                    req.add_header('X-Sync-Token', SYNC_TOKEN)
                    req.add_header('Content-Type', 'application/zip')
                    
                    with urllib.request.urlopen(req, timeout=30, context=ssl_context) as response:
                        res_data = json.loads(response.read().decode('utf-8'))
                        if response.status == 200 and res_data.get('success'):
                            self.send_json_response(200, {"success": True, "message": "Backup caricato sul server remoto con successo"})
                        else:
                            self.send_json_response(response.status, {"error": res_data.get('error', 'Errore server remoto')})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante il caricamento del backup sul cloud: {str(e)}"})
                return

            # POST /api/admin/backup/import-from-cloud
            elif parts == ['api', 'admin', 'backup', 'import-from-cloud']:
                user = self.get_authenticated_user()
                if not user or user['role'] != 'SUPER_ADMIN':
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    body = self.read_json_body()
                    remote_url = body.get('remote_url', '').strip()
                    sync_token = body.get('sync_token', '').strip()
                    
                    if not remote_url or not sync_token:
                        self.send_json_response(400, {"error": "URL server remoto e Token di sincronizzazione richiesti"})
                        return
                    
                    if remote_url.endswith('/'):
                        remote_url = remote_url[:-1]
                        
                    download_url = f"{remote_url}/api/sync/backup/download"
                    ssl_context = ssl._create_unverified_context()
                    
                    req = urllib.request.Request(download_url)
                    req.add_header('X-Sync-Token', sync_token)
                    
                    with urllib.request.urlopen(req, timeout=30, context=ssl_context) as response:
                        if response.status != 200:
                            self.send_json_response(response.status, {"error": "Impossibile scaricare il backup dal server remoto"})
                            return
                        zip_data = response.read()
                        
                    import io
                    import zipfile
                    
                    zip_buffer = io.BytesIO(zip_data)
                    if not zipfile.is_zipfile(zip_buffer):
                        self.send_json_response(400, {"error": "Il file scaricato non è un archivio ZIP valido"})
                        return
                    
                    allowed_files = {"settings.json", "categories.json", os.path.basename(db.DB_FILE)}
                    
                    with zipfile.ZipFile(zip_buffer, 'r') as zip_file:
                        for info in zip_file.infolist():
                            if info.filename not in allowed_files or "/" in info.filename or "\\" in info.filename:
                                self.send_json_response(400, {"error": f"File non consentito nel backup: {info.filename}"})
                                return
                        
                        for info in zip_file.infolist():
                            if info.filename == os.path.basename(db.DB_FILE):
                                target_path = db.DB_FILE + ".new"
                            else:
                                target_path = info.filename
                                
                            with open(target_path, "wb") as f:
                                f.write(zip_file.read(info.filename))
                                
                    self.send_json_response(200, {"success": True, "message": "Backup importato dal server remoto con successo. Riavvio in corso..."})
                    
                    def restart_server():
                        time.sleep(1.5)
                        print("[Server] Riavvio richiesto dopo ripristino backup remoto.")
                        os._exit(0)
                        
                    threading.Thread(target=restart_server).start()
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore ripristino backup remoto: {str(e)}"})
                return

            # POST /api/sync/backup/upload
            elif parts == ['api', 'sync', 'backup', 'upload']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                try:
                    content_length = int(self.headers.get('Content-Length', 0))
                    if content_length == 0:
                        self.send_json_response(400, {"error": "Corpo della richiesta vuoto"})
                        return
                    zip_data = self.rfile.read(content_length)
                    
                    with open("cloud_backup.zip", "wb") as f:
                        f.write(zip_data)
                    self.send_json_response(200, {"success": True, "message": "Backup salvato nel cloud con successo"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/sync/clear-database
            elif parts == ['api', 'sync', 'clear-database']:
                sync_t = self.headers.get('X-Sync-Token')
                if not sync_t or sync_t != SYNC_TOKEN:
                    self.send_json_response(401, {"error": "Token di sincronizzazione non valido o mancante"})
                    return
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "Endpoint disponibile solo in modalità REMOTE"})
                    return
                
                try:
                    db.clear_all_orders()
                    self.send_json_response(200, {"success": True, "message": "Tutti gli ordini sul database remoto sono stati eliminati."})
                except Exception as e:
                    self.send_json_response(500, {"error": f"Errore durante l'eliminazione degli ordini sul DB remoto: {str(e)}"})
            # POST /api/orders/<id>/mark-printed
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'mark-printed':
                try:
                    order_id = int(parts[2])
                    res = db.mark_order_printed(order_id)
                    self.send_json_response(200, res)
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/totem/order/<code_or_number>/print, /api/totem/print, /api/print
            elif (len(parts) == 5 and parts[1] == 'totem' and parts[2] == 'order' and parts[4] == 'print') or \
                 (parts == ['api', 'totem', 'print']) or \
                 (parts == ['api', 'print']):
                try:
                    content_len = int(self.headers.get('Content-Length', 0)) if self.headers.get('Content-Length') else 0
                    body = self.read_json_body() if content_len > 0 else {}
                    code = None
                    if len(parts) == 5 and parts[1] == 'totem' and parts[2] == 'order' and parts[4] == 'print':
                        code = parts[3].strip()
                    else:
                        code = body.get('order_number') or body.get('order_id') or body.get('code') or body.get('pickup_pin')
                    
                    if not code:
                        self.send_json_response(400, {"error": "Codice ordine o PIN mancante"})
                        return
                        
                    totem_order = db.get_totem_order(str(code))
                    if not totem_order:
                        # Fallback On-Demand: Interroga subito il Cloud se siamo in locale
                        totem_order = fetch_and_sync_remote_order_on_demand(str(code))
                    if not totem_order:
                        self.send_json_response(404, {"error": "Ordine non trovato"})
                        return
                        
                    if totem_order.get('status') == 'PENDING':
                        self.send_json_response(400, {"error": "ORDER_NOT_PAID", "message": "L'ordine non è ancora stato pagato."})
                        return
                        
                    if totem_order.get('is_printed'):
                        self.send_json_response(409, {
                            "error": "ALREADY_PRINTED",
                            "message": "I biglietti per questo ordine risultano già stampati.",
                            "printed_at": totem_order.get('printed_at')
                        })
                        return
                        
                    res = db.mark_order_printed(totem_order['id'])
                    if not res['success']:
                        self.send_json_response(409, {
                            "error": "ALREADY_PRINTED",
                            "message": "I biglietti per questo ordine risultano già stampati.",
                            "printed_at": res['order'].get('printed_at') if res.get('order') else None
                        })
                        return

                    # Invio fisico della stampa da server (ESC/POS di rete per Tablet/Totem)
                    server_print_res = None
                    try:
                        import escpos_printer
                        server_print_res = escpos_printer.print_totem_order(totem_order)
                    except Exception as print_e:
                        print(f"[Totem Stampa] Errore modulo escpos_printer: {print_e}")
                        server_print_res = {"success": False, "error": str(print_e)}
                        
                    self.send_json_response(200, {
                        "success": True,
                        "message": "Stampa autorizzata con successo",
                        "printed_at": res['order'].get('printed_at') if res.get('order') else None,
                        "server_print": server_print_res,
                        "order": self.format_orders(totem_order)
                    })
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # POST /api/totem/printer/test
            elif parts == ['api', 'totem', 'printer', 'test']:
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    body = self.read_json_body()
                    ip = body.get('ip')
                    port = int(body.get('port', 9100))
                    if not ip:
                        self.send_json_response(400, {"error": "Indirizzo IP stampante mancante"})
                        return
                    import escpos_printer
                    test_order = {
                        "display_number": "TEST",
                        "order_number": 999,
                        "customer_name": "Test Stampante Totem",
                        "total_price": 0.0,
                        "items": [
                            {"item_name": "Stampa di Prova Totem", "quantity": 1, "category": "BAR", "price": 0.0}
                        ]
                    }
                    raw_data = escpos_printer.format_totem_order_tickets(test_order)
                    res = escpos_printer.print_to_network_printer(ip, port, raw_data)
                    self.send_json_response(200, res)
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            self.send_json_response(404, {"error": "API Route Not Found"})
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.send_json_response(500, {"error": str(e)})

    # API PUT Request handlers
    def handle_api_put(self, parts, query):
        global MENU_CHANGED
        try:
            # PUT /api/ingredients/purchases/<id>
            if len(parts) == 4 and parts[1] == 'ingredients' and parts[2] == 'purchases':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    purchase_id = int(parts[3])
                    body = self.read_json_body()
                    qty = float(body.get('quantity', 0.0))
                    size = float(body.get('package_size', 0.0))
                    price = float(body.get('package_price', 0.0))
                    supplier = body.get('supplier', '').strip()
                    doc_number = body.get('doc_number', '').strip()
                    payment_status = body.get('payment_status', 'PAGATO').strip()
                    payment_date = body.get('payment_date', '').strip()
                    
                    iva = float(body.get('iva', 10.0))
                    
                    if qty == 0 or size <= 0 or price == 0:
                        self.send_json_response(400, {"error": "Parametri non validi o mancanti"})
                        return
                        
                    db.update_ingredient_purchase(purchase_id, qty, size, price, supplier, doc_number, payment_status, payment_date, iva=iva)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID acquisto non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # PUT /api/menu/<id>
            elif len(parts) == 3 and parts[1] == 'menu':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    item_id = int(parts[2])
                    body = self.read_json_body()
                    
                    existing_item = db.get_menu_item(item_id)
                    if not existing_item:
                        self.send_json_response(404, {"error": "Prodotto non trovato"})
                        return
                    
                    if user['role'] == 'CUCINA':
                        if existing_item['category'] != user.get('sector'):
                            self.send_json_response(403, {"error": "Non hai i permessi per modificare prodotti di un altro settore"})
                            return
                        name = existing_item['name']
                        description = existing_item['description'] or ""
                        price = existing_item['price']
                        category = existing_item['category']
                        is_available = int(body.get('is_available', existing_item['is_available']))
                        stock_limit = body.get('stock_limit', existing_item.get('stock_limit'))
                        if stock_limit is not None and stock_limit != '':
                            stock_limit = int(stock_limit)
                        else:
                            stock_limit = None
                    else:
                        name = body.get('name') if body.get('name') is not None else existing_item['name']
                        description = body.get('description') if body.get('description') is not None else (existing_item['description'] or "")
                        price_val = body.get('price')
                        price = float(price_val) if price_val is not None else existing_item['price']
                        category = body.get('category') if body.get('category') is not None else existing_item['category']
                        is_available = int(body.get('is_available')) if body.get('is_available') is not None else existing_item['is_available']
                        
                        # Leggi lo stock limit se presente
                        if 'stock_limit' in body:
                            val = body.get('stock_limit')
                            stock_limit = int(val) if val is not None and val != '' else None
                        else:
                            stock_limit = existing_item.get('stock_limit')
                    
                    if not name or not category:
                        self.send_json_response(400, {"error": "Mancano campi obbligatori (nome, categoria)"})
                        return
                    
                    if is_available == 1 and stock_limit == 0:
                        self.send_json_response(400, {"error": "Impossibile riabilitare il prodotto con stock residuo a 0. Contatta la cucina per ripristinare lo stock."})
                        return
                    
                    db.update_menu_item(item_id, name, description, price, category, is_available, stock_limit=stock_limit)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID prodotto non valido"})
                return

            # PUT /api/menu/<id>/purchase-cost
            elif len(parts) == 4 and parts[1] == 'menu' and parts[3] == 'purchase-cost':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    item_id = int(parts[2])
                    body = self.read_json_body()
                    cost = float(body.get('purchase_cost', 0.0))
                    if cost < 0:
                        self.send_json_response(400, {"error": "Il costo di acquisto non può essere negativo"})
                        return
                    db.update_menu_item_purchase_cost(item_id, cost)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID prodotto o costo non valido"})
                return


            # PUT /api/ingredients/<id>
            elif len(parts) == 3 and parts[1] == 'ingredients':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    ing_id = int(parts[2])
                    body = self.read_json_body()
                    name = body.get('name', '').strip()
                    unit = body.get('unit', '').strip()
                    package_size = float(body.get('package_size', 0.0))
                    package_price = float(body.get('package_price', 0.0))
                    
                    iva = float(body.get('iva', 10.0))
                    
                    if not name or not unit or package_size <= 0 or package_price <= 0:
                        self.send_json_response(400, {"error": "Campi non validi o mancanti"})
                        return
                        
                    db.update_ingredient(ing_id, name, unit, package_size, package_price, iva=iva)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID ingrediente non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # PUT /api/orders/<id>/validate
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'validate':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    body = self.read_json_body()
                    cashier = body.get('cashier') or user['username']
                    payment_type = body.get('payment_type') or 'CONTANTI'
                    updated_order = db.validate_order(order_id, cashier, payment_type)
                    
                    MENU_CHANGED = True
                    
                    self.send_json_response(200, self.format_orders(updated_order))
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>/lock
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'lock':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    body = self.read_json_body()
                    cashier = body.get('cashier') or user['username']
                    if not cashier:
                        self.send_json_response(400, {"error": "Manca l'identificativo della cassa"})
                        return
                    success = db.lock_order(order_id, cashier)
                    if success:
                        self.send_json_response(200, {"success": True})
                    else:
                        status = db.get_order_status(order_id)
                        locked_by = status.get('locked_by') if status else "un'altra cassa"
                        self.send_json_response(409, {"success": False, "error": f"L'ordine è già in gestione da {locked_by}"})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>/unlock
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'unlock':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    body = self.read_json_body()
                    cashier = body.get('cashier') or user['username']
                    if not cashier:
                        self.send_json_response(400, {"error": "Manca l'identificativo della cassa"})
                        return
                    db.unlock_order(order_id, cashier)
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>/release
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'release':
                try:
                    order_id = int(parts[2])
                    # Content-Length is checked to see if body is present
                    content_length = int(self.headers.get('Content-Length', 0))
                    body = self.read_json_body() if content_length > 0 else {}
                    release_items = body.get('items') # list of {'order_item_id': x, 'quantity': y} or None
                    
                    db.release_order_items(order_id, release_items)
                    self.send_json_response(200, {"success": True})
                except ValueError as ve:
                    self.send_json_response(400, {"error": str(ve)})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # PUT /api/kitchen/items/<id>/status
            elif len(parts) == 5 and parts[1] == 'kitchen' and parts[2] == 'items' and parts[4] == 'status':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_item_id = int(parts[3])
                    body = self.read_json_body()
                    new_status = body.get('status')
                    qty = body.get('quantity')
                    if qty is not None:
                        qty = int(qty)
                    
                    if new_status not in ['HOLD', 'PENDING', 'PREPARING', 'READY', 'DELIVERED']:
                        self.send_json_response(400, {"error": "Status non valido"})
                        return
                        
                    # Verifica restrizione di settore per il ruolo CUCINA
                    if user['role'] == 'CUCINA':
                        item_category = db.get_item_category(order_item_id)
                        if not item_category or item_category != user['sector']:
                            self.send_json_response(403, {"error": f"Accesso negato: non puoi modificare piatti del settore {item_category or 'sconosciuto'}"})
                            return

                    updated_order = db.update_order_item_status(order_item_id, new_status, qty)
                    self.send_json_response(200, {"success": True})
                except ValueError as e:
                    self.send_json_response(400, {"error": str(e)})
                return

            # PUT /api/orders/<id>/items/<item_id>/release
            elif len(parts) == 6 and parts[1] == 'orders' and parts[3] == 'items' and parts[5] == 'release':
                # Public endpoint per i clienti
                try:
                    order_id = int(parts[2])
                    item_id = int(parts[4])
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid IDs"})
                    return
                try:
                    success = db.release_order_item(order_id, item_id)
                    if success:
                        self.send_json_response(200, {"success": True})
                    else:
                        self.send_json_response(400, {"error": "Piatto non trovato o non in stato HOLD"})
                except ValueError as ve:
                    self.send_json_response(400, {"error": str(ve)})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # PUT /api/orders/<id>/deliver
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'deliver':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    category = query.get('category', [None])[0] if query else None
                    
                    # Verifica restrizione di settore per il ruolo CUCINA
                    if user['role'] == 'CUCINA':
                        if not category or category != user['sector']:
                            self.send_json_response(403, {"error": f"Accesso negato: non puoi consegnare ordini al di fuori del settore {user['sector']}"})
                            return

                    db.deliver_order(order_id, category)
                    self.send_json_response(200, {"success": True, "message": "Ordine consegnato con successo"})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>
            elif len(parts) == 3 and parts[1] == 'orders':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    body = self.read_json_body()
                    
                    name = body.get('customer_name')
                    phone = body.get('customer_phone', '')
                    table = body.get('table_number', '')
                    items = body.get('items', [])
                    wa_contact_saved = body.get('wa_contact_saved', 0)
                    
                    if not name or not items:
                        self.send_json_response(400, {"error": "Mancano campi obbligatori (nome, piatti)"})
                        return
                        
                    updated_order = db.update_order(order_id, name, phone, table, items, wa_contact_saved)
                    self.send_json_response(200, self.format_orders(updated_order))
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>/cancel
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'cancel':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    db.cancel_order(order_id)
                    self.send_json_response(200, {"success": True, "message": "Ordine annullato con successo"})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/orders/<id>/reopen
            elif len(parts) == 4 and parts[1] == 'orders' and parts[3] == 'reopen':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CASSIERE']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    order_id = int(parts[2])
                    db.reopen_order(order_id)
                    self.send_json_response(200, {"success": True, "message": "Ordine riaperto con successo"})
                except ValueError:
                    self.send_json_response(400, {"error": "Invalid Order ID"})
                return

            # PUT /api/admin/users/<id>
            elif len(parts) == 4 and parts[1] == 'admin' and parts[2] == 'users':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    target_user_id = int(parts[3])
                    body = self.read_json_body()
                    username = body.get('username')
                    password = body.get('password')
                    role = body.get('role')
                    sector = body.get('sector')
                    
                    # Validation: ADMIN (Junior) cannot edit administrators
                    if user['role'] == 'ADMIN':
                        target_user = None
                        all_users = db.get_all_users()
                        for u in all_users:
                            if u['id'] == target_user_id:
                                target_user = u
                                break
                        if not target_user:
                            self.send_json_response(404, {"error": "Utente non trovato"})
                            return
                        
                        is_target_admin = target_user['role'] in ['SUPER_ADMIN', 'ADMIN']
                        is_self = target_user['id'] == user['id']
                        
                        if is_target_admin and not is_self:
                            self.send_json_response(403, {"error": "Permesso negato: non puoi modificare altri amministratori."})
                            return
                        
                        if role in ['SUPER_ADMIN', 'ADMIN'] and not is_self:
                            self.send_json_response(403, {"error": "Permesso negato: non puoi assegnare ruoli amministrativi."})
                            return

                    db.update_user(target_user_id, username, password, role, sector)
                    self.send_json_response(200, {"success": True})
                except ValueError as ve:
                    self.send_json_response(400, {"error": str(ve)})
                return

            # PUT /api/kitchen/orders/<id>/bulk_status
            elif len(parts) == 5 and parts[1] == 'kitchen' and parts[2] == 'orders' and parts[4] == 'bulk_status':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'CUCINA']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                
                order_id = parts[3]
                data = self.read_json_body()
                if not data or 'status' not in data:
                    self.send_json_response(400, {"error": "Manca il parametro 'status'"})
                    return
                new_status = data['status']
                if new_status not in ['HOLD', 'PENDING', 'PREPARING', 'READY', 'DELIVERED']:
                    self.send_json_response(400, {"error": "Stato non valido"})
                    return

                only_from = data.get('only_from_status')
                category = data.get('category')
                
                # Verifica restrizione di settore per il ruolo CUCINA
                if user['role'] == 'CUCINA':
                    if not category or category != user['sector']:
                        self.send_json_response(403, {"error": f"Accesso negato: puoi aggiornare piatti solo del settore {user['sector']}"})
                        return

                success = db.bulk_update_order_items_status(order_id, new_status, only_from, category)
                if success is not False:
                    self.send_json_response(200, {"message": "Status updated for all items"})
                else:
                    self.send_json_response(500, {"error": "Database error"})
                return

            self.send_json_response(404, {"error": "API Route Not Found"})
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.send_json_response(500, {"error": str(e)})

    # API DELETE Request handlers
    def handle_api_delete(self, parts):
        global MENU_CHANGED
        try:
            # DELETE /api/menu/<id>
            if len(parts) == 3 and parts[1] == 'menu':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    item_id = int(parts[2])
                    was_deleted = db.delete_menu_item(item_id)
                    
                    MENU_CHANGED = True
                    
                    self.send_json_response(200, {"success": True, "physically_deleted": was_deleted})
                except ValueError:
                    self.send_json_response(400, {"error": "ID prodotto non valido"})
                return

            # DELETE /api/ingredients/<id>
            elif len(parts) == 3 and parts[1] == 'ingredients':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    ing_id = int(parts[2])
                    db.delete_ingredient(ing_id)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID ingrediente non valido"})

            # DELETE /api/ingredients/purchases/<id>
            elif len(parts) == 4 and parts[1] == 'ingredients' and parts[2] == 'purchases':
                if RUN_MODE != "remote":
                    self.send_json_response(403, {"error": "L'ottimizzazione prezzi è disabilitata sulle casse locali."})
                    return
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN', 'F&B_MANAGER']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    purchase_id = int(parts[3])
                    db.delete_ingredient_purchase(purchase_id)
                    MENU_CHANGED = True
                    self.send_json_response(200, {"success": True})
                except ValueError:
                    self.send_json_response(400, {"error": "ID acquisto non valido"})
                except Exception as e:
                    self.send_json_response(500, {"error": str(e)})
                return

            # DELETE /api/admin/users/<id>
            elif len(parts) == 4 and parts[1] == 'admin' and parts[2] == 'users':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                try:
                    target_user_id = int(parts[3])
                    
                    # Validation: ADMIN (Junior) cannot delete administrators
                    if user['role'] == 'ADMIN':
                        target_user = None
                        all_users = db.get_all_users()
                        for u in all_users:
                            if u['id'] == target_user_id:
                                target_user = u
                                break
                        if target_user and target_user['role'] in ['SUPER_ADMIN', 'ADMIN']:
                            self.send_json_response(403, {"error": "Permesso negato: non puoi eliminare utenti amministrativi."})
                            return

                    db.delete_user(target_user_id, user['id'])
                    self.send_json_response(200, {"success": True})
                except ValueError as ve:
                    self.send_json_response(400, {"error": str(ve)})
                return

            # DELETE /api/monitors/<code>
            elif len(parts) == 3 and parts[1] == 'monitors':
                user = self.get_authenticated_user()
                if not user or user['role'] not in ['SUPER_ADMIN', 'ADMIN']:
                    self.send_json_response(403, {"error": "Accesso negato"})
                    return
                code = parts[2].upper()
                if code in MONITORS:
                    del MONITORS[code]
                    save_monitors()
                self.send_json_response(200, {"success": True})
                return
            
            self.send_json_response(404, {"error": "API Route Not Found"})
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.send_json_response(500, {"error": str(e)})



    # Helper: read JSON request body
    def read_json_body(self):
        raw_length = self.headers.get('Content-Length')
        if raw_length is None:
            raise ValueError("Content-Length header mancante")
        try:
            content_length = int(raw_length)
        except ValueError:
            raise ValueError(f"Content-Length non valido: {raw_length!r}")
        if content_length < 0:
            raise ValueError("Content-Length negativo")
        if content_length > 10 * 1024 * 1024:  # limite 10 MB
            raise ValueError("Payload troppo grande (max 10 MB)")
        body = self.rfile.read(content_length)
        try:
            return json.loads(body.decode('utf-8'))
        except json.JSONDecodeError as e:
            raise ValueError(f"JSON non valido: {e}")

    # Helper: send JSON response
    def send_json_response(self, status, data):
        self.send_response(status)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
        self.send_header('Pragma', 'no-cache')
        self.send_header('Expires', '0')
        response_bytes = json.dumps(data, cls=DateTimeEncoder).encode('utf-8')
        self.send_header('Content-Length', str(len(response_bytes)))
        self.end_headers()
        self.wfile.write(response_bytes)


def make_zip_archive():
    import io
    import zipfile
    
    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
        for root, dirs, files in os.walk('.'):
            # Exclude folders
            if any(part.startswith('.') and part not in ['.', '..'] for part in root.split(os.sep)):
                continue
            if 'dev_utils' in root.split(os.sep) or '__pycache__' in root.split(os.sep):
                continue
                
            for file in files:
                # Exclude specific files
                if file.startswith('.') or file.endswith('.pyc') or file.endswith('.zip'):
                    continue
                # Exclude database, log and temporary files specifically
                if file.endswith('.db') or file.endswith('.db-shm') or file.endswith('.db-wal') or file.endswith('.db.new') or file.endswith('.log'):
                    continue
                
                file_path = os.path.join(root, file)
                # Keep paths relative to current directory for the zip
                archive_name = os.path.relpath(file_path, '.')
                zip_file.write(file_path, archive_name)
                
    zip_buffer.seek(0)
    return zip_buffer.getvalue()


def sync_worker():
    global SYNC_CONFLICT_ACTIVE, SYNC_CONFLICT_DETAILS, FORCE_SYNC_ONCE
    BASE_INTERVAL = int(os.environ.get("SYNC_INTERVAL_SECONDS", "30"))
    MAX_INTERVAL = 300  # 5 minuti massimo tra un tentativo e l'altro
    WARN_AFTER_FAILURES = 5  # avvisa ogni N fallimenti consecutivi

    current_interval = BASE_INTERVAL
    consecutive_failures = 0
    cloud_was_online = True
    
    # Create an unverified SSL context to bypass certificate verification
    # when connecting to remote/NAS HTTPS servers (very common on Windows)
    ssl_context = ssl._create_unverified_context()

    print(f"[Sync Worker] Avviato in background. Sincronizzazione verso {REMOTE_URL} ogni {BASE_INTERVAL} secondi.")

    while True:
        cycle_success = False
        try:
            # 1. Pull new orders from Cloud
            pull_url = f"{REMOTE_URL}/api/sync/pull"
            req = urllib.request.Request(pull_url)
            req.add_header('X-Sync-Token', SYNC_TOKEN)
            req.add_header('X-Server-ID', get_server_id())
            if FORCE_SYNC_ONCE:
                req.add_header('X-Force-Sync', 'true')
            
            cloud_now_online = False
            try:
                with urllib.request.urlopen(req, timeout=10, context=ssl_context) as response:
                    if response.status == 200:
                        cloud_now_online = True
                        if not cloud_was_online:
                            print("[Sync Worker] Connessione al Cloud ripristinata.")
                        
                        # Reset conflict state upon successful connection
                        SYNC_CONFLICT_ACTIVE = False
                        SYNC_CONFLICT_DETAILS = ""
                        FORCE_SYNC_ONCE = False
                        
                        remote_orders = json.loads(response.read().decode('utf-8'))
                        if remote_orders:
                            print(f"[Sync Worker] Ricevuti {len(remote_orders)} nuovi ordini dal Cloud.")
                            confirmations = []
                            for ro in remote_orders:
                                try:
                                    local_order = db.insert_synced_order(
                                        remote_id=ro['id'],
                                        order_number=ro['order_number'],
                                        customer_name=ro['customer_name'],
                                        customer_phone=ro['customer_phone'],
                                        table_number=ro['table_number'],
                                        total_price=ro['total_price'],
                                        status=ro['status'],
                                        created_at=ro['created_at'],
                                        items=ro['items'],
                                        pickup_pin=ro.get('pickup_pin'),
                                        is_printed=ro.get('is_printed', 0),
                                        printed_at=ro.get('printed_at')
                                    )
                                    confirmations.append({
                                        "remote_id": ro['id'],
                                        "local_order_number": local_order['order_number']
                                    })
                                except Exception as insert_err:
                                    print(f"[Sync Worker] Errore inserimento ordine remoto {ro['id']}: {insert_err}")
                                    
                            # Confirm pull back to Cloud
                            if confirmations:
                                confirm_url = f"{REMOTE_URL}/api/sync/confirm"
                                confirm_data = json.dumps(confirmations).encode('utf-8')
                                c_req = urllib.request.Request(confirm_url, data=confirm_data, method='POST')
                                c_req.add_header('Content-Type', 'application/json')
                                c_req.add_header('X-Sync-Token', SYNC_TOKEN)
                                c_req.add_header('X-Server-ID', get_server_id())
                                if FORCE_SYNC_ONCE:
                                    c_req.add_header('X-Force-Sync', 'true')
                                with urllib.request.urlopen(c_req, timeout=10, context=ssl_context) as c_res:
                                    if c_res.status == 200:
                                        print(f"[Sync Worker] Confermata sincronizzazione di {len(confirmations)} ordini al Cloud.")
                        cycle_success = True
            except urllib.error.HTTPError as e:
                cloud_now_online = False
                if e.code == 409:
                    SYNC_CONFLICT_ACTIVE = True
                    SYNC_CONFLICT_DETAILS = "Un altro server locale è attivo dello stesso cloud"
                    print(f"❌ CONFLITTO CRITICO DI SINCRONIZZAZIONE (Pull): {e.reason or 'Duplicate server'}")
                    print("[Sync Worker] Un altro server Sagra locale è attualmente attivo ed in sincronizzazione con questo Cloud.")
                    print("[Sync Worker] Sincronizzazione locale SOSPESA temporaneamente per evitare corruzione dati.")
                    time.sleep(30)
                    continue
                if cloud_was_online:
                    print(f"[Sync Worker] Errore HTTP nel pull/confirm: {e.code} - {e.reason}")
            except urllib.error.URLError as e:
                cloud_now_online = False
                if cloud_was_online:
                    print(f"[Sync Worker] Cloud non raggiungibile (Pull): {e.reason}. I tentativi continueranno in background...")
            except Exception as e:
                cloud_now_online = False
                if cloud_was_online:
                    print(f"[Sync Worker] Errore generico nel pull/confirm: {e}")
                
            # 2. Push local status updates to Cloud
            try:
                local_updates = db.get_unsynced_local_updates()
                push_updates = []
                order_ids_to_mark = []
                local_only_ids_to_mark = []
                
                for lo in local_updates:
                    if lo.get('remote_id') is not None:
                        push_updates.append({
                            "remote_id": lo['remote_id'],
                            "status": lo['status'],
                            "is_printed": lo.get('is_printed', 0),
                            "printed_at": lo.get('printed_at'),
                            "items": [
                                {
                                    "menu_item_id": item['menu_item_id'],
                                    "notes": item['notes'],
                                    "status": item['status'],
                                    "quantity": item.get('quantity', 1),
                                    "price_at_order": item.get('price_at_order', 0.0),
                                    "is_client_locked": item.get('is_client_locked', 0),
                                    "queue_index": item.get('queue_index')
                                }
                                for item in lo.get('items', [])
                            ]
                        })
                        order_ids_to_mark.append(lo['id'])
                    else:
                        local_only_ids_to_mark.append(lo['id'])
                
                # Mark local-only orders as synced immediately
                if local_only_ids_to_mark:
                    db.mark_local_orders_synced(local_only_ids_to_mark)
                
                # Push cloud updates only if cloud is online
                if push_updates and cloud_now_online:
                    push_url = f"{REMOTE_URL}/api/sync/push"
                    push_data = json.dumps(push_updates).encode('utf-8')
                    p_req = urllib.request.Request(push_url, data=push_data, method='POST')
                    p_req.add_header('Content-Type', 'application/json')
                    p_req.add_header('X-Sync-Token', SYNC_TOKEN)
                    p_req.add_header('X-Server-ID', get_server_id())
                    if FORCE_SYNC_ONCE:
                        p_req.add_header('X-Force-Sync', 'true')
                    
                    try:
                        with urllib.request.urlopen(p_req, timeout=10, context=ssl_context) as p_res:
                            if p_res.status == 200:
                                print(f"[Sync Worker] Spediti {len(push_updates)} aggiornamenti di stato al Cloud.")
                                db.mark_local_orders_synced(order_ids_to_mark)
                                # Reset conflict state on success
                                SYNC_CONFLICT_ACTIVE = False
                                SYNC_CONFLICT_DETAILS = ""
                                FORCE_SYNC_ONCE = False
                    except urllib.error.HTTPError as e:
                        if e.code == 409:
                            SYNC_CONFLICT_ACTIVE = True
                            SYNC_CONFLICT_DETAILS = "Un altro server locale è attivo dello stesso cloud"
                            print(f"❌ CONFLITTO CRITICO DI SINCRONIZZAZIONE (Push): {e.reason or 'Duplicate server'}")
                            print("[Sync Worker] Un altro server Sagra locale è attualmente attivo ed in sincronizzazione con questo Cloud.")
                            print("[Sync Worker] Sincronizzazione locale SOSPESA temporaneamente per evitare corruzione dati.")
                            time.sleep(30)
                            continue
                        if cloud_was_online:
                            print(f"[Sync Worker] Errore HTTP (Push): {e.code} - {e.reason}")
                    except urllib.error.URLError as e:
                        if cloud_was_online:
                            print(f"[Sync Worker] Cloud non raggiungibile (Push): {e.reason}")
                    except Exception as e:
                        if cloud_was_online:
                            print(f"[Sync Worker] Errore generico nel push: {e}")
            except Exception as e:
                print(f"[Sync Worker] Errore nel processamento degli aggiornamenti locali: {e}")
                
            # 3. Push local menu changes to Cloud if changed
            global MENU_CHANGED
            if MENU_CHANGED and cloud_now_online:
                try:
                    menu_items = db.get_menu(include_unavailable=True)
                    categories = []
                    if os.path.exists("categories.json"):
                        try:
                            with open("categories.json", "r", encoding="utf-8") as f:
                                categories = json.load(f)
                        except Exception as json_err:
                            print(f"[Sync Worker] Errore lettura categories.json: {json_err}")
                    
                    sync_menu_url = f"{REMOTE_URL}/api/sync/menu"
                    menu_payload = {
                        "menu_items": menu_items,
                        "categories": categories
                    }
                    menu_data = json.dumps(menu_payload, cls=DateTimeEncoder).encode('utf-8')
                    
                    m_req = urllib.request.Request(sync_menu_url, data=menu_data, method='POST')
                    m_req.add_header('Content-Type', 'application/json')
                    m_req.add_header('X-Sync-Token', SYNC_TOKEN)
                    m_req.add_header('X-Server-ID', get_server_id())
                    if FORCE_SYNC_ONCE:
                        m_req.add_header('X-Force-Sync', 'true')
                    
                    with urllib.request.urlopen(m_req, timeout=10, context=ssl_context) as m_res:
                        if m_res.status == 200:
                            print("[Sync Worker] Sincronizzazione menu completata con successo al Cloud.")
                            MENU_CHANGED = False
                            # Reset conflict state on success
                            SYNC_CONFLICT_ACTIVE = False
                            SYNC_CONFLICT_DETAILS = ""
                            FORCE_SYNC_ONCE = False
                except urllib.error.HTTPError as e:
                    if e.code == 409:
                        SYNC_CONFLICT_ACTIVE = True
                        SYNC_CONFLICT_DETAILS = "Un altro server locale è attivo dello stesso cloud"
                        print(f"❌ CONFLITTO CRITICO DI SINCRONIZZAZIONE (Menu): {e.reason or 'Duplicate server'}")
                        print("[Sync Worker] Un altro server Sagra locale è attualmente attivo ed in sincronizzazione con questo Cloud.")
                        print("[Sync Worker] Sincronizzazione locale SOSPESA temporaneamente per evitare corruzione dati.")
                        time.sleep(30)
                        continue
                    if cloud_was_online:
                        print(f"[Sync Worker] Errore HTTP (Menu): {e.code} - {e.reason}")
                except urllib.error.URLError as e:
                    if cloud_was_online:
                        print(f"[Sync Worker] Cloud non raggiungibile per sincronizzazione menu: {e.reason}")
                except Exception as e:
                    print(f"[Sync Worker] Errore generico nella sincronizzazione del menu: {e}")
                
            # 4. Push local settings changes to Cloud if changed
            global SETTINGS_CHANGED
            if SETTINGS_CHANGED and cloud_now_online:
                try:
                    s_data = {}
                    if os.path.exists("settings.json"):
                        with open("settings.json", "r", encoding="utf-8") as f:
                            s_data = json.load(f)
                    
                    sync_settings_url = f"{REMOTE_URL}/api/sync/settings"
                    settings_payload = {
                        "sagra_active": s_data.get("sagra_active", True),
                        "whatsapp_sim": s_data.get("whatsapp_sim", ""),
                        "whatsapp_name": s_data.get("whatsapp_name", ""),
                        "wifi_ssid": s_data.get("wifi_ssid", ""),
                        "wifi_password": s_data.get("wifi_password", ""),
                        "menu_url": s_data.get("menu_url", ""),
                        "auto_print_enabled": s_data.get("auto_print_enabled", True),
                        "kitchen_ui_mode": s_data.get("kitchen_ui_mode", "classic"),
                        "print_delay_before": s_data.get("print_delay_before", 300),
                        "print_delay_after": s_data.get("print_delay_after", 800),
                        "print_sequential": s_data.get("print_sequential", False)
                    }
                    settings_data = json.dumps(settings_payload).encode('utf-8')
                    
                    st_req = urllib.request.Request(sync_settings_url, data=settings_data, method='POST')
                    st_req.add_header('Content-Type', 'application/json')
                    st_req.add_header('X-Sync-Token', SYNC_TOKEN)
                    st_req.add_header('X-Server-ID', get_server_id())
                    if FORCE_SYNC_ONCE:
                        st_req.add_header('X-Force-Sync', 'true')
                    
                    with urllib.request.urlopen(st_req, timeout=10, context=ssl_context) as st_res:
                        if st_res.status == 200:
                            print("[Sync Worker] Sincronizzazione impostazioni completata con successo al Cloud.")
                            SETTINGS_CHANGED = False
                            # Reset conflict state on success
                            SYNC_CONFLICT_ACTIVE = False
                            SYNC_CONFLICT_DETAILS = ""
                            FORCE_SYNC_ONCE = False
                except urllib.error.HTTPError as e:
                    if e.code == 409:
                        SYNC_CONFLICT_ACTIVE = True
                        SYNC_CONFLICT_DETAILS = "Un altro server locale è attivo dello stesso cloud"
                        print(f"❌ CONFLITTO CRITICO DI SINCRONIZZAZIONE (Settings): {e.reason or 'Duplicate server'}")
                        print("[Sync Worker] Un altro server Sagra locale è attualmente attivo ed in sincronizzazione con questo Cloud.")
                        print("[Sync Worker] Sincronizzazione locale SOSPESA temporaneamente per evitare corruzione dati.")
                        time.sleep(30)
                        continue
                    if cloud_was_online:
                        print(f"[Sync Worker] Errore HTTP (Settings): {e.code} - {e.reason}")
                except urllib.error.URLError as e:
                    if cloud_was_online:
                        print(f"[Sync Worker] Cloud non raggiungibile per sincronizzazione impostazioni: {e.reason}")
                except Exception as e:
                    print(f"[Sync Worker] Errore generico nella sincronizzazione delle impostazioni: {e}")
                
            # 5. Push active connections to Cloud
            if cloud_now_online:
                try:
                    conn_url = f"{REMOTE_URL}/api/sync/connections"
                    conn_data = json.dumps(ACTIVE_CONNECTIONS).encode('utf-8')
                    c_req = urllib.request.Request(conn_url, data=conn_data, method='POST')
                    c_req.add_header('Content-Type', 'application/json')
                    c_req.add_header('X-Sync-Token', SYNC_TOKEN)
                    with urllib.request.urlopen(c_req, timeout=5, context=ssl_context) as c_res:
                        pass
                except Exception:
                    pass
                
            cloud_was_online = cloud_now_online
            
        except Exception as global_err:
            print(f"[Sync Worker] Errore nel ciclo di sync: {global_err}")

        # --- Gestione backoff esponenziale ---
        if cycle_success or cloud_now_online:
            # Ciclo riuscito: ripristina intervallo base
            if consecutive_failures > 0:
                print(f"[Sync Worker] Connessione ristabilita dopo {consecutive_failures} fallimenti. Intervallo ripristinato a {BASE_INTERVAL}s.")
            consecutive_failures = 0
            current_interval = BASE_INTERVAL
        else:
            # Ciclo fallito: raddoppia l'intervallo fino al massimo
            consecutive_failures += 1
            current_interval = min(current_interval * 2, MAX_INTERVAL)
            if consecutive_failures % WARN_AFTER_FAILURES == 0:
                print(f"[Sync Worker] ⚠️  {consecutive_failures} fallimenti consecutivi. Prossimo tentativo tra {current_interval}s.")

        time.sleep(current_interval)




if __name__ == '__main__':
    # Initialize SQLite database
    db.init_db()
    
    # Genera o carica l'ID univoco di questa istanza
    my_id = get_server_id()
    print(f"[Config] Identificativo server locale: {my_id}")
    
    # Ensure public folder exists
    os.makedirs(DIRECTORY, exist_ok=True)
    
    # Caricamento configurazioni da settings.json
    SYNC_ENABLED = True
    if not os.path.exists("settings.json"):
        default_settings = {
            "whatsapp_sim": "+39 345 999 8888",
            "whatsapp_name": "Sagra APB",
            "wifi_ssid": "Test SSID",
            "wifi_password": "testpassword",
            "sync_target": "aruba",
            "aruba_remote_url": "http://31.14.139.127:8082",
            "aruba_sync_token": "YroFFngDygKHhPMFWKkB",
            "aruba_menu_url": "http://31.14.139.127:8082/index.html",
            "remote_url": "http://31.14.139.127:8082",
            "sync_token": "YroFFngDygKHhPMFWKkB",
            "menu_url": "http://31.14.139.127:8082/index.html",
            "sync_enabled": True,
            "run_mode": "local",
            "db_type": "sqlite",
            "db_file": "sagra.db",
            "sync_interval_seconds": 10,
            "local_port": 8000,
            "aruba_port": 8082,
            "port": 8000,
            "auto_print_enabled": True,
            "kitchen_ui_mode": "matrix",
            "sagra_active": True
        }
        try:
            with open("settings.json", "w", encoding="utf-8") as sf:
                json.dump(default_settings, sf, indent=4, ensure_ascii=False)
            print("[Config] Creato settings.json predefinito per Aruba Cloud.")
        except Exception as e:
            print(f"[Config] Errore creazione settings.json predefinito: {e}")

    if os.path.exists("settings.json"):
        try:
            with open("settings.json", "r", encoding="utf-8") as f:
                settings = json.load(f)
                RUN_MODE = settings.get("run_mode", RUN_MODE).lower()
                SYNC_ENABLED = settings.get("sync_enabled", True)
                
                # Risoluzione dinamica del target di sincronizzazione (Synology vs Aruba)
                sync_target = settings.get("sync_target", "synology").lower()
                print(f"[Config] Target di sincronizzazione attivo: {sync_target.upper()}")
                
                if sync_target == "aruba":
                    REMOTE_URL = settings.get("aruba_remote_url", REMOTE_URL)
                    SYNC_TOKEN = settings.get("aruba_sync_token", SYNC_TOKEN)
                else:
                    REMOTE_URL = settings.get("synology_remote_url", settings.get("remote_url", REMOTE_URL))
                    SYNC_TOKEN = settings.get("synology_sync_token", settings.get("sync_token", SYNC_TOKEN))
                
                if RUN_MODE == "local":
                    PORT = int(settings.get("local_port", settings.get("port", 8000)))
                else:
                    if sync_target == "aruba":
                        PORT = int(settings.get("aruba_port", settings.get("port", 8082)))
                    else:
                        PORT = int(settings.get("synology_port", settings.get("port", 8082)))
                # Intervallo di sync configurabile (default 10s)
                sync_interval = int(settings.get("sync_interval_seconds", 10))
                os.environ.setdefault("SYNC_INTERVAL_SECONDS", str(sync_interval))
        except Exception as e:
            print(f"[Config] Errore nel caricamento di settings.json: {e}")
            
    # Override da variabili d'ambiente
    RUN_MODE = os.environ.get("RUN_MODE", RUN_MODE).lower()
    REMOTE_URL = os.environ.get("REMOTE_URL", REMOTE_URL)
    SYNC_TOKEN = os.environ.get("SYNC_TOKEN", SYNC_TOKEN)
    PORT = int(os.environ.get("PORT", PORT))
    if os.environ.get("SYNC_ENABLED"):
        SYNC_ENABLED = os.environ.get("SYNC_ENABLED").lower() == "true"
    
    print(f"[Config] RUN_MODE impostato a: {RUN_MODE.upper()}")
    if RUN_MODE == "remote":
        db.init_foodcost_db()
        print("[Config] Database Food Cost (foodcost.db) inizializzato.")
    if RUN_MODE == "local":
        if SYNC_ENABLED:
            print(f"[Config] Sincronizzazione Cloud ABILITATA (Modalità Ibrida) verso {REMOTE_URL}")
            print(f"[Config] Sync ogni {os.environ.get('SYNC_INTERVAL_SECONDS', '10')}s")
            # Avvio del thread in background per il Sync
            t = threading.Thread(target=sync_worker, daemon=True)
            t.start()
        else:
            print("[Config] Sincronizzazione Cloud DISABILITATA (Modalità Solo Locale)")
            
    # Avvia il worker per scrivere il JSON delle connessioni
    def monitor_worker():
        while True:
            try:
                # Rimuovi le connessioni inattive da più di 5 minuti (300 secondi)
                now = time.time()
                to_remove = [k for k, v in ACTIVE_CONNECTIONS.items() if now - v["last_seen"] > 300]
                for k in to_remove:
                    del ACTIVE_CONNECTIONS[k]
                    
                with open('active_connections.json', 'w', encoding='utf-8') as f:
                    json.dump(ACTIVE_CONNECTIONS, f, indent=2)
            except Exception as e:
                print(f"Errore scrittura active_connections.json: {e}")
            time.sleep(2)
            
    t_monitor = threading.Thread(target=monitor_worker, daemon=True)
    t_monitor.start()

        
    # Run the server
    with MyTCPServer(("", PORT), MyHandler) as httpd:
        print(f"Server Sagra ({RUN_MODE.upper()}) running at http://localhost:{PORT}")
        print(f"Serving static files from: {os.path.abspath(DIRECTORY)}")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print("\nStopping server...")
            httpd.shutdown()
            print("Server stopped.")
