import time
import json
import os
import random
import logging
import sqlite3
import threading
import urllib.request
from urllib.parse import urlparse, parse_qs, urlencode
from curl_cffi import requests
import telebot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton

# --- CONFIGURAZIONE ---
TOKEN = "8749454736:AAHW3dVtg2DoNFLhg2xwK-cV6tUsnanvbwk"
ALLOWED_CHAT_IDS = [1640434336, 6678228625]
ADMIN_CHAT_ID = 1640434336

ollama_semaphore = threading.Semaphore(1)

# --- FUNZIONE CLASSICA CON LLAMA3 (OLLAMA) ---



def fetch_item_details(scraper, item_id):
    if str(item_id).startswith("subito_"): return None
    url = f"https://www.vinted.it/api/v2/items/{item_id}"
    try:
        resp = scraper.session.get(url, headers=scraper.headers, impersonate='chrome110', timeout=10)
        if resp.status_code == 200:
            return resp.json().get('item', {})
    except Exception as e:
        logging.error(f"Errore fetch dettagli {item_id}: {e}")
    return {}

def call_ollama(prompt, model="llama3:latest", format_json=False, image_b64=None):
    url = "http://localhost:11434/api/generate"
    data = {
        "model": model,
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": 0.0}
    }
    if format_json:
        data["format"] = "json"
    if image_b64:
        data["images"] = [image_b64]
        
    try:
        req = urllib.request.Request(
            url, 
            data=json.dumps(data).encode('utf-8'), 
            headers={'Content-Type': 'application/json'}
        )
        with urllib.request.urlopen(req, timeout=120) as response:
            result = json.loads(response.read().decode('utf-8'))
            return result.get('response', '').strip()
    except Exception as e:
        logging.error(f"Errore Ollama ({model}): {e}")
        return None

def process_with_ai_async(bot, db, scraper, item, search):
    item_id = str(item.get("id"))
    title = item.get("title", "")
    chat_id = search['chat_id']
    
    price_val = 0.0
    try:
        price_str = item.get("price", {}).get("amount")
        if price_str:
            price_val = float(price_str)
    except: pass
        
    clean_query = search['query'].replace("Link: ", "").lower()
    
    # --- 0. HARD BLOCKS (Filtro Deterministic 100%) ---
    # Se il titolo contiene parole chiare di giochi o accessori, scartiamo senza scomodare l'AI
    title_lower = title.lower()
    blocked_words = ['gioco', 'jeu', 'juego', 'game', 'fifa', 'spiderman', 'call of duty', 'gta', 'manette', 'controller', 'dualsense', 'cuffie', 'headset', 'scatola', 'boite', 'caja', 'box', 'ricambio', 'pezzi', 'stand', 'base', 'cover', 'faceplate', 'adesivo']
    if 'ps5' in clean_query or 'playstation' in clean_query:
        if any(w in title_lower for w in blocked_words):
            logging.info(f"HARD BLOCK scarta: '{title}' (Parola bloccata trovata)")
            return

    # --- 1. FIRST PASS: Fast Title Check ---
    prompt_1 = (
        f"Titolo annuncio: '{title}'.\\n"
        f"L'utente sta cercando il dispositivo hardware PRINCIPALE '{clean_query}'.\\n"
        f"Rispondi SOLO con un JSON valido strutturato in questo modo:\\n"
        f"{{\"reasoning\": \"spiega qui in 1 frase se l'oggetto è davvero il dispositivo principale o se è solo un gioco/accessorio\", \"category\": \"[INSERISCI CATEGORIA]\"}}\\n"
        f"Le categorie possibili sono SOLO:\\n"
        f"- 'CONSOLE' (se vende ESATTAMENTE la console/dispositivo)\\n"
        f"- 'GIOCO' (se è un videogioco per quella console)\\n"
        f"- 'ACCESSORIO' (cavi, joystick, cuffie)\\n"
        f"- 'ALTRO' (scatole, cover, ricambi)\\n"
        f"ATTENZIONE: Se il titolo contiene il nome di un videogioco, la categoria è 'GIOCO'!"
    )
    
    with ollama_semaphore:
        res_1 = call_ollama(prompt_1, format_json=True)
        
    if not res_1:
        logging.warning("Ollama timeout/vuoto al primo pass.")
        return
        
    try:
        parsed = json.loads(res_1)
        if parsed.get('category', 'ALTRO') != 'CONSOLE':
            logging.info(f"AI(Title) scarta: '{title}' (Ragionamento: {parsed.get('reasoning', '')})")
            return
    except: 
        return
    
    # --- 2. FETCH DETAILS ---
    full_item = fetch_item_details(scraper, item_id)
    if not full_item:
        full_item = item
    
    # --- 5. SEND NOTIFICATION ---
    ai_data = {"deal_score": "NORMALE", "message": ""}
    send_notification_advanced(bot, chat_id, full_item, search, ai_data)

def send_notification_advanced(bot, chat_id, item, search, ai_data):
    title = item.get("title", "Titolo non disponibile")
    price_val = item.get("price", {}).get("amount", "N/D")
    currency = item.get("price", {}).get("currency_code", "EUR")
    condition = item.get("status", "N/D")
    user_login = item.get("user", {}).get("login", "Anonimo")
    item_url = item.get("url")
    if not item_url:
        item_url = f"https://www.vinted.it/items/{item.get('id')}"
    elif not item_url.startswith("http"):
        item_url = f"https://www.vinted.it{item_url}"
        
    photo_url = None
    if item.get("photos") and len(item.get("photos")) > 0:
        photo_url = item.get("photos")[0].get("full_size_url") or item.get("photos")[0].get("url")

    score_badge = "🟢 NORMALE"
    score = ai_data.get('deal_score', 'NORMALE')
    if score == 'OTTIMO': score_badge = "🟡 OTTIMO PREZZO"
    if score == 'AFFARE ASSURDO': score_badge = "🔥 AFFARE ASSURDO!"

    msg = (
        f"🛍️ <b>Nuovo su {item.get('platform', 'Vinted')}:</b> <code>{search['query']}</code>\n\n"
        f"📌 <b>Prodotto:</b> {title}\n"
        f"💰 <b>Prezzo:</b> {price_val} {currency}\n"
        f"✨ <b>Condizione:</b> {condition}\n"
        f"👤 <b>Venditore:</b> {user_login}\n\n"
        f"🤖 <b>AI Deal Score:</b> {score_badge}\n"
        f"💬 <b>Messaggio Consigliato:</b> <code>{ai_data.get('message', '')}</code>\n\n"
        f"🔗 <a href='{item_url}'>Visualizza su {item.get('platform', 'Vinted')}</a>"
    )

    import telebot.types as types
    markup = types.InlineKeyboardMarkup()
    btn_link = types.InlineKeyboardButton("🔗 Apri Annuncio", url=item_url)
    btn_chat = types.InlineKeyboardButton("💬 Invia Messaggio Rapido", url=f"https://www.vinted.it/items/{item.get('id')}/want_it/new")
    markup.add(btn_link, btn_chat)

    try:
        if photo_url:
            try:
                bot.send_photo(chat_id, photo_url, caption=msg, parse_mode="HTML", reply_markup=markup)
            except Exception as e:
                logging.error(f"Errore send_photo (probabile blocco URL da parte di Telegram): {e}. Fallback a text.")
                bot.send_message(chat_id, msg, parse_mode="HTML", reply_markup=markup, disable_web_page_preview=True)
        else:
            bot.send_message(chat_id, msg, parse_mode="HTML", reply_markup=markup, disable_web_page_preview=True)
        
        # Segna come inviato solo se l'invio su Telegram va a buon fine
        # db.mark_item_as_sent() dovrebbe essere chiamato qui per sicurezza, o dal chiamante. 
        # Siccome era chiamato prima nel loop, va bene.
    except Exception as e:
        logging.error(f"Errore critico invio notifica: {e}")

# --- CLASSE DATABASE ---
class Database:
    def __init__(self, db_path="/home/ubuntu/vinted_tracker/tracker.db"):
        self.db_path = db_path
        self.init_db()

    def get_conn(self):
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        return conn

    def init_db(self):
        os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
        with self.get_conn() as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS searches (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    chat_id INTEGER DEFAULT 1640434336,
                    query TEXT,
                    min_price REAL DEFAULT 0,
                    max_price REAL DEFAULT NULL,
                    enabled INTEGER DEFAULT 1,
                    url_params TEXT DEFAULT NULL,
                    exclude_keywords TEXT DEFAULT NULL,
                    must_keywords TEXT DEFAULT NULL,
                    use_ai INTEGER DEFAULT 0,
                    UNIQUE(chat_id, query)
                )
            """)
            conn.execute("""
                CREATE TABLE IF NOT EXISTS sent_items (
                    item_id TEXT,
                    search_id INTEGER,
                    title TEXT,
                    price REAL,
                    url TEXT,
                    sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    PRIMARY KEY (item_id, search_id)
                )
            """)
            conn.execute("""
                CREATE TABLE IF NOT EXISTS settings (
                    chat_id INTEGER,
                    key TEXT,
                    value TEXT,
                    PRIMARY KEY (chat_id, key)
                )
            """)
            
            # Impostazioni di default
            conn.execute("INSERT OR IGNORE INTO settings (chat_id, key, value) VALUES (0, 'check_interval', '60')")
            conn.execute("INSERT OR IGNORE INTO settings (chat_id, key, value) VALUES (1640434336, 'paused', '0')")
            conn.execute("INSERT OR IGNORE INTO settings (chat_id, key, value) VALUES (6678228625, 'paused', '0')")
            conn.commit()

    def add_search(self, chat_id, query, min_price=0, max_price=None, url_params=None):
        try:
            with self.get_conn() as conn:
                cursor = conn.cursor()
                cursor.execute(
                    "INSERT INTO searches (chat_id, query, min_price, max_price, url_params) VALUES (?, ?, ?, ?, ?)",
                    (chat_id, query, min_price, max_price, url_params)
                )
                conn.commit()
                return cursor.lastrowid
        except sqlite3.IntegrityError:
            return None

    def get_searches(self, chat_id):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT * FROM searches WHERE chat_id = ?", (chat_id,))
            return [dict(row) for row in cursor.fetchall()]
            
    def get_all_searches_admin(self):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT * FROM searches ORDER BY chat_id, id")
            return [dict(row) for row in cursor.fetchall()]

    def get_active_searches(self):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT * FROM searches WHERE enabled = 1")
            return [dict(row) for row in cursor.fetchall()]

    def delete_search(self, chat_id, search_id):
        with self.get_conn() as conn:
            conn.execute("DELETE FROM searches WHERE id = ? AND chat_id = ?", (search_id, chat_id))
            conn.commit()

    def toggle_search(self, chat_id, search_id):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT enabled FROM searches WHERE id = ? AND chat_id = ?", (search_id, chat_id))
            row = cursor.fetchone()
            if row:
                new_state = 0 if row['enabled'] == 1 else 1
                conn.execute("UPDATE searches SET enabled = ? WHERE id = ?", (new_state, search_id))
                conn.commit()
                return new_state
            return None

    def toggle_search_ai(self, chat_id, search_id):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT use_ai FROM searches WHERE id = ? AND chat_id = ?", (search_id, chat_id))
            row = cursor.fetchone()
            if row:
                new_state = 0 if row['use_ai'] == 1 else 1
                conn.execute("UPDATE searches SET use_ai = ? WHERE id = ?", (new_state, search_id))
                conn.commit()
                return new_state
            return None

    def update_search_prices(self, chat_id, search_id, min_price, max_price):
        with self.get_conn() as conn:
            conn.execute(
                "UPDATE searches SET min_price = ?, max_price = ? WHERE id = ? AND chat_id = ?",
                (min_price, max_price, search_id, chat_id)
            )
            conn.commit()

    def update_search_keywords(self, chat_id, search_id, key_type, keywords):
        field = 'exclude_keywords' if key_type == 'exclude' else 'must_keywords'
        val = keywords.strip() if keywords else None
        with self.get_conn() as conn:
            conn.execute(f"UPDATE searches SET {field} = ? WHERE id = ? AND chat_id = ?", (val, search_id, chat_id))
            conn.commit()

    def is_item_sent(self, item_id, search_id):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT 1 FROM sent_items WHERE item_id = ? AND search_id = ?", (str(item_id), search_id))
            return cursor.fetchone() is not None

    def mark_item_as_sent(self, item_id, search_id, title, price, url):
        with self.get_conn() as conn:
            conn.execute(
                "INSERT OR IGNORE INTO sent_items (item_id, search_id, title, price, url) VALUES (?, ?, ?, ?, ?)",
                (str(item_id), search_id, title, price, url)
            )
            conn.commit()

    def get_setting(self, chat_id, key, default=None):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT value FROM settings WHERE chat_id = ? AND key = ?", (chat_id, key))
            row = cursor.fetchone()
            return row['value'] if row else default

    def set_setting(self, chat_id, key, value):
        with self.get_conn() as conn:
            conn.execute(
                "INSERT OR REPLACE INTO settings (chat_id, key, value) VALUES (?, ?, ?)",
                (chat_id, key, str(value))
            )
            conn.commit()

    def get_stats(self, chat_id):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            
            cursor.execute("""
                SELECT COUNT(*) as total FROM sent_items 
                JOIN searches ON sent_items.search_id = searches.id 
                WHERE searches.chat_id = ?
            """, (chat_id,))
            total_sent = cursor.fetchone()['total']
            
            cursor.execute("""
                SELECT COUNT(*) as total FROM sent_items 
                JOIN searches ON sent_items.search_id = searches.id 
                WHERE searches.chat_id = ? AND sent_items.sent_at >= datetime('now', '-1 day')
            """, (chat_id,))
            sent_24h = cursor.fetchone()['total']
            
            cursor.execute("SELECT COUNT(*) as total FROM searches WHERE enabled = 1 AND chat_id = ?", (chat_id,))
            active_searches = cursor.fetchone()['total']
            
            cursor.execute("SELECT COUNT(*) as total FROM searches WHERE chat_id = ?", (chat_id,))
            total_searches = cursor.fetchone()['total']
            
            return {
                "total_sent": total_sent,
                "sent_24h": sent_24h,
                "active_searches": active_searches,
                "total_searches": total_searches
            }
            
    def get_global_stats(self):
        with self.get_conn() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT COUNT(DISTINCT chat_id) as users FROM searches")
            users = cursor.fetchone()['users']
            
            cursor.execute("SELECT COUNT(*) as total FROM sent_items")
            total_sent = cursor.fetchone()['total']
            
            cursor.execute("SELECT COUNT(*) as total FROM searches")
            total_searches = cursor.fetchone()['total']
            
            return {
                "users": users,
                "total_sent": total_sent,
                "total_searches": total_searches
            }

# --- CLASSE SCRAPER VINTED ---
class VintedScraper:
    def __init__(self, db):
        self.db = db
        self.session = None
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
            'Accept-Language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
            'Accept-Encoding': 'gzip, deflate, br',
            'Connection': 'keep-alive'
        }
        self.last_cookie_update = 0
        self.init_session()

    def init_session(self):
        logging.info("Inizializzazione sessione Vinted e recupero cookie...")
        try:
            self.session = requests.Session()
            resp = self.session.get('https://www.vinted.it', headers=self.headers, impersonate='chrome120', timeout=15)
            if resp.status_code == 200:
                self.last_cookie_update = time.time()
                logging.info("Sessione Vinted inizializzata con successo.")
                return True
            else:
                logging.error(f"Impossibile inizializzare sessione Vinted, status: {resp.status_code}")
                return False
        except Exception as e:
            logging.error(f"Errore durante inizializzazione sessione Vinted: {e}")
            return False

    def fetch_items(self, search):
        if time.time() - self.last_cookie_update > 1800 or not self.session:
            self.init_session()

        api_headers = self.headers.copy()
        
        query = search['query']
        url_params_str = search.get('url_params')

        if url_params_str:
            try:
                params = json.loads(url_params_str)
            except Exception:
                params = {'search_text': query}
        else:
            params = {'search_text': query}

        params['order'] = 'newest_first'
        params['per_page'] = '20'
        
        search_ref = params.get('search_text', [''])[0] if isinstance(params.get('search_text'), list) else params.get('search_text', '')
        api_headers['Referer'] = f'https://www.vinted.it/catalog?search_text={search_ref}'
        api_headers['Accept'] = 'application/json, text/plain, */*'

        url = 'https://www.vinted.it/api/v2/catalog/items?' + urlencode(params, doseq=True)
        
        try:
            resp = self.session.get(url, headers=api_headers, impersonate='chrome120', timeout=15)
            if resp.status_code in [401, 403]:
                logging.warning(f"Ricevuto {resp.status_code} da Vinted. Tento di rinfrescare la sessione...")
                if self.init_session():
                    resp = self.session.get(url, headers=api_headers, impersonate='chrome120', timeout=15)
            
            if resp.status_code != 200:
                logging.error(f"Errore API Vinted per '{query}' (Utente {search.get('chat_id')}): {resp.status_code}")
                return None
                
            data = resp.json()
            return data.get('items', [])
        except Exception as e:
            logging.error(f"Eccezione durante lo scraping per query '{query}': {e}")
            return None

# --- FUNZIONE DI NOTIFICA TELEGRAM ---
def send_notification(bot, chat_id, item, search):
    title = item.get("title", "Titolo non disponibile")
    price_val = item.get("price", {}).get("amount", "N/D")
    currency = item.get("price", {}).get("currency_code", "EUR")
    
    total_price = item.get("total_item_price", {}).get("amount", price_val)
    
    condition = item.get("status", "N/D")
    user_login = item.get("user", {}).get("login", "Anonimo")
    item_url = item.get("url")
    if not item_url:
        item_url = f"https://www.vinted.it/items/{item.get('id')}"
    elif not item_url.startswith("http"):
        item_url = f"https://www.vinted.it{item_url}"
        
    photo_url = None
    if item.get("photo"):
        photo_url = item.get("photo", {}).get("full_size_url") or item.get("photo", {}).get("url")
    elif item.get("photos") and len(item.get("photos")) > 0:
        photo_url = item.get("photos")[0].get("full_size_url") or item.get("photos")[0].get("url")

    msg = (
        f"🛍️ <b>Nuovo articolo trovato per:</b> <code>{search['query']}</code>\n\n"
        f"📌 <b>Prodotto:</b> {title}\n"
        f"💰 <b>Prezzo:</b> {price_val} {currency} <i>(Totale: {total_price} {currency} con comm.)</i>\n"
        f"✨ <b>Condizione:</b> {condition}\n"
        f"👤 <b>Venditore:</b> {user_login}\n\n"
        f"🔗 <a href='{item_url}'>Visualizza su Vinted</a>"
    )

    markup = InlineKeyboardMarkup()
    btn_link = InlineKeyboardButton("🔗 Apri Annuncio", url=item_url)
    btn_chat = InlineKeyboardButton("💬 Contatta Venditore", url=f"https://www.vinted.it/items/{item.get('id')}/want_it/new")
    markup.add(btn_link, btn_chat)

    try:
        if photo_url:
            bot.send_photo(chat_id, photo_url, caption=msg, parse_mode="HTML", reply_markup=markup)
        else:
            bot.send_message(chat_id, msg, parse_mode="HTML", reply_markup=markup, disable_web_page_preview=True)
    except Exception as e:
        logging.error(f"Errore durante l'invio della notifica Telegram all'utente {chat_id}: {e}")

# --- FILTRA ARTICOLI IN BASE A FILTRI VELOCI ---
def filter_item_fast(item, search):
    price_val = 0.0
    try:
        price_str = item.get("price", {}).get("amount")
        if price_str:
            price_val = float(price_str)
    except Exception:
        pass
        
    min_price = search['min_price'] or 0.0
    max_price = search['max_price']
    
    if price_val < min_price:
        return False
    if max_price is not None and price_val > max_price:
        return False

    title_lower = item.get("title", "").lower()

    exclude_str = search.get('exclude_keywords')
    if exclude_str:
        exclude_list = [w.strip().lower() for w in exclude_str.split(",") if w.strip()]
        if any(w in title_lower for w in exclude_list):
            return False

    must_str = search.get('must_keywords')
    if must_str:
        must_list = [w.strip().lower() for w in must_str.split(",") if w.strip()]
        if not all(w in title_lower for w in must_list):
            return False

    return True

# --- THREAD ASINCRONO PER LA VERIFICA AI DI OLLAMA ---



class SubitoScraper:
    def __init__(self):
        self.session = requests.Session()
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Accept': 'application/json, text/plain, */*'
        }

    def fetch_items(self, search):
        query = search['query'].replace("Link: ", "")
        import urllib.parse
        url = f"https://hades.subito.it/v1/search/items?q={urllib.parse.quote(query)}"
        try:
            resp = self.session.get(url, headers=self.headers, impersonate='chrome110', timeout=15)
            if resp.status_code != 200:
                logging.error(f"Errore Subito API: {resp.status_code}")
                return None
            ads = resp.json().get('ads', [])
            normalized = []
            for ad in ads:
                price_str = "0"
                for feat in ad.get('features', []):
                    if feat.get('uri') == '/price':
                        price_str = feat['values'][0]['key']
                        break
                price_val = float(price_str.replace(',', '.'))
                
                cond_str = "N/D"
                for feat in ad.get('features', []):
                    if feat.get('uri') == '/item_condition':
                        cond_str = feat['values'][0]['value']
                        break
                
                photo_url = None
                if ad.get('images'):
                    base_img = ad['images'][0].get('cdn_base_url')
                    if base_img: photo_url = f"{base_img}-rule=gallery-600-auto.jpg"
                
                user_name = ad.get('advertiser', {}).get('name', '')
                if not user_name: user_name = 'Utente Subito'

                normalized.append({
                    "id": f"subito_{ad['urn']}", 
                    "title": ad.get('subject', ''),
                    "price": {"amount": price_val, "currency_code": "EUR"},
                    "status": cond_str,
                    "user": {"login": user_name},
                    "url": ad.get('urls', {}).get('default', ''),
                    "description": ad.get('body', ''),
                    "photos": [{"full_size_url": photo_url}] if photo_url else [],
                    "platform": "Subito.it"
                })
            return normalized
        except Exception as e:
            logging.error(f"Eccezione SubitoScraper: {e}")
            return None

# --- LOOP DI SCANSIONE IN BACKGROUND ---
def tracker_loop(bot, db, scrapers):
    logging.info("Avvio thread del ciclo di scraping...")
    
    while True:
        try:
            active_searches = db.get_active_searches()
            if not active_searches:
                time.sleep(10)
                continue
                
            for search in active_searches:
                chat_id = search['chat_id']
                
                # Controlla se questo specifico utente ha messo in pausa
                if db.get_setting(chat_id, "paused", "0") == "1":
                    continue
                    
                query = search['query']
                logging.info(f"Controllo per utente {chat_id}: '{query}'")
                
                items = []
                for s_scraper in scrapers:
                    s_items = s_scraper.fetch_items(search)
                    if s_items: items.extend(s_items)
                if not items:
                    continue
                    
                new_items_count = 0
                for item in items:
                    item_id = str(item.get("id"))
                    search_id = search['id']
                    
                    if db.is_item_sent(item_id, search_id):
                        continue
                        
                    if not filter_item_fast(item, search):
                        continue
                    
                    if search.get('use_ai') == 1:
                        price_val = 0.0
                        try:
                            price_val = float(item.get("price", {}).get("amount", 0.0))
                        except Exception:
                            pass
                        db.mark_item_as_sent(item_id, search_id, item.get("title"), price_val, item.get("url"))
                        
                        threading.Thread(
                            target=process_with_ai_async, 
                            args=(bot, db, scrapers[0], item, search),
                            daemon=True
                        ).start()
                    else:
                        send_notification(bot, chat_id, item, search)
                        price_val = 0.0
                        try:
                            price_val = float(item.get("price", {}).get("amount", 0.0))
                        except Exception:
                            pass
                        db.mark_item_as_sent(item_id, search_id, item.get("title"), price_val, item.get("url"))
                    
                    new_items_count += 1
                    time.sleep(random.uniform(1.5, 3.0))
                    
                logging.info(f"Completato '{query}' (Utente {chat_id}): {new_items_count} nuovi articoli.")
                time.sleep(random.uniform(3, 7))
                
            # Intervallo globale
            interval = int(db.get_setting(0, "check_interval", "60"))
            time.sleep(interval)
            
        except Exception as e:
            logging.error(f"Errore non gestito nel tracker loop: {e}")
            time.sleep(30)

# --- CONFIGURAZIONE INTERFACCIA TELEGRAM ---
def setup_bot(bot, db, scraper):
    
    def owner_only(func):
        def wrapper(message, *args, **kwargs):
            if message.from_user.id not in ALLOWED_CHAT_IDS:
                logging.warning(f"Accesso negato a ID: {message.from_user.id}")
                bot.reply_to(message, "Non sei autorizzato ad utilizzare questo bot.")
                return
            return func(message, *args, **kwargs)
        return wrapper

    def owner_only_callback(func):
        def wrapper(call, *args, **kwargs):
            if call.from_user.id not in ALLOWED_CHAT_IDS:
                logging.warning(f"Callback negato a ID: {call.from_user.id}")
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
            return func(call, *args, **kwargs)
        return wrapper

    def is_admin(chat_id):
        return chat_id == ADMIN_CHAT_ID

    def send_main_menu(chat_id, message_id=None):
        paused = db.get_setting(chat_id, "paused", "0") == "1"
        status_emoji = "🔴 In Pausa" if paused else "🟢 Attivo"
        stats = db.get_stats(chat_id)
        
        text = (
            f"🤖 <b>Vinted Tracker Bot</b>\n\n"
            f"👤 Utente: <code>{chat_id}</code>\n"
            f"• Stato Tracker: <b>{status_emoji}</b>\n"
            f"• Ricerche attive: <b>{stats['active_searches']} / {stats['total_searches']}</b>\n"
            f"• Notifiche inviate: <b>{stats['total_sent']}</b> <i>({stats['sent_24h']} 24h)</i>\n\n"
            f"Gestisci le TUE ricerche in modo indipendente."
        )
        
        markup = InlineKeyboardMarkup(row_width=2)
        btn_add = InlineKeyboardButton("➕ Aggiungi Ricerca", callback_data="menu_add")
        btn_list = InlineKeyboardButton("📋 Lista Ricerche", callback_data="menu_list")
        
        toggle_label = "▶️ Riprendi" if paused else "⏸️ Pausa"
        btn_toggle = InlineKeyboardButton(toggle_label, callback_data="menu_toggle_state")
        
        btn_stats = InlineKeyboardButton("📊 Statistiche", callback_data="menu_stats")
        btn_logs = InlineKeyboardButton("📜 Log Globali", callback_data="menu_logs")
        btn_close = InlineKeyboardButton("❌ Chiudi", callback_data="menu_close")
        
        markup.add(btn_add, btn_list)
        markup.add(btn_toggle, btn_stats)
        markup.add(btn_logs, btn_close)
        
        if is_admin(chat_id):
            markup.add(InlineKeyboardButton("👑 Pannello Admin", callback_data="admin_menu"))
        
        if message_id:
            try:
                bot.edit_message_text(text, chat_id, message_id, parse_mode="HTML", reply_markup=markup)
            except Exception:
                bot.send_message(chat_id, text, parse_mode="HTML", reply_markup=markup)
        else:
            bot.send_message(chat_id, text, parse_mode="HTML", reply_markup=markup)

    @bot.message_handler(commands=['admin'])
    @owner_only
    def admin_cmd(message):
        if not is_admin(message.chat.id):
            bot.send_message(message.chat.id, "🚫 Accesso negato. Solo l'amministratore può usare questo comando.")
            return
        send_admin_menu(message.chat.id)

    def send_admin_menu(chat_id, message_id=None):
        stats = db.get_global_stats()
        text = (
            f"👑 <b>Pannello Amministratore GLOBALE</b>\n\n"
            f"• Utenti con ricerche: <b>{stats['users']}</b>\n"
            f"• Totale Ricerche nel DB: <b>{stats['total_searches']}</b>\n"
            f"• Totale Notifiche Inviate (tutti): <b>{stats['total_sent']}</b>\n\n"
            f"Da qui puoi gestire tutte le ricerche di tutti gli utenti in modo diretto."
        )
        markup = InlineKeyboardMarkup(row_width=1)
        markup.add(InlineKeyboardButton("👥 Tutte le Ricerche (Tutti gli Utenti)", callback_data="admin_list_all"))
        markup.add(InlineKeyboardButton("⬅️ Torna al tuo Menu", callback_data="menu_back_to_main"))
        
        if message_id:
            bot.edit_message_text(text, chat_id, message_id, parse_mode="HTML", reply_markup=markup)
        else:
            bot.send_message(chat_id, text, parse_mode="HTML", reply_markup=markup)

    def send_admin_list_all(chat_id, message_id):
        searches = db.get_all_searches_admin()
        if not searches:
            text = "Nessuna ricerca presente nel database globale."
            markup = InlineKeyboardMarkup()
            markup.add(InlineKeyboardButton("⬅️ Indietro", callback_data="admin_menu"))
            bot.edit_message_text(text, chat_id, message_id, reply_markup=markup)
            return

        text = "👑 <b>Lista Ricerche Globali (Admin):</b>"
        markup = InlineKeyboardMarkup(row_width=1)
        
        for s in searches:
            status_symbol = "🟢" if s['enabled'] == 1 else "🔴"
            user_label = "TUO" if s['chat_id'] == chat_id else str(s['chat_id'])
            btn_label = f"[{user_label}] {status_symbol} {s['query']}"
            markup.add(InlineKeyboardButton(btn_label, callback_data=f"search_detail_{s['id']}_{s['chat_id']}"))
            
        markup.add(InlineKeyboardButton("⬅️ Indietro", callback_data="admin_menu"))
        bot.edit_message_text(text, chat_id, message_id, parse_mode="HTML", reply_markup=markup)

    @bot.message_handler(commands=['start', 'menu'])
    @owner_only
    def start_cmd(message):
        send_main_menu(message.chat.id)

    @bot.message_handler(commands=['help'])
    @owner_only
    def help_cmd(message):
        text = (
            "🤖 <b>Comandi Disponibili:</b>\n\n"
            "/menu o /start - Pannello di controllo principale\n"
            "/add [query o link] - Aggiunge query o link Vinted completo\n"
            "/list - Mostra l'elenco delle tue ricerche salvate\n"
            "/pause - Sospende il tuo monitoraggio\n"
            "/resume - Riprende il tuo monitoraggio\n"
            "/setinterval [secondi] - Cambia l'intervallo GLOBALE tra controlli\n"
            "/logs - Mostra gli ultimi log di sistema\n"
            "/stats - Statistiche del bot"
        )
        if is_admin(message.chat.id):
            text += "\n/admin - Apre il Pannello Amministratore Globale"
            
        bot.send_message(message.chat.id, text, parse_mode="HTML")

    @bot.message_handler(commands=['add'])
    @owner_only
    def add_cmd(message):
        args = message.text.split(maxsplit=1)
        chat_id = message.chat.id
        if len(args) < 2:
            bot.send_message(chat_id, "Sintassi: `/add parola chiave o link`", parse_mode="Markdown")
            return
        
        input_data = args[1].strip()
        
        if input_data.startswith("http"):
            try:
                parsed = urlparse(input_data)
                q_params = parse_qs(parsed.query)
                if not q_params:
                    bot.send_message(chat_id, "⚠️ Il link fornito non sembra contenere parametri di ricerca validi.")
                    return
                
                search_text = q_params.get('search_text', [''])[0]
                if not search_text:
                    search_text = f"Link_{int(time.time())}"
                else:
                    search_text = f"Link: {search_text}"
                    
                search_id = db.add_search(chat_id, search_text, url_params=json.dumps(q_params))
                if search_id:
                    bot.send_message(chat_id, f"✅ Link Vinted tracciato come <code>{search_text}</code>!", parse_mode="HTML")
                else:
                    bot.send_message(chat_id, f"⚠️ Una ricerca simile o uguale è già tra le TUE ricerche.", parse_mode="HTML")
            except Exception as e:
                bot.send_message(chat_id, f"❌ Errore durante l'elaborazione del link: {e}")
        else:
            query = input_data
            search_id = db.add_search(chat_id, query)
            if search_id:
                bot.send_message(chat_id, f"✅ Ricerca per <code>{query}</code> aggiunta! Configura filtri via /menu.", parse_mode="HTML")
            else:
                bot.send_message(chat_id, f"⚠️ La ricerca per <code>{query}</code> è già tra le TUE ricerche.", parse_mode="HTML")

    @bot.message_handler(commands=['list'])
    @owner_only
    def list_cmd(message):
        send_list_menu(message.chat.id)

    @bot.message_handler(commands=['pause'])
    @owner_only
    def pause_cmd(message):
        db.set_setting(message.chat.id, "paused", "1")
        bot.send_message(message.chat.id, "⏸️ Monitoraggio messo in pausa per il tuo profilo.")

    @bot.message_handler(commands=['resume'])
    @owner_only
    def resume_cmd(message):
        db.set_setting(message.chat.id, "paused", "0")
        bot.send_message(message.chat.id, "▶️ Monitoraggio ripreso per il tuo profilo.")

    @bot.message_handler(commands=['setinterval'])
    @owner_only
    def setinterval_cmd(message):
        args = message.text.split()
        if len(args) < 2:
            bot.send_message(message.chat.id, "Sintassi: `/setinterval secondi`", parse_mode="Markdown")
            return
        try:
            seconds = int(args[1])
            if seconds < 10:
                seconds = 10
            db.set_setting(0, "check_interval", str(seconds)) # Settings globale = 0
            bot.send_message(message.chat.id, f"✅ Intervallo globale impostato a {seconds} secondi.")
        except ValueError:
            bot.send_message(message.chat.id, "Inserisci un numero intero valido.")

    @bot.message_handler(commands=['logs'])
    @owner_only
    def logs_cmd(message):
        send_logs(message.chat.id)

    @bot.message_handler(commands=['stats'])
    @owner_only
    def stats_cmd(message):
        send_stats(message.chat.id)

    def send_stats(chat_id):
        stats = db.get_stats(chat_id)
        text = (
            f"📊 <b>Le TUE Statistiche Vinted Tracker</b>\n\n"
            f"• Ricerche salvate: {stats['total_searches']}\n"
            f"• Ricerche attive: {stats['active_searches']}\n"
            f"• Notifiche totali inviate a te: {stats['total_sent']}\n"
            f"• Notifiche inviate a te (24h): {stats['sent_24h']}\n"
            f"• Intervallo globale: {db.get_setting(0, 'check_interval', '60')} secondi\n"
        )
        markup = InlineKeyboardMarkup()
        markup.add(InlineKeyboardButton("⬅️ Menu Principale", callback_data="menu_back_to_main"))
        bot.send_message(chat_id, text, parse_mode="HTML", reply_markup=markup)

    def send_logs(chat_id, message_id=None):
        log_path = "/home/ubuntu/vinted_tracker/tracker.log"
        if not os.path.exists(log_path):
            bot.send_message(chat_id, "Nessun file di log trovato.")
            return
        try:
            with open(log_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            last_lines = lines[-35:]
            log_text = "".join(last_lines)
            if len(log_text) > 4000:
                log_text = log_text[-4000:]
            
            markup = InlineKeyboardMarkup()
            btn_dl = InlineKeyboardButton("📂 Scarica File Log", callback_data="download_log_file")
            btn_close = InlineKeyboardButton("❌ Chiudi", callback_data="menu_close")
            markup.add(btn_dl, btn_close)
            
            bot.send_message(chat_id, f"📜 <b>Log recenti GLOBALI del Tracker:</b>\n<pre>{log_text}</pre>", parse_mode="HTML", reply_markup=markup)
        except Exception as e:
            bot.send_message(chat_id, f"Errore lettura log: {e}")

    def send_list_menu(chat_id, message_id=None):
        searches = db.get_searches(chat_id)
        if not searches:
            text = "⚠️ Non hai salvato nessuna ricerca. Clicca su '➕ Aggiungi Ricerca' per iniziare."
            markup = InlineKeyboardMarkup()
            markup.add(InlineKeyboardButton("➕ Aggiungi Ricerca", callback_data="menu_add"))
            markup.add(InlineKeyboardButton("⬅️ Menu Principale", callback_data="menu_back_to_main"))
            if message_id:
                bot.edit_message_text(text, chat_id, message_id, reply_markup=markup)
            else:
                bot.send_message(chat_id, text, reply_markup=markup)
            return

        text = "📋 <b>La TUA Lista Ricerche:</b>\nSeleziona una voce per impostare filtri o eliminarla."
        markup = InlineKeyboardMarkup(row_width=1)
        
        for s in searches:
            status_symbol = "🟢" if s['enabled'] == 1 else "🔴"
            price_limit = ""
            if s['min_price'] > 0 or s['max_price'] is not None:
                min_p = f"{s['min_price']}€" if s['min_price'] > 0 else "0€"
                max_p = f"{s['max_price']}€" if s['max_price'] is not None else "∞"
                price_limit = f" [{min_p}-{max_p}]"
            
            btn_label = f"{status_symbol} {s['query']}{price_limit}"
            markup.add(InlineKeyboardButton(btn_label, callback_data=f"search_detail_{s['id']}_{chat_id}"))
            
        markup.add(InlineKeyboardButton("➕ Aggiungi Ricerca", callback_data="menu_add"))
        markup.add(InlineKeyboardButton("⬅️ Menu Principale", callback_data="menu_back_to_main"))
        
        if message_id:
            bot.edit_message_text(text, chat_id, message_id, parse_mode="HTML", reply_markup=markup)
        else:
            bot.send_message(chat_id, text, parse_mode="HTML", reply_markup=markup)

    def send_search_detail(caller_chat_id, target_chat_id, search_id, message_id):
        searches = db.get_searches(target_chat_id)
        search = next((s for s in searches if s['id'] == int(search_id)), None)
        if not search:
            if caller_chat_id != target_chat_id and is_admin(caller_chat_id):
                send_admin_list_all(caller_chat_id, message_id)
            else:
                send_list_menu(caller_chat_id, message_id)
            return
            
        status_label = "🟢 Attiva" if search['enabled'] == 1 else "🔴 Disattivata"
        min_p = f"{search['min_price']} €" if search['min_price'] > 0 else "Nessuno"
        max_p = f"{search['max_price']} €" if search['max_price'] is not None else "Nessuno"
        
        type_search = "Link Vinted 🔗" if search['url_params'] else "Testuale 📝"
        excl_keys = search['exclude_keywords'] if search['exclude_keywords'] else "Nessuna"
        must_keys = search['must_keywords'] if search['must_keywords'] else "Nessuna"
        ai_status = "🤖 Attivo" if search['use_ai'] == 1 else "❌ Disattivato"
        
        owner_str = f" (Utente {target_chat_id})" if target_chat_id != caller_chat_id else ""
        
        text = (
            f"🔍 <b>Dettaglio Ricerca{owner_str}:</b> <code>{search['query']}</code>\n\n"
            f"• Tipo ricerca: <b>{type_search}</b>\n"
            f"• Stato: <b>{status_label}</b>\n"
            f"• Filtro AI (Ollama): <b>{ai_status}</b>\n"
            f"• Prezzo Minimo: <b>{min_p}</b>\n"
            f"• Prezzo Massimo: <b>{max_p}</b>\n"
            f"• Parole escluse: <code>{excl_keys}</code>\n"
            f"• Parole richieste: <code>{must_keys}</code>"
        )
        
        markup = InlineKeyboardMarkup(row_width=2)
        toggle_text = "🔴 Disattiva" if search['enabled'] == 1 else "🟢 Attiva"
        
        # Inseriamo il target_chat_id in ogni callback per applicare l'azione sull'utente corretto!
        btn_toggle = InlineKeyboardButton(toggle_text, callback_data=f"search_toggle_{search_id}_{target_chat_id}")
        btn_setprice = InlineKeyboardButton("💰 Limiti Prezzo", callback_data=f"search_setprice_{search_id}_{target_chat_id}")
        
        btn_excl = InlineKeyboardButton("🚫 Par. Escluse", callback_data=f"search_setexcl_{search_id}_{target_chat_id}")
        btn_must = InlineKeyboardButton("🎯 Par. Richieste", callback_data=f"search_setmust_{search_id}_{target_chat_id}")
        
        ai_toggle_btn = "🤖 Disattiva AI" if search['use_ai'] == 1 else "🤖 Attiva AI"
        btn_toggle_ai = InlineKeyboardButton(ai_toggle_btn, callback_data=f"search_toggleai_{search_id}_{target_chat_id}")
        btn_scan = InlineKeyboardButton("🔍 Forza Controllo", callback_data=f"search_scan_{search_id}_{target_chat_id}")
        
        btn_delete = InlineKeyboardButton("🗑️ Elimina", callback_data=f"search_delete_{search_id}_{target_chat_id}")
        
        back_callback = "admin_list_all" if target_chat_id != caller_chat_id else "menu_list"
        btn_back = InlineKeyboardButton("⬅️ Indietro", callback_data=back_callback)
        
        markup.add(btn_toggle, btn_setprice)
        markup.add(btn_excl, btn_must)
        markup.add(btn_toggle_ai, btn_scan)
        markup.add(btn_delete, btn_back)
        
        bot.edit_message_text(text, caller_chat_id, message_id, parse_mode="HTML", reply_markup=markup)

    @bot.callback_query_handler(func=lambda call: True)
    @owner_only_callback
    def handle_callbacks(call):
        caller_chat_id = call.message.chat.id
        message_id = call.message.message_id
        data = call.data
        
        if data == "menu_back_to_main":
            send_main_menu(caller_chat_id, message_id)
            bot.answer_callback_query(call.id)
            
        elif data == "menu_list":
            send_list_menu(caller_chat_id, message_id)
            bot.answer_callback_query(call.id)
            
        elif data == "admin_menu":
            if is_admin(caller_chat_id):
                send_admin_menu(caller_chat_id, message_id)
            bot.answer_callback_query(call.id)
            
        elif data == "admin_list_all":
            if is_admin(caller_chat_id):
                send_admin_list_all(caller_chat_id, message_id)
            bot.answer_callback_query(call.id)
            
        elif data == "menu_add":
            bot.answer_callback_query(call.id)
            msg = bot.send_message(caller_chat_id, "✍️ Digita la parola chiave da tracciare, oppure incolla un link di ricerca Vinted completo:\n<i>Invia /cancel per annullare.</i>", parse_mode="HTML")
            bot.register_next_step_handler(msg, process_add_query)
            
        elif data == "menu_toggle_state":
            paused = db.get_setting(caller_chat_id, "paused", "0") == "1"
            new_paused = "0" if paused else "1"
            db.set_setting(caller_chat_id, "paused", new_paused)
            status_text = "messo in PAUSA" if new_paused == "1" else "RIPRESO"
            bot.answer_callback_query(call.id, f"Tracker personale {status_text}!")
            send_main_menu(caller_chat_id, message_id)
            
        elif data == "menu_stats":
            bot.answer_callback_query(call.id)
            bot.delete_message(caller_chat_id, message_id)
            send_stats(caller_chat_id)
            
        elif data == "menu_logs":
            bot.answer_callback_query(call.id)
            send_logs(caller_chat_id)
            
        elif data == "menu_close":
            bot.answer_callback_query(call.id)
            bot.delete_message(caller_chat_id, message_id)
            
        elif data == "download_log_file":
            log_path = "/home/ubuntu/vinted_tracker/tracker.log"
            if os.path.exists(log_path):
                try:
                    with open(log_path, 'rb') as f:
                        bot.send_document(caller_chat_id, f, caption="📂 File di log completo del Vinted Tracker")
                    bot.answer_callback_query(call.id, "Log inviato!")
                except Exception as e:
                    bot.answer_callback_query(call.id, f"Errore invio: {e}")
            else:
                bot.answer_callback_query(call.id, "File non trovato.")
                
        elif data.startswith("search_detail_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            send_search_detail(caller_chat_id, target_chat_id, search_id, message_id)
            bot.answer_callback_query(call.id)
            
        elif data.startswith("search_toggle_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            new_state = db.toggle_search(target_chat_id, search_id)
            state_text = "attivata" if new_state == 1 else "disattivata"
            bot.answer_callback_query(call.id, f"Ricerca {state_text}!")
            send_search_detail(caller_chat_id, target_chat_id, search_id, message_id)

        elif data.startswith("search_toggleai_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            new_state = db.toggle_search_ai(target_chat_id, search_id)
            state_text = "AI locale abilitata!" if new_state == 1 else "AI locale disabilitata!"
            bot.answer_callback_query(call.id, state_text)
            send_search_detail(caller_chat_id, target_chat_id, search_id, message_id)
            
        elif data.startswith("search_delete_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            db.delete_search(target_chat_id, search_id)
            bot.answer_callback_query(call.id, "Ricerca eliminata.")
            if target_chat_id != caller_chat_id:
                send_admin_list_all(caller_chat_id, message_id)
            else:
                send_list_menu(caller_chat_id, message_id)
            
        elif data.startswith("search_setprice_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            bot.answer_callback_query(call.id)
            msg = bot.send_message(
                caller_chat_id, 
                "💰 <b>Imposta Limiti Prezzo</b>\n"
                "Invia il range in formato <code>min-max</code> (es: <code>100-250</code>).\n"
                "Invia <code>0</code> per disattivare i limiti.\n"
                "<i>Invia /cancel per annullare.</i>",
                parse_mode="HTML"
            )
            bot.register_next_step_handler(msg, lambda m: process_set_price(m, search_id, target_chat_id))
            
        elif data.startswith("search_setexcl_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            bot.answer_callback_query(call.id)
            msg = bot.send_message(
                caller_chat_id,
                "🚫 <b>Parole Chiave Escluse</b>\n"
                "Invia le parole che NON devono essere presenti nel titolo dell'articolo, separate da virgola (es: <code>controller,cover,cavo,gioco</code>).\n"
                "Invia <code>0</code> per cancellare i filtri.\n"
                "<i>Invia /cancel per annullare.</i>",
                parse_mode="HTML"
            )
            bot.register_next_step_handler(msg, lambda m: process_set_keywords(m, 'exclude', search_id, target_chat_id))
            
        elif data.startswith("search_setmust_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            bot.answer_callback_query(call.id)
            msg = bot.send_message(
                caller_chat_id,
                "🎯 <b>Parole Chiave Richieste</b>\n"
                "Invia le parole che DEVONO essere tutte presenti nel titolo dell'articolo, separate da virgola (es: <code>console,ps5</code>).\n"
                "Invia <code>0</code> per cancellare i filtri.\n"
                "<i>Invia /cancel per annullare.</i>",
                parse_mode="HTML"
            )
            bot.register_next_step_handler(msg, lambda m: process_set_keywords(m, 'must', search_id, target_chat_id))
            
        elif data.startswith("search_scan_"):
            parts = data.split("_")
            search_id = parts[2]
            target_chat_id = int(parts[3])
            
            if target_chat_id != caller_chat_id and not is_admin(caller_chat_id):
                bot.answer_callback_query(call.id, "Non autorizzato.")
                return
                
            bot.answer_callback_query(call.id, "Scansione forzata avviata...")
            
            def run_forced_scan():
                try:
                    searches = db.get_searches(target_chat_id)
                    search = next((s for s in searches if s['id'] == int(search_id)), None)
                    if not search:
                        bot.send_message(caller_chat_id, "⚠️ Ricerca non trovata.")
                        return
                    
                    bot.send_message(caller_chat_id, f"🔍 <i>Avvio controllo manuale per: '{search['query']}'...</i>", parse_mode="HTML")
                    temp_scrapers = [VintedScraper(db), SubitoScraper()]
                    items = []
                    for s_scraper in temp_scrapers:
                        s_items = s_scraper.fetch_items(search)
                        if s_items: items.extend(s_items)
                    if not items:
                        bot.send_message(caller_chat_id, "❌ Scraper fallito. Controlla i log.")
                        return
                        bot.send_message(caller_chat_id, "❌ Scraper fallito. Controlla i log.")
                        return
                        
                    new_items_count = 0
                    for item in items:
                        item_id = str(item.get("id"))
                        if db.is_item_sent(item_id, search['id']):
                            continue
                            
                        if not filter_item_fast(item, search):
                            continue
                            
                        if search.get('use_ai') == 1:
                            price_val = 0.0
                            try:
                                price_val = float(item.get("price", {}).get("amount", 0.0))
                            except Exception:
                                pass
                            clean_query = search['query'].replace("Link: ", "")
                            if not check_item_with_ai(item.get("title", ""), price_val, clean_query):
                                db.mark_item_as_sent(item_id, search['id'], item.get("title"), price_val, item.get("url"))
                                continue
                                
                        send_notification(bot, target_chat_id, item, search)
                        price_val = 0.0
                        try:
                            price_val = float(item.get("price", {}).get("amount", 0.0))
                        except Exception:
                            pass
                        db.mark_item_as_sent(item_id, search['id'], item.get("title"), price_val, item.get("url"))
                        new_items_count += 1
                        time.sleep(1.0)
                        
                    bot.send_message(caller_chat_id, f"✅ Controllo completato per <code>{search['query']}</code>!\nNotificati {new_items_count} nuovi articoli.", parse_mode="HTML")
                except Exception as e:
                    bot.send_message(caller_chat_id, f"❌ Errore durante il controllo manuale: {e}")
                    
            threading.Thread(target=run_forced_scan).start()

    def process_add_query(message):
        chat_id = message.chat.id
        text = message.text.strip()
        
        if text.lower() == '/cancel':
            bot.send_message(chat_id, "❌ Operazione annullata.")
            send_main_menu(chat_id)
            return
            
        if text.startswith('/'):
            bot.send_message(chat_id, "⚠️ Input non valido. Operazione annullata.")
            send_main_menu(chat_id)
            return

        if text.startswith("http"):
            try:
                parsed = urlparse(text)
                q_params = parse_qs(parsed.query)
                if not q_params:
                    bot.send_message(chat_id, "⚠️ Il link fornito non sembra contenere parametri di ricerca validi.")
                    send_main_menu(chat_id)
                    return
                
                search_text = q_params.get('search_text', [''])[0]
                if not search_text:
                    search_text = f"Link_{int(time.time())}"
                else:
                    search_text = f"Link: {search_text}"
                    
                search_id = db.add_search(chat_id, search_text, url_params=json.dumps(q_params))
                if search_id:
                    bot.send_message(chat_id, f"✅ Link Vinted tracciato come <code>{search_text}</code>!\nApplicherò tutti i filtri nativi impostati sul sito.", parse_mode="HTML")
                else:
                    bot.send_message(chat_id, f"⚠️ Questa ricerca o link è già presente nel database.")
                send_main_menu(chat_id)
            except Exception as e:
                bot.send_message(chat_id, f"❌ Errore elaborazione link: {e}")
                send_main_menu(chat_id)
        else:
            search_id = db.add_search(chat_id, text)
            if search_id:
                msg = bot.send_message(
                    chat_id, 
                    f"✅ Ricerca per <code>{text}</code> aggiunta.\n\n"
                    "Vuoi impostare un limite di prezzo? Se sì, inserisci il prezzo massimo (es: <code>250</code>) o un range (es: <code>100-300</code>).\n"
                    "Altrimenti rispondi <code>NO</code>.\n"
                    "<i>Invia /cancel per annullare.</i>", 
                    parse_mode="HTML"
                )
                bot.register_next_step_handler(msg, lambda m: process_initial_price(m, search_id))
            else:
                bot.send_message(chat_id, f"⚠️ La ricerca per <code>{text}</code> è già esistente fra le tue ricerche.")
                send_main_menu(chat_id)

    def process_initial_price(message, search_id):
        chat_id = message.chat.id
        text = message.text.strip()
        
        if text.lower() == '/cancel':
            bot.send_message(chat_id, "❌ Operazione annullata. Ricerca creata senza limiti di prezzo.")
            send_main_menu(chat_id)
            return
            
        if text.lower() == 'no':
            bot.send_message(chat_id, "✅ Ricerca creata senza limiti di prezzo.")
            send_main_menu(chat_id)
            return

        min_p, max_p = parse_price_input(text)
        if min_p is None and max_p is None:
            bot.send_message(chat_id, "⚠️ Formato non valido. La ricerca è stata salvata senza filtri di prezzo.")
        else:
            db.update_search_prices(chat_id, search_id, min_p, max_p)
            limits_desc = f"Min: {min_p or 0}€, Max: {max_p or 'Nessuno'}€"
            bot.send_message(chat_id, f"✅ Limiti impostati: <code>{limits_desc}</code>", parse_mode="HTML")
            
        send_main_menu(chat_id)

    def process_set_price(message, search_id, target_chat_id):
        caller_chat_id = message.chat.id
        text = message.text.strip()
        
        if text.lower() == '/cancel':
            bot.send_message(caller_chat_id, "❌ Modifica annullata.")
            if caller_chat_id != target_chat_id:
                send_admin_list_all(caller_chat_id, None)
            else:
                send_list_menu(caller_chat_id)
            return
            
        if text == '0':
            db.update_search_prices(target_chat_id, search_id, 0.0, None)
            bot.send_message(caller_chat_id, "✅ Filtri di prezzo azzerati.")
            if caller_chat_id != target_chat_id:
                send_admin_list_all(caller_chat_id, None)
            else:
                send_list_menu(caller_chat_id)
            return
            
        min_p, max_p = parse_price_input(text)
        if min_p is None and max_p is None:
            bot.send_message(caller_chat_id, "⚠️ Formato non valido. Nessuna modifica applicata.")
        else:
            db.update_search_prices(target_chat_id, search_id, min_p, max_p)
            limits_desc = f"Min: {min_p or 0}€, Max: {max_p or 'Nessuno'}€"
            bot.send_message(caller_chat_id, f"✅ Nuovi limiti impostati: <code>{limits_desc}</code>", parse_mode="HTML")
            
        if caller_chat_id != target_chat_id:
            send_admin_list_all(caller_chat_id, None)
        else:
            send_list_menu(caller_chat_id)

    def process_set_keywords(message, key_type, search_id, target_chat_id):
        caller_chat_id = message.chat.id
        text = message.text.strip()
        
        if text.lower() == '/cancel':
            bot.send_message(caller_chat_id, "❌ Modifica annullata.")
            if caller_chat_id != target_chat_id:
                send_admin_list_all(caller_chat_id, None)
            else:
                send_list_menu(caller_chat_id)
            return
            
        if text == '0':
            db.update_search_keywords(target_chat_id, search_id, key_type, None)
            bot.send_message(caller_chat_id, "✅ Filtri parole chiave cancellati.")
            if caller_chat_id != target_chat_id:
                send_admin_list_all(caller_chat_id, None)
            else:
                send_list_menu(caller_chat_id)
            return
            
        db.update_search_keywords(target_chat_id, search_id, key_type, text)
        bot.send_message(caller_chat_id, f"✅ Parole chiave aggiornate a: <code>{text}</code>", parse_mode="HTML")
        if caller_chat_id != target_chat_id:
            send_admin_list_all(caller_chat_id, None)
        else:
            send_list_menu(caller_chat_id)

    def parse_price_input(text):
        text = text.replace(" ", "")
        if "-" in text:
            try:
                parts = text.split("-")
                min_p = float(parts[0]) if parts[0] else 0.0
                max_p = float(parts[1]) if parts[1] else None
                return min_p, max_p
            except ValueError:
                return None, None
        else:
            try:
                max_p = float(text)
                return 0.0, max_p
            except ValueError:
                return None, None

# --- MAIN ---
def main():
    base_dir = "/home/ubuntu/vinted_tracker"
    os.makedirs(base_dir, exist_ok=True)
    db_path = os.path.join(base_dir, "tracker.db")
    log_path = os.path.join(base_dir, "tracker.log")
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler(log_path, encoding='utf-8'),
            logging.StreamHandler()
        ]
    )
    
    logging.info("=== AVVIO VINTED TRACKER BOT ===")
    
    db = Database(db_path)
    scraper = VintedScraper(db)
    subito_scraper = SubitoScraper()
    bot = telebot.TeleBot(TOKEN)
    
    setup_bot(bot, db, scraper)
    
    tracker_thread = threading.Thread(
        target=tracker_loop, 
        args=(bot, db, [scraper, subito_scraper]),
        daemon=True
    )
    tracker_thread.start()
    
    for cid in ALLOWED_CHAT_IDS:
        try:
            bot.send_message(cid, "🚀 <b>Vinted Tracker Bot Avviato (Multi-Utente + Admin)!</b>\nUsa /menu per gestire le ricerche in tempo reale.", parse_mode="HTML")
        except Exception as e:
            logging.error(f"Errore notifica Telegram a {cid}: {e}")
        
    logging.info("Avvio polling Telegram...")
    while True:
        try:
            bot.polling(none_stop=True, interval=2, timeout=60)
        except Exception as e:
            logging.error(f"Errore polling Telegram, riavvio in 10s: {e}")
            time.sleep(10)

if __name__ == '__main__':
    main()
