# simclient/simclient.py
import time
import uuid
import json
import socket
import hashlib
import paho.mqtt.client as mqtt
import os
import re
import platform
import logging
DEBUG_MODE = True  # Auf False setzen für Produktion

# Logging-Konfiguration
LOG_DIR = os.path.dirname(os.path.abspath(__file__))
LOG_PATH = os.path.join(LOG_DIR, "simclient.log")
log_handlers = [logging.FileHandler(LOG_PATH, encoding="utf-8")]
if DEBUG_MODE:
    log_handlers.append(logging.StreamHandler())
logging.basicConfig(
    level=logging.DEBUG if DEBUG_MODE else logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=log_handlers
)


def on_message(client, userdata, msg):
    logging.info(f"Empfangen: {msg.topic} {msg.payload.decode()}")


def get_mac_addresses():
    macs = set()
    try:
        for root, dirs, files in os.walk('/sys/class/net/'):
            for iface in dirs:
                try:
                    with open(f'/sys/class/net/{iface}/address') as f:
                        mac = f.read().strip()
                        if mac and mac != '00:00:00:00:00:00':
                            macs.add(mac)
                except Exception:
                    continue
            break
    except Exception:
        pass
    return sorted(macs)


def get_board_serial():
    # Raspberry Pi: /proc/cpuinfo, andere: /sys/class/dmi/id/product_serial
    serial = None
    try:
        with open('/proc/cpuinfo') as f:
            for line in f:
                if line.lower().startswith('serial'):
                    serial = line.split(':')[1].strip()
                    break
    except Exception:
        pass
    if not serial:
        try:
            with open('/sys/class/dmi/id/product_serial') as f:
                serial = f.read().strip()
        except Exception:
            pass
    return serial or "unknown"


def get_ip():
    # Versucht, die lokale IP zu ermitteln (nicht 127.0.0.1)
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return "unknown"


def get_hardware_token():
    serial = get_board_serial()
    macs = get_mac_addresses()
    token_raw = serial + "_" + "_".join(macs)
    # Hashen für Datenschutz
    token_hash = hashlib.sha256(token_raw.encode()).hexdigest()
    return token_hash


def get_model():
    # Versucht, das Modell auszulesen (z.B. Raspberry Pi, PC, etc.)
    try:
        if os.path.exists('/proc/device-tree/model'):
            with open('/proc/device-tree/model') as f:
                return f.read().strip()
        elif os.path.exists('/sys/class/dmi/id/product_name'):
            with open('/sys/class/dmi/id/product_name') as f:
                return f.read().strip()
    except Exception:
        pass
    return "unknown"


SOFTWARE_VERSION = "1.0.0"  # Optional: Anpassen bei neuen Releases


def send_discovery(client, client_id, hardware_token, ip_addr):
    macs = get_mac_addresses()
    discovery_msg = {
        "client_id": client_id,
        "hardware_token": hardware_token,
        "ip": ip_addr,
        "type": "infoscreen",
        "hostname": socket.gethostname(),
        "os_version": platform.platform(),
        "software_version": SOFTWARE_VERSION,
        "macs": macs,
        "model": get_model(),
    }
    client.publish("infoscreen/discovery", json.dumps(discovery_msg))
    logging.info(f"Discovery-Nachricht gesendet: {discovery_msg}")


def main():
    client_id = str(uuid.uuid4())
    hardware_token = get_hardware_token()
    ip_addr = get_ip()
    client = mqtt.Client(protocol=mqtt.MQTTv311, callback_api_version=2)
    client.on_message = on_message
    client.connect("mqtt", 1883)
    client.subscribe(f"infoscreen/{client_id}/config")
    send_discovery(client, client_id, hardware_token, ip_addr)
    while True:
        # Heartbeat senden
        client.publish(f"infoscreen/{client_id}/heartbeat", "alive")
        logging.debug("Heartbeat gesendet.")
        client.loop(timeout=1.0)
        time.sleep(5)


if __name__ == "__main__":
    main()
