import json
import random
import signal
import sys
import threading
import time
import os
from datetime import datetime, timedelta

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# Gestione GUI per input
try:
    import tkinter as tk
    from tkinter import simpledialog, messagebox
except ImportError:
    tk = None

# Gestione Tray Icon
try:
    import pystray
    from PIL import Image, ImageDraw
except ImportError:
    pystray = None

# ─── Configurazione Worker ─────────────────────────────────────────────────────
CONFIG_FILE = os.path.expanduser("~/.velk_worker.json")

def load_config():
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r") as f:
                return json.load(f)
        except:
            pass
    return {"NODE_NAME": "", "MASTER_URL": "https://velk.davidemarino.it"}

def save_config(cfg):
    try:
        with open(CONFIG_FILE, "w") as f:
            json.dump(cfg, f)
    except:
        pass

cfg = load_config()
NODE_NAME = cfg.get("NODE_NAME", "")
MASTER_URL = "http://velk.davidemarino.it"  # Forza sempre l'URL corretto, ignorando cache vecchie
MASTER_SECRET = "velk-botnet-2026"

# ─── Configurazione Sondaggio ──────────────────────────────────────────────────
POLL_ID = 592
ANSWER_ID = 842
AJAX_URL = "https://www.premiomiamartini.it/wp-admin/admin-ajax.php"

DEFAULT_INTERVAL = 1800       
INTERVAL_SIGMA = 120          
INTERVAL_MIN = 1830           
INTERVAL_MAX = 2100           
LONG_PAUSE_CHANCE = 0.05      
LONG_PAUSE_MIN = 2700         
LONG_PAUSE_MAX = 3600         

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
]

class WorkerState:
    def __init__(self):
        self.lock = threading.RLock()
        self.votes_ok = 0
        self.votes_blocked = 0
        self.errors = 0
        self.status = "Attivo"
        self.last_ok_vote_time = None
        self.next_vote_time = None
        
        # Carica ultimo voto da config
        cfg = load_config()
        t_str = cfg.get("LAST_OK_VOTE_TIME")
        if t_str:
            try:
                self.last_ok_vote_time = datetime.fromisoformat(t_str)
            except:
                pass
        self.status = "Avviato"
        self.recent_logs = []
        self.is_running = True
        self.is_paused = False

    def log(self, msg):
        with self.lock:
            ts = datetime.now().strftime("%H:%M:%S")
            log_entry = f"[{ts}] {msg}"
            self.recent_logs.append(log_entry)
            print(log_entry)
            if len(self.recent_logs) > 10:
                self.recent_logs.pop(0)

state = WorkerState()

def get_wait_time():
    if random.random() < LONG_PAUSE_CHANCE:
        wait_s = random.uniform(LONG_PAUSE_MIN, LONG_PAUSE_MAX)
        state.log("☕ Scelta pausa lunga casuale.")
    else:
        wait_s = random.gauss(DEFAULT_INTERVAL, INTERVAL_SIGMA)
        if wait_s < INTERVAL_MIN:
            wait_s = INTERVAL_MIN
        elif wait_s > INTERVAL_MAX:
            wait_s = INTERVAL_MAX
    return wait_s

def send_telemetry():
    if not MASTER_URL or "INSERISCI-TUO-DOMINIO" in MASTER_URL:
        return
        
    with state.lock:
        now = datetime.now()
        uptime = now - state.start_time
        uptime_str = f"{uptime.days}d {uptime.seconds//3600}h {(uptime.seconds//60)%60}m"
        next_ts = int(state.next_vote_time.timestamp()) if state.next_vote_time else None
        
        payload = {
            "node_name": NODE_NAME,
            "votes_ok": state.votes_ok,
            "votes_blocked": state.votes_blocked,
            "errors": state.errors,
            "next_vote_time": next_ts,
            "uptime": uptime_str,
            "status": state.status,
            "logs": state.recent_logs.copy()
        }
        state.recent_logs.clear()

    try:
        resp = requests.post(
            f"{MASTER_URL.rstrip('/')}/api/telemetry",
            json=payload,
            headers={"Authorization": f"Bearer {MASTER_SECRET}"},
            timeout=5
        )
        if resp.status_code == 200:
            data = resp.json()
            with state.lock:
                state.is_paused = data.get("is_paused", False)
    except Exception:
        pass

def send_vote(session):
    headers_get = {
        "User-Agent": random.choice(USER_AGENTS),
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
        "Accept-Language": "it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3",
    }
    
    poll_url = "https://www.premiomiamartini.it/velk/"
    
    try:
        r_get = session.get(poll_url, headers=headers_get, timeout=15)
        
        # Pre-check: il sondaggio è attivo?
        html = r_get.text
        if "dem-vote" not in html and "dem-button" not in html:
            with state.lock:
                state.status = "Sondaggio Chiuso"
            state.log("🛑 Pulsante non trovato. Sondaggio chiuso. Nessun POST inviato.")
            return False
            
        # Pulisci cookie democracy
        for cookie in list(session.cookies):
            if cookie.name.startswith("demPoll_"):
                session.cookies.clear(domain="", path="/", name=cookie.name)
                
        # Cache check per invisibilità (getVotedIds)
        check_data = {
            "action": "dem_ajax",
            "dem_act": "getVotedIds",
            "dem_pid": str(POLL_ID)
        }
        headers_post = headers_get.copy()
        headers_post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
        headers_post["X-Requested-With"] = "XMLHttpRequest"
        headers_post["Origin"] = "https://www.premiomiamartini.it"
        headers_post["Referer"] = poll_url
        
        check_resp = session.post(AJAX_URL, data=check_data, headers=headers_post, timeout=15)
        if check_resp.text.strip() != "":
            with state.lock:
                state.votes_blocked += 1
                state.status = "Voto Bloccato (IP)"
            state.log("⚠️ Limiti di voto superati (rilevato da cache).")
            return False

        # Se arriviamo qui, inviamo il vero voto
        vote_data = {
            "action": "dem_ajax",
            "dem_act": "vote",
            "dem_pid": str(POLL_ID),
            "answer_ids": str(ANSWER_ID)
        }
        r_post = session.post(AJAX_URL, data=vote_data, headers=headers_post, timeout=15)
        
    except Exception as e:
        with state.lock:
            state.errors += 1
            state.status = "Errore Connessione"
        state.log(f"Errore POST: {e}")
        return False

    if r_post.status_code == 200:
        body = r_post.text
        if "Limiti di voto superati" in body or "has been reached" in body or "voting is blocked" in body or "Già votato" in body:
            with state.lock:
                state.votes_blocked += 1
                state.status = "Voto Bloccato (IP)"
            state.log("⚠️ Limiti di voto superati.")
            return False
        elif "dem-voted-this" in body or "dem-fill" in body or "dem-answers" in body:
            with state.lock:
                state.votes_ok += 1
                state.status = "Voto INVIATO! 🎉"
                state.last_ok_vote_time = datetime.now()
            
            # Salva timestamp per evitare loop ai riavvii
            cfg = load_config()
            cfg["LAST_OK_VOTE_TIME"] = state.last_ok_vote_time.isoformat()
            save_config(cfg)
            
            state.log("✅ VOTO REGISTRATO CON SUCCESSO!")
            return True
        else:
            with state.lock:
                state.errors += 1
                state.status = "Errore Sconosciuto"
            state.log("❌ Voto fallito: Risposta imprevista dal server")
            return False
    else:
        with state.lock:
            state.errors += 1
            state.status = f"Errore HTTP {r_post.status_code}"
        state.log(f"❌ Errore server {r_post.status_code}")
        return False

def voter_thread_func():
    session = requests.Session()
    retries = Retry(total=3, backoff_factor=1, status_forcelist=[500,502,503,504])
    session.mount('https://', HTTPAdapter(max_retries=retries))
    
    state.log("🚀 Worker Avviato")
    
    # Attesa preventiva se riavviato durante il cooldown
    with state.lock:
        last_ok = state.last_ok_vote_time
    if last_ok:
        now = datetime.now()
        elapsed = (now - last_ok).total_seconds()
        if elapsed < 1800:
            remaining = 1800 - elapsed
            wait_s = remaining + random.randint(10, 30)
            state.log(f"⏳ Riavvio in cooldown. Attesa preventiva: mancano {wait_s/60:.1f} min")
            with state.lock:
                state.next_vote_time = now + timedelta(seconds=wait_s)
                state.status = "Attivo"
            send_telemetry()
            for _ in range(int(wait_s)):
                if not state.is_running: break
                time.sleep(1)
    
    # Sincronizza lo stato dal master PRIMA del ciclo
    send_telemetry()

    while state.is_running:
        if state.is_paused:
            with state.lock:
                state.status = "In Pausa"
            send_telemetry()
            for _ in range(5):
                if not state.is_running: break
                time.sleep(1)
            continue

        success = send_vote(session)
        
        wait_s = get_wait_time()
        with state.lock:
            state.next_vote_time = datetime.now() + timedelta(seconds=wait_s)
            state.status = "Attivo"
            
        state.log(f"⏳ Attesa: mancano {wait_s/60:.1f} min")
        send_telemetry()
        
        for _ in range(int(wait_s)):
            if not state.is_running:
                break
            time.sleep(1)

def ask_name_gui():
    if not tk:
        return f"Amico-{random.randint(1000,9999)}"
    root = tk.Tk()
    root.withdraw()
    nome = simpledialog.askstring("VELK Auto-Voter", "👉 Inserisci il tuo nome (es. Marco):")
    if not nome:
        nome = f"Amico-{random.randint(1000,9999)}"
    return nome.strip()

def create_image():
    # Crea un'icona verde 64x64
    image = Image.new('RGB', (64, 64), color=(0, 200, 0))
    d = ImageDraw.Draw(image)
    d.text((10,25), "VELK", fill=(255,255,255))
    return image

def setup_tray(voter_thread):
    if not pystray:
        voter_thread.join()
        return

    def on_exit(icon, item):
        state.is_running = False
        icon.stop()
        
    def show_status(icon, item):
        messagebox.showinfo("VELK Auto-Voter", f"Stato: {state.status}\nVoti Inviati: {state.votes_ok}\nProssimo Voto: {state.next_vote_time.strftime('%H:%M:%S') if state.next_vote_time else '-'}")

    menu = pystray.Menu(
        pystray.MenuItem('Visualizza Stato', show_status),
        pystray.MenuItem('Esci', on_exit)
    )
    
    icon = pystray.Icon("VELK", create_image(), "VELK Auto-Voter", menu)
    icon.run()

if __name__ == "__main__":
    if not NODE_NAME:
        NODE_NAME = ask_name_gui()
        cfg["NODE_NAME"] = NODE_NAME
        save_config(cfg)
        
    voter_thread = threading.Thread(target=voter_thread_func, daemon=True)
    voter_thread.start()
    
    setup_tray(voter_thread)
    sys.exit(0)
