"""
Integrity Watchdog - Mavis 2026-07-20
Controlla che i file delle regole Charter non vengano modificati da nessuno.
Snapshot SHA256 in _FROZEN_RULES_v1.json. Se cambia, log CRITICAL + alert.

File protetti (regole Charter):
- webhook_receiver.py (P006, P007, sizing, SL/TP)
- sltp_engine.py (P006, P005, P013)
- bybit_demo_client.py (round_qty, set_trading_stop trailing)
- trailing_stop_watchdog.py (trailing stop logic)
- charter_core/paletti.py (P001-P014)
- charter_core/charter_config.py (P015)
- charter_core/charter_engine.py
"""
import os
import json
import time
import hashlib
import logging
import sys

SNAPSHOT_FILE = r"/opt/charter-live/live_deploy/_FROZEN_RULES_v1.json"
LOG_FILE = r"/opt/charter-live/live_deploy/logs/integrity_watchdog.log"
PID_FILE = r"/tmp/integrity_watchdog.pid"
POLL_SECONDS = 300  # 5 minuti

logging.basicConfig(
    filename=LOG_FILE,
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
log = logging.getLogger()


def sha256(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            h.update(chunk)
    return h.hexdigest().lower()  # normalize to lowercase per confronto


def load_snapshot() -> dict:
    # utf-8-sig handles BOM that PowerShell ConvertTo-Json writes
    with open(SNAPSHOT_FILE, "r", encoding="utf-8-sig") as f:
        return json.load(f)


def main_loop():
    log.info("=== INTEGRITY WATCHDOG STARTED (Mavis 2026-07-20) ===")
    log.info("Snapshot: %s", SNAPSHOT_FILE)
    log.info("Poll interval: %ds", POLL_SECONDS)
    with open(PID_FILE, "w") as f:
        f.write(str(os.getpid()))

    snapshot = load_snapshot()
    files = {f["path"]: f["sha256"] for f in snapshot["files"]}
    log.info("Loaded %d frozen files (version %s, created %s)",
             len(files), snapshot.get("version", "?"), snapshot.get("created", "?"))

    while True:
        try:
            changes = []
            for path, expected_sha in files.items():
                if not os.path.exists(path):
                    changes.append({"path": path, "type": "MISSING", "expected": expected_sha, "actual": None})
                    continue
                actual = sha256(path)
                if actual != expected_sha:
                    changes.append({
                        "path": path,
                        "type": "MODIFIED",
                        "expected": expected_sha,
                        "actual": actual,
                    })
            if changes:
                log.critical("=" * 70)
                log.critical("INTEGRITY VIOLATION: %d file(s) modified!", len(changes))
                for c in changes:
                    log.critical("  %s: %s", c["type"], c["path"])
                    log.critical("    expected: %s", c["expected"][:16] + "...")
                    log.critical("    actual:   %s", (c["actual"] or "MISSING")[:16] + "...")
                log.critical("=" * 70)
                # Stampa anche su stderr per catturare l'attenzione
                print("INTEGRITY VIOLATION:", changes, file=sys.stderr)
            else:
                log.info("CHECK OK: %d files unchanged", len(files))
        except Exception as e:
            log.error("watchdog iteration error: %s", e)
        time.sleep(POLL_SECONDS)


if __name__ == "__main__":
    try:
        main_loop()
    except KeyboardInterrupt:
        log.info("WATCHDOG STOPPED (KeyboardInterrupt)")
        sys.exit(0)
