# logic.py — ТЕСТОВАЯ ВЕРСИЯ
# ============================================
# Не управляет светом. Только:
# - публикует память (GC + IDF heap)
# - публикует конфиг net
# - публикует счётчики boot_count / poweron_count
# - принимает команду status — публикует всё
# ============================================

import asyncio
import gc
import json
import os
import time
import net

VERSION = "test-0.0.1"

STATUS_INTERVAL_SEC = 60


def _ts():
    tm = time.localtime()
    return f"{tm[3]:02d}:{tm[4]:02d}:{tm[5]:02d}"


async def _publish(topic, payload, qos=1):
    if not (net.wifi_ok and net.net_ok and net.time_ok and net.mqtt_ok):
        return False
    try:
        if isinstance(payload, dict):
            payload = json.dumps(payload)
        return await net.publish(topic, payload, qos=qos)
    except Exception as e:
        print(f"[{_ts()}] [logic] publish error:", type(e).__name__, e)
        return False


def _read_gc_info():
    try:
        return {
            "free": gc.mem_free(),
            "alloc": gc.mem_alloc(),
            "threshold": gc.threshold(),
        }
    except Exception as e:
        return {"error": f"{type(e).__name__}: {e}"}


def _read_heap_info():
    try:
        import esp32

        blocks = esp32.idf_heap_info(esp32.HEAP_DATA)
        result = []
        for block in blocks:
            size, free, alloc, largest = block
            frag_pct = (largest * 100 // free) if free > 0 else 0
            result.append(
                {
                    "size": size,
                    "free": free,
                    "alloc": alloc,
                    "largest_free": largest,
                    "frag_pct": frag_pct,
                }
            )
        return result
    except AttributeError as e:
        return {"error": f"AttributeError: {e}"}
    except Exception as e:
        return {"error": f"{type(e).__name__}: {e}"}


def _read_boot_counters():
    try:
        with open("/boot_count.json") as f:
            data = json.load(f)
            return data.get("count", 0), data.get("poweron_count", 0)
    except Exception:
        return 0, 0


def _collect_status():
    """Собирает всё для публикации."""
    gc.collect()
    bc, pc = _read_boot_counters()

    return {
        "version": VERSION,
        "uptime": time.ticks_diff(time.ticks_ms(), _start_ticks) // 1000,
        "ts": int(time.time()) + 946684800 if time.time() > 100000 else None,
        "time_ok": net.time_ok,
        "wifi_ok": net.wifi_ok,
        "net_ok": net.net_ok,
        "time_ok_flag": net.time_ok,
        "mqtt_ok": net.mqtt_ok,
        "all_ok": net.all_ok,
        "safe_mode": net.safe_mode,
        "boot_count": bc,
        "poweron_count": pc,
        "gc": _read_gc_info(),
        "heap": _read_heap_info(),
        "net_config": dict(net.config),
    }


async def _publish_status(reason):
    data = _collect_status()
    data["reason"] = reason
    await _publish("data/test/status", data)
    print(f"[{_ts()}] [logic] status published ({reason})")


async def _publish_mem():
    gc.collect()
    data = {
        "gc": _read_gc_info(),
        "heap": _read_heap_info(),
        "uptime": time.ticks_diff(time.ticks_ms(), _start_ticks) // 1000,
        "ts": int(time.time()) + 946684800 if time.time() > 100000 else None,
    }
    await _publish("data/test/mem", data)


async def on_command(payload):
    print(f"[{_ts()}] [logic] command:", payload)

    try:
        data = json.loads(payload)
    except Exception:
        return

    if not isinstance(data, dict):
        return

    action = data.get("action")

    if action == "status":
        await _publish_status("command")

    elif action == "mem":
        await _publish_mem()

    elif action == "config":
        # Отправить текущий net.config
        await _publish("data/test/net_config", dict(net.config))
        print(f"[{_ts()}] [logic] net_config published")


async def on_data(topic, payload):
    pass


async def run():
    global _start_ticks
    _start_ticks = time.ticks_ms()

    print(f"[{_ts()}] [logic] {VERSION} ЗАПУЩЕНА")

    net.on_command(on_command)
    net.on_data(on_data)

    # Ждём, пока net поднимется
    await asyncio.sleep(5)

    # Публикуем статус при старте
    try:
        await _publish_status("startup")
    except Exception as e:
        print(f"[{_ts()}] [logic] startup publish error:", type(e).__name__, e)

    last_status = time.ticks_ms()

    while True:
        if net.ota_pending:
            print(f"[{_ts()}] [logic] OTA pending")
            net.set_ota_safe(True)
            while net.ota_pending:
                await asyncio.sleep(1)
            net.set_ota_safe(False)

        # Периодический статус раз в STATUS_INTERVAL_SEC
        now_ms = time.ticks_ms()
        if time.ticks_diff(now_ms, last_status) >= STATUS_INTERVAL_SEC * 1000:
            try:
                await _publish_status("periodic")
            except Exception as e:
                print(f"[{_ts()}] [logic] periodic publish error:", type(e).__name__, e)
            last_status = now_ms

        await asyncio.sleep(1)
