import os
import io
import re
import html
import sqlite3
import asyncio
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

import requests
import jdatetime
from openpyxl import Workbook, load_workbook
from telegram import (
    Update,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    ReplyKeyboardMarkup,
    KeyboardButton,
    MenuButtonCommands
)
from telegram.ext import (
    Application,
    CommandHandler,
    MessageHandler,
    CallbackQueryHandler,
    ConversationHandler,
    ContextTypes,
    filters
)

# =========================================================
# تنظیمات پایه و اتصال به سایت
# =========================================================

BOT_TOKEN = "8904381505:AAFfzr-A26RcihYzGdIx2_PAZcca0GiBu3Y"

# آدرس سایت وردپرسی شما
SITE_URL = "https://echeshmak.com"

# (اختیاری) کلیدهای API ووکامرس در صورت داشتن
WC_CONSUMER_KEY = ""
WC_CONSUMER_SECRET = ""

# اگر آیدی عددی خودت را می‌دانی اینجا بگذار، وگرنه اولین نفری که /start بزند مالک اصلی قفل می‌شود
OWNER_ID = None

DEFAULT_MIN_STOCK = 3
PAGE_SIZE = 30  # تعداد نمایش کالا در هر صفحه تلگرام
DB = "shop.db"


# =========================================================
# DATABASE
# =========================================================

def db():
    conn = sqlite3.connect(DB)
    conn.row_factory = sqlite3.Row
    return conn


def init_db():
    conn = db()
    cur = conn.cursor()

    cur.execute("""
        CREATE TABLE IF NOT EXISTS products (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            code TEXT UNIQUE,
            barcode TEXT,
            base_price REAL DEFAULT 0,
            price REAL DEFAULT 0,
            stock INTEGER DEFAULT 0,
            min_stock INTEGER DEFAULT 3,
            category TEXT,
            description TEXT,
            image_id TEXT,
            price_updated_at TEXT
        )
    """)

    cur.execute("""
        CREATE TABLE IF NOT EXISTS settings (
            key TEXT PRIMARY KEY,
            value TEXT
        )
    """)

    cur.execute("""
        CREATE TABLE IF NOT EXISTS sales (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            product_id INTEGER,
            product_name TEXT,
            quantity INTEGER DEFAULT 1,
            unit_price REAL,
            total_price REAL,
            seller_id INTEGER,
            sold_at TEXT,
            status TEXT DEFAULT 'approved'
        )
    """)

    cur.execute("""
        CREATE TABLE IF NOT EXISTS sellers (
            user_id INTEGER PRIMARY KEY,
            name TEXT,
            active INTEGER DEFAULT 1,
            role TEXT DEFAULT 'seller'
        )
    """)

    prod_cols = [r["name"] for r in cur.execute("PRAGMA table_info(products)").fetchall()]
    prod_migrations = {
        "base_price": "ALTER TABLE products ADD COLUMN base_price REAL DEFAULT 0",
        "min_stock": f"ALTER TABLE products ADD COLUMN min_stock INTEGER DEFAULT {DEFAULT_MIN_STOCK}",
        "category": "ALTER TABLE products ADD COLUMN category TEXT",
        "description": "ALTER TABLE products ADD COLUMN description TEXT",
        "image_id": "ALTER TABLE products ADD COLUMN image_id TEXT",
        "price_updated_at": "ALTER TABLE products ADD COLUMN price_updated_at TEXT",
    }
    for col, sql in prod_migrations.items():
        if col not in prod_cols:
            cur.execute(sql)

    sale_cols = [r["name"] for r in cur.execute("PRAGMA table_info(sales)").fetchall()]
    if "status" not in sale_cols:
        cur.execute("ALTER TABLE sales ADD COLUMN status TEXT DEFAULT 'approved'")

    seller_cols = [r["name"] for r in cur.execute("PRAGMA table_info(sellers)").fetchall()]
    if "role" not in seller_cols:
        cur.execute("ALTER TABLE sellers ADD COLUMN role TEXT DEFAULT 'seller'")

    if cur.execute("SELECT 1 FROM settings WHERE key='markup_percent'").fetchone() is None:
        cur.execute("INSERT INTO settings(key, value) VALUES('markup_percent', '0')")

    cur.execute("""
        UPDATE products
        SET base_price = price
        WHERE (base_price IS NULL OR base_price = 0)
        AND price IS NOT NULL
    """)

    cur.execute(f"""
        UPDATE products
        SET min_stock = {DEFAULT_MIN_STOCK}
        WHERE min_stock IS NULL
    """)

    conn.commit()
    conn.close()


def remove_duplicate_products(conn):
    """پاکسازی خودکار کالاهای تکراری که نام کاملاً یکسان دارند (نگهداری آخرین نسخه)"""
    conn.execute("""
        DELETE FROM products
        WHERE id NOT IN (
            SELECT MAX(id)
            FROM products
            GROUP BY TRIM(name)
        )
    """)


# =========================================================
# ROLES & PERMISSIONS
# =========================================================

def get_owner_id():
    if OWNER_ID is not None:
        return OWNER_ID
    conn = db()
    row = conn.execute("SELECT value FROM settings WHERE key='admin_id'").fetchone()
    conn.close()
    return int(row["value"]) if row and str(row["value"]).isdigit() else None


def set_owner_id(user_id, name="مالک فروشگاه"):
    conn = db()
    conn.execute("INSERT OR REPLACE INTO settings(key, value) VALUES('admin_id', ?)", (str(user_id),))
    conn.execute("""
        INSERT OR REPLACE INTO sellers(user_id, name, active, role)
        VALUES(?, ?, 1, 'admin')
    """, (user_id, name))
    conn.commit()
    conn.close()


def is_admin(user_id):
    owner = get_owner_id()
    if owner is not None and user_id == owner:
        return True
    conn = db()
    row = conn.execute(
        "SELECT 1 FROM sellers WHERE user_id=? AND active=1 AND role='admin'",
        (user_id,)
    ).fetchone()
    conn.close()
    return bool(row)


def is_seller(user_id):
    if is_admin(user_id):
        return True
    conn = db()
    row = conn.execute(
        "SELECT 1 FROM sellers WHERE user_id=? AND active=1",
        (user_id,)
    ).fetchone()
    conn.close()
    return bool(row)


def get_all_admin_ids():
    ids = set()
    owner = get_owner_id()
    if owner:
        ids.add(owner)
    conn = db()
    rows = conn.execute("SELECT user_id FROM sellers WHERE active=1 AND role='admin'").fetchall()
    conn.close()
    for r in rows:
        ids.add(r["user_id"])
    return list(ids)


def sync_staff_name(user):
    if not user:
        return
    conn = db()
    row = conn.execute("SELECT name FROM sellers WHERE user_id=?", (user.id,)).fetchone()
    if row and user.full_name:
        conn.execute("UPDATE sellers SET name=? WHERE user_id=?", (user.full_name, user.id))
        conn.commit()
    conn.close()


# =========================================================
# HELPERS & PRICE LOGIC
# =========================================================

def get_markup():
    conn = db()
    row = conn.execute("SELECT value FROM settings WHERE key='markup_percent'").fetchone()
    conn.close()
    return float(row["value"]) if row else 0.0


def set_markup(value):
    conn = db()
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    conn.execute("INSERT OR REPLACE INTO settings(key, value) VALUES('markup_percent', ?)", (str(value),))
    conn.execute("""
        UPDATE products
        SET price = base_price * (1 + ? / 100.0),
            price_updated_at = ?
    """, (value, now))
    conn.commit()
    conn.close()


def calc_price(base_price):
    return float(base_price) * (1 + get_markup() / 100)


def get_product(product_id):
    conn = db()
    row = conn.execute("SELECT * FROM products WHERE id=?", (product_id,)).fetchone()
    conn.close()
    return row


def money(value):
    try:
        return f"{float(value):,.0f}"
    except Exception:
        return str(value)


def fa_date(value=None):
    if not value:
        return "ثبت نشده"
    try:
        dt = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
        jdt = jdatetime.datetime.fromgregorian(datetime=dt)
        return jdt.strftime("%Y/%m/%d - %H:%M")
    except Exception:
        return value


def strip_html_tags(text):
    if not text:
        return ""
    clean = re.sub(r"<[^>]+>", " ", str(text))
    clean = html.unescape(clean)
    clean = re.sub(r"\s+", " ", clean).strip()
    return clean[:600]


def normalize_name(name):
    """نرمال‌سازی نام کالا برای تشخیص دقیق تکراری‌ها"""
    if not name:
        return ""
    s = str(name).strip()
    s = s.replace("ي", "ی").replace("ك", "ک").replace("\u200c", " ")
    s = re.sub(r"\s+", " ", s)
    return s


def stock_warning_text(product_row):
    if not product_row:
        return ""
    st = product_row["stock"]
    limit = product_row["min_stock"] if product_row["min_stock"] is not None else DEFAULT_MIN_STOCK

    if st == 0:
        return f"\n\n🚨 <b>هشدار فوری: موجودی «{product_row['name']}» کاملاً تمام شد (۰ عدد)!</b>"
    elif st <= limit:
        return (
            f"\n\n⚠️ <b>هشدار انبار: موجودی «{product_row['name']}» به حد هشدار ({limit} عدد) رسید! "
            f"(موجودی فعلی: {st} عدد)</b>"
        )
    return ""


async def process_sale_request(product_id, qty, user, context):
    if not is_seller(user.id):
        return False, "⛔ شما دسترسی ثبت فروش ندارید."

    product = get_product(product_id)
    if not product:
        return False, "❌ کالا پیدا نشد."

    if product["stock"] < qty:
        return False, f"❌ موجودی کافی نیست! (موجودی فعلی: {product['stock']} عدد)"

    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    unit_price = float(product["price"])
    total_price = unit_price * qty

    conn = db()

    if is_admin(user.id):
        conn.execute("""
            UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ?
        """, (qty, product_id, qty))

        conn.execute("""
            INSERT INTO sales (
                product_id, product_name, quantity,
                unit_price, total_price, seller_id, sold_at, status
            )
            VALUES (?, ?, ?, ?, ?, ?, ?, 'approved')
        """, (product_id, product["name"], qty, unit_price, total_price, user.id, now))

        conn.commit()
        conn.close()

        new_product = get_product(product_id)
        warn = stock_warning_text(new_product)

        msg = (
            "✅ <b>فروش مستقیماً ثبت شد.</b>\n\n"
            f"📦 کالا: {product['name']}\n"
            f"🔢 تعداد: {qty} عدد\n"
            f"💰 فی: {money(unit_price)}\n"
            f"💵 مبلغ کل: {money(total_price)}\n"
            f"📦 موجودی جدید: {new_product['stock']}\n"
            f"🕐 تاریخ: {fa_date(now)}"
            f"{warn}"
        )
        return True, msg

    cur = conn.execute("""
        INSERT INTO sales (
            product_id, product_name, quantity,
            unit_price, total_price, seller_id, sold_at, status
        )
        VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')
    """, (product_id, product["name"], qty, unit_price, total_price, user.id, now))
    sale_id = cur.lastrowid
    conn.commit()
    conn.close()

    admin_text = (
        "🔔 <b>درخواست تایید فروش جدید</b>\n\n"
        f"🧾 شماره درخواست: #{sale_id}\n"
        f"👤 فروشنده: {user.full_name} (<code>{user.id}</code>)\n"
        f"📦 کالا: <b>{product['name']}</b>\n"
        f"🔢 تعداد: <b>{qty} عدد</b> (موجودی فعلی: {product['stock']})\n"
        f"💰 قیمت واحد: {money(unit_price)}\n"
        f"💵 مبلغ کل: <b>{money(total_price)} تومان</b>\n"
        f"🕐 زمان ثبت: {fa_date(now)}"
    )
    admin_markup = InlineKeyboardMarkup([
        [
            InlineKeyboardButton("✅ تایید و کسر موجودی", callback_data=f"appsale_{sale_id}"),
            InlineKeyboardButton("❌ رد درخواست", callback_data=f"rejsale_{sale_id}")
        ]
    ])

    for adm_id in get_all_admin_ids():
        try:
            await context.bot.send_message(
                chat_id=adm_id,
                text=admin_text,
                parse_mode="HTML",
                reply_markup=admin_markup
            )
        except Exception:
            pass

    seller_msg = (
        "⏳ <b>درخواست فروش ثبت شد و برای تایید به مدیریت ارسال گردید.</b>\n\n"
        f"🧾 کد پیگیری: #{sale_id}\n"
        f"📦 کالا: {product['name']}\n"
        f"🔢 تعداد: {qty} عدد\n"
        f"💵 مبلغ کل: {money(total_price)}\n\n"
        "<i>پس از تایید مدیر، از موجودی کسر شده و به شما اطلاع داده می‌شود.</i>"
    )
    return True, seller_msg


# =========================================================
# WORDPRESS / WOOCOMMERCE UNLIMITED & FAST SYNC
# =========================================================

def parse_single_product_page(session, purl, idx):
    """خواندن اطلاعات یک صفحه محصول"""
    try:
        pr = session.get(purl, timeout=15)
        if pr.status_code != 200:
            return None
        page_html = pr.text

        title_m = (
            re.search(r'<h1[^>]*class=["\'][^"\']*product_title[^"\']*["\'][^>]*>(.*?)</h1>', page_html, re.S)
            or re.search(r'<meta\s+property=["\']og:title["\']\s+content=["\'](.*?)["\']', page_html, re.I)
            or re.search(r'<h1[^>]*>(.*?)</h1>', page_html, re.S)
        )
        if not title_m:
            return None
        name = normalize_name(strip_html_tags(title_m.group(1)).split("|")[0].split("–")[0])
        if not name:
            return None

        price_val = 0.0
        price_m = (
            re.search(r'property=["\']product:price:amount["\']\s+content=["\']([\d\.]+)["\']', page_html, re.I)
            or re.search(r'"price"\s*:\s*"(\d+(?:\.\d+)?)"', page_html)
            or re.search(r'"price"\s*:\s*(\d+(?:\.\d+)?)', page_html)
        )
        if price_m:
            price_val = float(price_m.group(1))
        else:
            bdi_matches = re.findall(r'<bdi>(.*?)</bdi>', page_html, re.S)
            for b in bdi_matches:
                ba = b.translate(str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789"))
                digits = re.sub(r"[^\d]", "", ba)
                if digits:
                    price_val = float(digits)
            if price_val == 0:
                fa_m = re.search(r'([\d,٬۰-۹]{4,})\s*(?:تومان|ریال)', page_html)
                if fa_m:
                    fa_num = fa_m.group(1).translate(str.maketrans("۰۱۲۳۴۵۶۷۸۹", "0123456789"))
                    digits = re.sub(r"[^\d]", "", fa_num)
                    if digits:
                        price_val = float(digits)

        img_m = re.search(r'<meta\s+property=["\']og:image["\']\s+content=["\'](.*?)["\']', page_html, re.I)
        image_url = img_m.group(1).strip() if img_m else None

        desc_m = re.search(r'<meta\s+property=["\']og:description["\']\s+content=["\'](.*?)["\']', page_html, re.I)
        desc = strip_html_tags(desc_m.group(1)) if desc_m else ""

        sku_m = re.search(r'class=["\']sku["\'][^>]*>(.*?)<', page_html, re.S)
        postid_m = re.search(r'postid-(\d+)', page_html)
        shortlink_m = re.search(r'\?p=(\d+)', page_html)
        if sku_m and strip_html_tags(sku_m.group(1)):
            code = strip_html_tags(sku_m.group(1))
        elif postid_m:
            code = f"WC-{postid_m.group(1)}"
        elif shortlink_m:
            code = f"WC-{shortlink_m.group(1)}"
        else:
            slug = purl.rstrip("/").split("/")[-1][:60]
            code = f"WEB-{slug}"

        cat_m = re.search(r'rel=["\']tag["\'][^>]*>(.*?)</a>', page_html, re.S)
        category = strip_html_tags(cat_m.group(1)) if cat_m else None

        out_of_stock = ("outofstock" in page_html.lower()) or ("ناموجود" in page_html)
        stock = 0 if out_of_stock else 10

        return {
            "name": name,
            "code": code,
            "base_price": price_val,
            "stock": stock,
            "category": category,
            "description": desc,
            "image_url": image_url
        }
    except Exception:
        return None


def fetch_wc_products_sync():
    """استخراج کامل و بدون محدودیت تمام محصولات سایت echeshmak.com"""
    base = SITE_URL.rstrip("/")
    session = requests.Session()
    session.trust_env = False
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,application/json,*/*;q=0.8",
        "Accept-Language": "fa-IR,fa;q=0.9,en-US;q=0.8,en;q=0.7",
    })

    items = []

    # ۱. اگر کلید REST API ووکامرس تنظیم شده باشد
    if WC_CONSUMER_KEY and WC_CONSUMER_SECRET:
        page = 1
        while True:
            url = f"{base}/wp-json/wc/v3/products"
            resp = session.get(
                url,
                params={"per_page": 100, "page": page, "status": "publish"},
                auth=(WC_CONSUMER_KEY, WC_CONSUMER_SECRET),
                timeout=20
            )
            if resp.status_code != 200:
                break
            data = resp.json()
            if not data or not isinstance(data, list):
                break
            for p in data:
                name = normalize_name(html.unescape(p.get("name", "")))
                code = str(p.get("sku") or f"WC-{p.get('id')}").strip()
                raw_price = float(p.get("price") or p.get("regular_price") or 0)
                stock_qty = p.get("stock_quantity")
                in_stock = p.get("stock_status") == "instock"
                stock = int(stock_qty) if stock_qty is not None else (10 if in_stock else 0)
                cats = p.get("categories") or []
                category = html.unescape(cats[0]["name"]).strip() if cats else None
                desc = strip_html_tags(p.get("short_description") or p.get("description") or "")
                imgs = p.get("images") or []
                image_url = imgs[0].get("src") if imgs else None
                items.append({
                    "name": name, "code": code, "base_price": raw_price,
                    "stock": stock, "category": category, "description": desc, "image_url": image_url
                })
            if len(data) < 100:
                break
            page += 1
        if items:
            return items

    # ۲. Store API عمومی ووکامرس
    page = 1
    while True:
        try:
            url = f"{base}/wp-json/wc/store/v1/products"
            resp = session.get(url, params={"per_page": 100, "page": page}, timeout=15)
            if resp.status_code != 200:
                break
            data = resp.json()
            if not data or not isinstance(data, list):
                break
            for p in data:
                name = normalize_name(html.unescape(p.get("name", "")))
                code = str(p.get("sku") or f"WC-{p.get('id')}").strip()
                prices_obj = p.get("prices") or {}
                raw_price = float(prices_obj.get("price") or 0)
                currency_code = str(prices_obj.get("currency_code") or "").upper()
                if currency_code == "IRR" and raw_price >= 100000:
                    raw_price = raw_price / 10.0
                in_stock = p.get("is_in_stock", True)
                stock = 10 if in_stock else 0
                cats = p.get("categories") or []
                category = html.unescape(cats[0]["name"]).strip() if cats else None
                desc = strip_html_tags(p.get("short_description") or p.get("description") or "")
                imgs = p.get("images") or []
                image_url = imgs[0].get("src") if imgs else None
                items.append({
                    "name": name, "code": code, "base_price": raw_price,
                    "stock": stock, "category": category, "description": desc, "image_url": image_url
                })
            if len(data) < 100:
                break
            page += 1
        except Exception:
            break

    if items:
        return items

    # ۳. استخراج تمام لینک‌های محصولات از تمام Sitemap ها
    product_urls = []
    sitemap_indexes = [
        f"{base}/sitemap_index.xml",
        f"{base}/sitemap.xml",
        f"{base}/wp-sitemap.xml",
    ]

    visited_sitemaps = set()

    for idx_url in sitemap_indexes:
        try:
            r = session.get(idx_url, timeout=15)
            if r.status_code == 200 and "<loc>" in r.text:
                locs = [l.strip() for l in re.findall(r"<loc>(.*?)</loc>", r.text)]
                sub_sitemaps = [l for l in locs if "product" in l and l.endswith(".xml")]
                if sub_sitemaps:
                    for sub in sub_sitemaps:
                        if sub in visited_sitemaps:
                            continue
                        visited_sitemaps.add(sub)
                        sr = session.get(sub, timeout=15)
                        if sr.status_code == 200:
                            sub_locs = re.findall(r"<loc>(.*?)</loc>", sr.text)
                            product_urls.extend([u.strip() for u in sub_locs if "/product/" in u])
                else:
                    product_urls.extend([u for u in locs if "/product/" in u])
        except Exception:
            continue

    if not product_urls:
        direct_sitemaps = (
            [f"{base}/product-sitemap.xml"]
            + [f"{base}/product-sitemap{i}.xml" for i in range(1, 15)]
            + [f"{base}/wp-sitemap-posts-product-{i}.xml" for i in range(1, 15)]
        )
        for sm_url in direct_sitemaps:
            if sm_url in visited_sitemaps:
                continue
            try:
                r = session.get(sm_url, timeout=12)
                if r.status_code == 200 and "<loc>" in r.text:
                    visited_sitemaps.add(sm_url)
                    locs = re.findall(r"<loc>(.*?)</loc>", r.text)
                    found = [u.strip() for u in locs if "/product/" in u]
                    if found:
                        product_urls.extend(found)
            except Exception:
                continue

    if not product_urls:
        for pg in range(1, 200):
            shop_url = f"{base}/shop/" if pg == 1 else f"{base}/shop/page/{pg}/"
            try:
                r = session.get(shop_url, timeout=15)
                if r.status_code != 200:
                    break
                found = re.findall(r'href=["\'](https?://[^"\']*?/product/[^"\']+/)["\']', r.text)
                if not found:
                    break
                product_urls.extend(found)
            except Exception:
                break

    clean_urls = []
    seen = set()
    for u in product_urls:
        if u not in seen and not u.lower().endswith((".jpg", ".jpeg", ".png", ".webp", ".gif")):
            seen.add(u)
            clean_urls.append(u)

    with ThreadPoolExecutor(max_workers=10) as executor:
        futures = {
            executor.submit(parse_single_product_page, session, purl, idx): idx
            for idx, purl in enumerate(clean_urls, start=1)
        }
        for future in as_completed(futures):
            res = future.result()
            if res:
                items.append(res)

    return items


async def sync_website_products(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("⛔ فقط ادمین دسترسی دارد.", show_alert=True)
        return

    await query.answer("در حال اتصال به سایت...")
    status_msg = await query.message.reply_text(
        f"🌐 <b>در حال استخراج و بروزرسانی محصولات از سایت <code>{SITE_URL}</code>...</b>\n"
        "⏳ لطفاً صبر کنید (کالاهای قبلی آپدیت می‌شوند و تکراری اضافه نمی‌شود)...",
        parse_mode="HTML"
    )

    try:
        items = await asyncio.to_thread(fetch_wc_products_sync)
    except Exception as e:
        await status_msg.edit_text(
            f"❌ خطا در اتصال به سایت:\n<code>{e}</code>",
            parse_mode="HTML"
        )
        return

    if not items:
        await status_msg.edit_text(
            "❌ هیچ محصولی از سایت دریافت نشد. لطفاً مطمئن شوید اینترنت شما به سایت echeshmak.com دسترسی دارد."
        )
        return

    conn = db()
    # پاکسازی تکراری‌های احتمالی قبلی
    remove_duplicate_products(conn)

    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    markup = get_markup()

    added = 0
    updated = 0
    seen_in_batch = set()

    for item in items:
        norm_name = normalize_name(item["name"])
        if not norm_name:
            continue

        # جلوگیری از پردازش تکراری در یک بار اجرا
        dedup_key = (item["code"], norm_name)
        if dedup_key in seen_in_batch:
            continue
        seen_in_batch.add(dedup_key)

        final_price = item["base_price"] * (1 + markup / 100.0)

        # جستجوی هوشمند: اول بر اساس کد کالا، و اگر نبود بر اساس نام دقیق کالا!
        existing = None
        if item["code"]:
            existing = conn.execute("SELECT id, stock, image_id, code FROM products WHERE code=?", (item["code"],)).fetchone()
        if not existing:
            existing = conn.execute("SELECT id, stock, image_id, code FROM products WHERE TRIM(name)=?", (norm_name,)).fetchone()

        if existing:
            new_stock = item["stock"] if (WC_CONSUMER_KEY and WC_CONSUMER_SECRET) else (0 if item["stock"] == 0 else existing["stock"])
            new_img = item["image_url"] or existing["image_id"]
            new_code = existing["code"] or item["code"]

            conn.execute("""
                UPDATE products
                SET name=?, code=?, base_price=?, price=?, stock=?,
                    category=?, description=?, image_id=?, price_updated_at=?
                WHERE id=?
            """, (
                norm_name, new_code, item["base_price"], final_price, new_stock,
                item["category"], item["description"], new_img, now, existing["id"]
            ))
            updated += 1
        else:
            conn.execute("""
                INSERT INTO products (
                    name, code, base_price, price, stock, min_stock,
                    category, description, image_id, price_updated_at
                )
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                norm_name, item["code"], item["base_price"], final_price,
                item["stock"], DEFAULT_MIN_STOCK, item["category"],
                item["description"], item["image_url"], now
            ))
            added += 1

    conn.commit()
    total_in_db = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"]
    conn.close()

    await status_msg.edit_text(
        f"✅ <b>همگام‌سازی کامل با سایت {SITE_URL} انجام شد!</b>\n\n"
        f"🔍 کل کالاهای خوانده‌شده از سایت: <b>{len(seen_in_batch)} کالا</b>\n"
        f"➕ محصولات جدید اضافه‌شده: <b>{added} کالا</b>\n"
        f"🔄 محصولات قبلی بروزرسانی‌شده: <b>{updated} کالا</b>\n"
        f"📦 مجموع کل کالاهای فعلی در ربات: <b>{total_in_db} کالا</b>",
        parse_mode="HTML"
    )


# =========================================================
# MENUS (DYNAMIC BY ROLE)
# =========================================================

def main_menu(user_id=None):
    if user_id and is_admin(user_id):
        return ReplyKeyboardMarkup(
            [
                [KeyboardButton("📦 لیست محصولات"), KeyboardButton("🔎 جستجوی کالا")],
                [KeyboardButton("📂 دسته‌بندی‌ها"), KeyboardButton("💰 ثبت فروش")],
                [KeyboardButton("⚙️ مدیریت")]
            ],
            resize_keyboard=True
        )

    if user_id and is_seller(user_id):
        return ReplyKeyboardMarkup(
            [
                [KeyboardButton("📦 لیست محصولات"), KeyboardButton("🔎 جستجوی کالا")],
                [KeyboardButton("📂 دسته‌بندی‌ها"), KeyboardButton("💰 ثبت فروش")]
            ],
            resize_keyboard=True
        )

    return ReplyKeyboardMarkup(
        [
            [KeyboardButton("📦 لیست محصولات"), KeyboardButton("📂 دسته‌بندی‌ها")],
            [KeyboardButton("🔎 جستجوی کالا")]
        ],
        resize_keyboard=True
    )


def admin_menu():
    conn = db()
    pending_count = conn.execute("SELECT COUNT(*) AS c FROM sales WHERE status='pending'").fetchone()["c"]
    low_count = conn.execute("SELECT COUNT(*) AS c FROM products WHERE stock <= COALESCE(min_stock, 3)").fetchone()["c"]
    total_prods = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"]
    conn.close()

    pending_label = f"⏳ فروش‌های منتظر تایید ({pending_count})" if pending_count > 0 else "⏳ فروش‌های منتظر تایید"
    low_label = f"⚠️ کالاهای رو به اتمام ({low_count})" if low_count > 0 else "⚠️ کالاهای رو به اتمام"

    return InlineKeyboardMarkup([
        [
            InlineKeyboardButton("🌐 دریافت و آپدیت خودکار از سایت", callback_data="sync_site")
        ],
        [
            InlineKeyboardButton(pending_label, callback_data="pending_sales"),
            InlineKeyboardButton(low_label, callback_data="low_stock")
        ],
        [
            InlineKeyboardButton("➕ افزودن کالا", callback_data="add_product"),
            InlineKeyboardButton("📤 ایمپورت کالا (اکسل)", callback_data="import_excel")
        ],
        [
            InlineKeyboardButton("✏️ ویرایش کالا", callback_data="edit_product"),
            InlineKeyboardButton("🗑 حذف تکی کالا", callback_data="delete_product")
        ],
        [
            InlineKeyboardButton("🧨 حذف همه کالاها (پاکسازی انبار)", callback_data="delete_all_products")
        ],
        [
            InlineKeyboardButton(f"📦 همه کالاها ({total_prods})", callback_data="all_products"),
            InlineKeyboardButton("📂 دسته‌بندی‌ها", callback_data="categories")
        ],
        [
            InlineKeyboardButton("🔎 جستجو", callback_data="search"),
            InlineKeyboardButton("📈 تغییر درصدی قیمت", callback_data="markup")
        ],
        [
            InlineKeyboardButton("📊 گزارش فروش", callback_data="report"),
            InlineKeyboardButton("👥 مدیریت ادمین و فروشنده", callback_data="sellers")
        ],
        [
            InlineKeyboardButton("📥 اکسل کالاها", callback_data="export_products"),
            InlineKeyboardButton("📥 اکسل فروش", callback_data="export_sales")
        ],
        [
            InlineKeyboardButton("🏠 خانه", callback_data="home")
        ]
    ])


# =========================================================
# START & BASIC COMMANDS
# =========================================================

async def menu_commands(app):
    await app.bot.set_my_commands([
        ("start", "شروع و مشاهده محصولات"),
        ("search", "جستجوی کالا"),
        ("myid", "دریافت آیدی عددی من"),
    ])
    await app.bot.set_chat_menu_button(menu_button=MenuButtonCommands())


async def myid_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    await update.message.reply_text(
        f"👤 نام: {user.full_name}\n"
        f"🔑 آیدی عددی تلگرام شما:\n<code>{user.id}</code>",
        parse_mode="HTML"
    )


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    current_owner = get_owner_id()

    if current_owner is None:
        set_owner_id(user.id, user.full_name)
        await update.message.reply_text(
            f"👑 <b>تبریک! حساب شما ({user.full_name}) به عنوان «مالک و ادمین اصلی فروشگاه» ثبت شد.</b>\n\n"
            f"🔑 آیدی شما: <code>{user.id}</code>",
            parse_mode="HTML",
            reply_markup=main_menu(user.id)
        )
        return

    sync_staff_name(user)

    await update.message.reply_text(
        "سلام 👋\n"
        "به فروشگاه الکترونیک چشمک خوش آمدید.\n"
        "از منوی زیر می‌توانید لیست محصولات و دسته‌بندی‌ها را مشاهده کنید:",
        reply_markup=main_menu(user.id)
    )


async def admin_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    sync_staff_name(user)
    if not is_admin(user.id):
        await update.message.reply_text("⛔ شما به پنل مدیریت دسترسی ندارید.")
        return
    await update.message.reply_text("⚙️ پنل مدیریت", reply_markup=admin_menu())


# =========================================================
# STAFF MANAGEMENT (ADD/REMOVE ADMIN OR SELLER BY ID)
# =========================================================

ADD_STAFF_ID_STATE = 60


async def sellers(update, context, edit_existing=False):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()

    conn = db()
    rows = conn.execute("""
        SELECT s.user_id, s.name, s.active, s.role,
               COALESCE(SUM(CASE WHEN sa.status='approved' THEN sa.quantity ELSE 0 END), 0) AS qty,
               COALESCE(SUM(CASE WHEN sa.status='approved' THEN sa.total_price ELSE 0 END), 0) AS total,
               COALESCE(SUM(CASE WHEN sa.status='pending' THEN 1 ELSE 0 END), 0) AS pending_cnt
        FROM sellers s
        LEFT JOIN sales sa ON sa.seller_id=s.user_id
        WHERE s.active = 1
        GROUP BY s.user_id
        ORDER BY CASE WHEN s.role='admin' THEN 0 ELSE 1 END, total DESC
    """).fetchall()
    conn.close()

    owner_id = get_owner_id()
    text = (
        "👥 <b>مدیریت ادمین‌ها و فروشندگان</b>\n\n"
        "💡 <i>نکته: هر کس دستور /myid را به ربات بفرستد، آیدی عددی‌اش را دریافت می‌کند.</i>\n\n"
    )

    buttons = [
        [
            InlineKeyboardButton("➕ افزودن فروشنده (با آیدی)", callback_data="addstaff_seller"),
            InlineKeyboardButton("➕ افزودن ادمین (با آیدی)", callback_data="addstaff_admin")
        ]
    ]

    if not rows:
        text += "هنوز هیچ ادمین یا فروشنده‌ای ثبت نشده است."
    else:
        for row in rows:
            uid = row["user_id"]
            name = row["name"] or f"کاربر {uid}"
            if uid == owner_id:
                badge = "👑 مالک اصلی"
            elif row["role"] == "admin":
                badge = "🛡 ادمین"
            else:
                badge = "💼 فروشنده"

            text += (
                f"• <b>{name}</b> ({badge})\n"
                f"  آیدی: <code>{uid}</code>\n"
                f"  فروش قطعی: {row['qty']} عدد ({money(row['total'])} تومان)\n"
            )
            if row["pending_cnt"] > 0:
                text += f"  منتظر تایید: {row['pending_cnt']} مورد\n"
            text += "\n"

            if uid != owner_id:
                role_fa = "ادمین" if row["role"] == "admin" else "فروشنده"
                buttons.append([
                    InlineKeyboardButton(
                        f"🗑 حذف {role_fa}: {name} ({uid})",
                        callback_data=f"delstaff_{uid}"
                    )
                ])

    buttons.append([InlineKeyboardButton("⬅️ برگشت به پنل مدیریت", callback_data="back_admin")])
    markup = InlineKeyboardMarkup(buttons)

    if edit_existing:
        try:
            await query.message.edit_text(text, parse_mode="HTML", reply_markup=markup)
            return
        except Exception:
            pass

    await query.message.reply_text(text, parse_mode="HTML", reply_markup=markup)


async def add_staff_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("⛔ فقط ادمین دسترسی دارد.", show_alert=True)
        return ConversationHandler.END

    await query.answer()
    role = query.data.split("_")[1]
    context.user_data["new_staff_role"] = role

    role_fa = "🛡 ادمین (مدیر)" if role == "admin" else "💼 فروشنده"

    await query.message.reply_text(
        f"➕ <b>افزودن {role_fa} جدید</b>\n\n"
        "لطفاً <b>آیدی عددی (User ID)</b> شخص مورد نظر را وارد کنید:\n"
        "<i>(می‌توانید به او بگویید در ربات دستور /myid را بزند و عددش را برایتان بفرستد)</i>\n\n"
        "برای انصراف /cancel بزنید:",
        parse_mode="HTML"
    )
    return ADD_STAFF_ID_STATE


async def add_staff_receive_id(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if not is_admin(update.effective_user.id):
        return ConversationHandler.END

    text = update.message.text.strip()
    role = context.user_data.get("new_staff_role", "seller")

    try:
        target_id = int(text)
        if target_id <= 0:
            raise ValueError
    except ValueError:
        await update.message.reply_text(
            "❌ آیدی عددی نامعتبر است! فقط عدد انگلیسی (مثلاً <code>123456789</code>) وارد کنید یا /cancel بزنید:",
            parse_mode="HTML"
        )
        return ADD_STAFF_ID_STATE

    staff_name = f"کاربر {target_id}"
    try:
        chat = await context.bot.get_chat(target_id)
        if chat.full_name:
            staff_name = chat.full_name
    except Exception:
        pass

    conn = db()
    conn.execute("""
        INSERT OR REPLACE INTO sellers (user_id, name, active, role)
        VALUES (?, ?, 1, ?)
    """, (target_id, staff_name, role))
    conn.commit()
    conn.close()

    context.user_data.pop("new_staff_role", None)
    role_fa = "ادمین (مدیر)" if role == "admin" else "فروشنده"

    await update.message.reply_text(
        f"✅ کاربر <b>{staff_name}</b> با آیدی <code>{target_id}</code> به عنوان <b>«{role_fa}»</b> ثبت شد!",
        parse_mode="HTML",
        reply_markup=main_menu(update.effective_user.id)
    )

    try:
        await context.bot.send_message(
            chat_id=target_id,
            text=(
                f"🎉 <b>حساب شما توسط مدیریت به عنوان «{role_fa}» در فروشگاه فعال شد!</b>\n"
                "لطفاً یک بار روی /start بزنید تا منوی جدید برایتان باز شود."
            ),
            parse_mode="HTML",
            reply_markup=main_menu(target_id)
        )
    except Exception:
        pass

    return ConversationHandler.END


async def delete_staff_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("⛔ فقط ادمین دسترسی دارد.", show_alert=True)
        return

    target_id = int(query.data.split("_")[1])
    if target_id == get_owner_id():
        await query.answer("⛔ حساب مالک اصلی فروشگاه قابل حذف نیست!", show_alert=True)
        return

    conn = db()
    conn.execute("DELETE FROM sellers WHERE user_id=?", (target_id,))
    conn.commit()
    conn.close()

    await query.answer("✅ دسترسی کاربر حذف شد و به مشتری عادی تبدیل گردید.")

    try:
        await context.bot.send_message(
            chat_id=target_id,
            text="ℹ️ دسترسی همکاری شما توسط مدیریت غیرفعال شد.",
            reply_markup=main_menu(target_id)
        )
    except Exception:
        pass

    await sellers(update, context, edit_existing=True)


# =========================================================
# PENDING SALES APPROVAL & LOW STOCK ALERTS
# =========================================================

async def show_pending_sales(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return

    await query.answer()

    conn = db()
    rows = conn.execute("""
        SELECT sa.*, s.name AS seller_name
        FROM sales sa
        LEFT JOIN sellers s ON sa.seller_id = s.user_id
        WHERE sa.status = 'pending'
        ORDER BY sa.id ASC
    """).fetchall()
    conn.close()

    if not rows:
        await query.message.reply_text(
            "✅ هیچ درخواست فروشی در انتظار تایید نیست.",
            reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ برگشت به پنل", callback_data="back_admin")]])
        )
        return

    await query.message.reply_text(f"⏳ <b>لیست فروش‌های در انتظار تایید ({len(rows)} مورد):</b>", parse_mode="HTML")

    for r in rows:
        text = (
            f"🧾 <b>درخواست فروش #{r['id']}</b>\n"
            f"👤 فروشنده: {r['seller_name'] or r['seller_id']}\n"
            f"📦 کالا: <b>{r['product_name']}</b>\n"
            f"🔢 تعداد: {r['quantity']} عدد\n"
            f"💵 مبلغ کل: {money(r['total_price'])}\n"
            f"🕐 زمان: {fa_date(r['sold_at'])}"
        )
        markup = InlineKeyboardMarkup([
            [
                InlineKeyboardButton("✅ تایید فروش", callback_data=f"appsale_{r['id']}"),
                InlineKeyboardButton("❌ رد کردن", callback_data=f"rejsale_{r['id']}")
            ]
        ])
        await query.message.reply_text(text, parse_mode="HTML", reply_markup=markup)


async def approve_sale_callback(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("فقط ادمین اجازه تایید دارد.", show_alert=True)
        return

    sale_id = int(query.data.split("_")[1])
    conn = db()
    sale = conn.execute("SELECT * FROM sales WHERE id=?", (sale_id,)).fetchone()

    if not sale:
        conn.close()
        await query.answer("این درخواست پیدا نشد.", show_alert=True)
        return

    if sale["status"] != "pending":
        conn.close()
        status_fa = "تایید شده" if sale["status"] == "approved" else "رد شده"
        await query.answer(f"این درخواست قبلاً {status_fa} است.", show_alert=True)
        return

    product = conn.execute("SELECT * FROM products WHERE id=?", (sale["product_id"],)).fetchone()
    if not product:
        conn.close()
        await query.answer("❌ این کالا از انبار حذف شده است!", show_alert=True)
        return

    if product["stock"] < sale["quantity"]:
        conn.close()
        await query.answer(
            f"❌ موجودی انبار کافی نیست! (موجودی فعلی: {product['stock']}، تعداد درخواستی: {sale['quantity']})",
            show_alert=True
        )
        return

    conn.execute("UPDATE products SET stock = stock - ? WHERE id = ?", (sale["quantity"], sale["product_id"]))
    conn.execute("UPDATE sales SET status = 'approved' WHERE id = ?", (sale_id,))
    conn.commit()
    conn.close()

    new_product = get_product(sale["product_id"])
    warn = stock_warning_text(new_product)

    await query.answer("✅ فروش تایید شد.")

    updated_text = (
        f"✅ <b>فروش #{sale_id} تایید و ثبت نهایی شد.</b>\n\n"
        f"📦 کالا: {sale['product_name']}\n"
        f"🔢 تعداد: {sale['quantity']} عدد\n"
        f"💵 مبلغ کل: {money(sale['total_price'])}\n"
        f"📦 موجودی جدید انبار: {new_product['stock']} عدد"
        f"{warn}"
    )
    try:
        await query.message.edit_text(updated_text, parse_mode="HTML")
    except Exception:
        await query.message.reply_text(updated_text, parse_mode="HTML")

    try:
        await context.bot.send_message(
            chat_id=sale["seller_id"],
            text=(
                f"✅ <b>فروش شما توسط مدیر تایید شد!</b>\n\n"
                f"🧾 کد پیگیری: #{sale_id}\n"
                f"📦 کالا: {sale['product_name']}\n"
                f"🔢 تعداد: {sale['quantity']} عدد\n"
                f"💵 مبلغ کل: {money(sale['total_price'])}\n"
                f"📦 موجودی باقی‌مانده: {new_product['stock']}"
            ),
            parse_mode="HTML"
        )
    except Exception:
        pass


async def reject_sale_callback(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("فقط ادمین اجازه رد کردن دارد.", show_alert=True)
        return

    sale_id = int(query.data.split("_")[1])
    conn = db()
    sale = conn.execute("SELECT * FROM sales WHERE id=?", (sale_id,)).fetchone()

    if not sale:
        conn.close()
        await query.answer("این درخواست پیدا نشد.", show_alert=True)
        return

    if sale["status"] != "pending":
        conn.close()
        status_fa = "تایید شده" if sale["status"] == "approved" else "رد شده"
        await query.answer(f"این درخواست قبلاً {status_fa} است.", show_alert=True)
        return

    conn.execute("UPDATE sales SET status = 'rejected' WHERE id = ?", (sale_id,))
    conn.commit()
    conn.close()

    await query.answer("❌ درخواست فروش رد شد.")

    updated_text = (
        f"❌ <b>درخواست فروش #{sale_id} رد شد (موجودی کسر نشد).</b>\n\n"
        f"📦 کالا: {sale['product_name']} ({sale['quantity']} عدد)\n"
        f"💵 مبلغ: {money(sale['total_price'])}"
    )
    try:
        await query.message.edit_text(updated_text, parse_mode="HTML")
    except Exception:
        await query.message.reply_text(updated_text, parse_mode="HTML")

    try:
        await context.bot.send_message(
            chat_id=sale["seller_id"],
            text=(
                f"❌ <b>درخواست فروش شما توسط مدیر رد شد.</b>\n\n"
                f"🧾 کد پیگیری: #{sale_id}\n"
                f"📦 کالا: {sale['product_name']} ({sale['quantity']} عدد)"
            ),
            parse_mode="HTML"
        )
    except Exception:
        pass


async def show_low_stock(update, context, page=0):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()

    conn = db()
    total = conn.execute("SELECT COUNT(*) AS c FROM products WHERE stock <= COALESCE(min_stock, 3)").fetchone()["c"]
    rows = conn.execute("""
        SELECT * FROM products
        WHERE stock <= COALESCE(min_stock, 3)
        ORDER BY stock ASC, id DESC
        LIMIT ? OFFSET ?
    """, (PAGE_SIZE, page * PAGE_SIZE)).fetchall()
    conn.close()

    if not rows and page == 0:
        await query.message.reply_text(
            "✅ وضعیت انبار عالی است! هیچ کالایی به حد هشدار نرسیده است.",
            reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("⬅️ برگشت به پنل", callback_data="back_admin")]])
        )
        return

    buttons = []
    for r in rows:
        limit = r["min_stock"] if r["min_stock"] is not None else DEFAULT_MIN_STOCK
        badge = "🚨 ناموجود (۰)" if r["stock"] == 0 else f"⚠️ موجودی: {r['stock']} (حد: {limit})"
        buttons.append([
            InlineKeyboardButton(
                f"{r['name']} | {badge}",
                callback_data=f"product_{r['id']}"
            )
        ])

    nav = []
    if page > 0:
        nav.append(InlineKeyboardButton("➡️ صفحه قبل", callback_data=f"lowpage_{page - 1}"))
    if (page + 1) * PAGE_SIZE < total:
        nav.append(InlineKeyboardButton("⬅️ صفحه بعد", callback_data=f"lowpage_{page + 1}"))
    if nav:
        buttons.append(nav)

    buttons.append([InlineKeyboardButton("⬅️ برگشت به پنل", callback_data="back_admin")])
    total_pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE)

    text = (
        f"⚠️ <b>کالاهای رو به اتمام یا ناموجود (کل: {total} مورد - صفحه {page + 1} از {total_pages}):</b>\n"
        "برای مشاهده یا ویرایش روی کالا بزنید:"
    )

    if query.data.startswith("lowpage_"):
        try:
            await query.message.edit_text(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))
            return
        except Exception:
            pass

    await query.message.reply_text(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))


# =========================================================
# SEARCH (OPEN TO CUSTOMERS & STAFF)
# =========================================================

SEARCH_QUERY = 1


async def search_start(update, context):
    staff = is_seller(update.effective_user.id)
    prompt = "نام، کد یا بارکد کالا را وارد کن:" if staff else "🔎 نام کالای مورد نظر را وارد کنید:"

    if update.callback_query:
        await update.callback_query.answer()
        await update.callback_query.message.reply_text(prompt)
    else:
        await update.message.reply_text(prompt)
    return SEARCH_QUERY


async def search_text(update, context):
    text = update.message.text.strip()
    staff = is_seller(update.effective_user.id)
    conn = db()

    if staff:
        rows = conn.execute("""
            SELECT * FROM products
            WHERE name LIKE ? OR code LIKE ? OR barcode LIKE ?
            ORDER BY id DESC LIMIT 40
        """, (f"%{text}%", f"%{text}%", f"%{text}%")).fetchall()
    else:
        rows = conn.execute("""
            SELECT * FROM products
            WHERE name LIKE ? OR category LIKE ?
            ORDER BY id DESC LIMIT 40
        """, (f"%{text}%", f"%{text}%")).fetchall()

    conn.close()

    if not rows:
        await update.message.reply_text("❌ کالایی با این مشخصات پیدا نشد.")
        return ConversationHandler.END

    buttons = [
        [InlineKeyboardButton(f"{r['name']} | {money(r['price'])} تومان", callback_data=f"product_{r['id']}")]
        for r in rows
    ]
    buttons.append([InlineKeyboardButton("❌ بستن", callback_data="home")])

    await update.message.reply_text("🔎 نتایج جستجو:", reply_markup=InlineKeyboardMarkup(buttons))
    return ConversationHandler.END


# =========================================================
# ADD PRODUCT (WITH CUSTOM MIN_STOCK)
# =========================================================

ADD_NAME = 10
ADD_CODE = 11
ADD_BARCODE = 12
ADD_BASE = 13
ADD_STOCK = 14
ADD_MIN_STOCK = 15
ADD_CATEGORY = 16
ADD_DESCRIPTION = 17
ADD_IMAGE = 18


async def add_start(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return ConversationHandler.END

    await query.answer()
    await query.message.reply_text("➕ افزودن کالا\n\nنام کالا را وارد کن:")
    return ADD_NAME


async def add_name(update, context):
    context.user_data["add"] = {"name": normalize_name(update.message.text)}
    await update.message.reply_text("کد کالا را وارد کن یا /skip بزن:")
    return ADD_CODE


async def add_code(update, context):
    value = update.message.text.strip()
    context.user_data["add"]["code"] = None if value == "/skip" else value
    await update.message.reply_text("بارکد را وارد کن یا /skip بزن:")
    return ADD_BARCODE


async def add_barcode(update, context):
    value = update.message.text.strip()
    context.user_data["add"]["barcode"] = None if value == "/skip" else value
    await update.message.reply_text("قیمت پایه را به تومان وارد کن:")
    return ADD_BASE


async def add_base(update, context):
    try:
        value = float(update.message.text.replace(",", "").replace("٬", "").strip())
        if value < 0:
            raise ValueError
    except ValueError:
        await update.message.reply_text("❌ قیمت نامعتبر است.\nفقط عدد وارد کن:")
        return ADD_BASE

    context.user_data["add"]["base_price"] = value
    await update.message.reply_text("موجودی اولیه انبار را وارد کن:")
    return ADD_STOCK


async def add_stock(update, context):
    try:
        value = int(update.message.text.strip())
        if value < 0:
            raise ValueError
    except ValueError:
        await update.message.reply_text("❌ موجودی باید عدد صحیح باشد:")
        return ADD_STOCK

    context.user_data["add"]["stock"] = value
    await update.message.reply_text(
        "⚠️ <b>حد هشدار اتمام موجودی:</b>\n"
        "موجودی این کالا به چند عدد (یا کمتر) رسید هشدار بدهم؟\n"
        f"(عدد وارد کن یا برای پیش‌فرضِ {DEFAULT_MIN_STOCK} عدد، /skip بزن):",
        parse_mode="HTML"
    )
    return ADD_MIN_STOCK


async def add_min_stock(update, context):
    text = update.message.text.strip()
    if text == "/skip":
        value = DEFAULT_MIN_STOCK
    else:
        try:
            value = int(text)
            if value < 0:
                raise ValueError
        except ValueError:
            await update.message.reply_text("❌ لطفاً یک عدد صحیح وارد کن یا /skip بزن:")
            return ADD_MIN_STOCK

    context.user_data["add"]["min_stock"] = value
    await update.message.reply_text("دسته‌بندی را وارد کن یا /skip بزن:")
    return ADD_CATEGORY


async def add_category(update, context):
    value = update.message.text.strip()
    context.user_data["add"]["category"] = None if value == "/skip" else value
    await update.message.reply_text("توضیحات را وارد کن یا /skip بزن:")
    return ADD_DESCRIPTION


async def add_description(update, context):
    value = update.message.text.strip()
    context.user_data["add"]["description"] = None if value == "/skip" else value
    await update.message.reply_text("عکس کالا را بفرست یا /skip بزن:")
    return ADD_IMAGE


async def add_image(update, context):
    data = context.user_data["add"]
    if update.message.photo:
        image_id = update.message.photo[-1].file_id
    elif update.message.text == "/skip":
        image_id = None
    else:
        await update.message.reply_text("❌ عکس بفرست یا /skip بزن.")
        return ADD_IMAGE

    data["image_id"] = image_id
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    final_price = calc_price(data["base_price"])

    conn = db()
    try:
        # بررسی عدم وجود کالای تکراری با همین کد یا نام
        existing = None
        if data["code"]:
            existing = conn.execute("SELECT id FROM products WHERE code=?", (data["code"],)).fetchone()
        if not existing:
            existing = conn.execute("SELECT id FROM products WHERE TRIM(name)=?", (data["name"],)).fetchone()

        if existing:
            conn.execute("""
                UPDATE products
                SET name=?, code=?, barcode=?, base_price=?, price=?,
                    stock=?, min_stock=?, category=?, description=?, image_id=?, price_updated_at=?
                WHERE id=?
            """, (
                data["name"], data["code"], data["barcode"], data["base_price"],
                final_price, data["stock"], data["min_stock"], data["category"],
                data["description"], data["image_id"], now, existing["id"]
            ))
            action_msg = "🔄 این کالا از قبل وجود داشت و اطلاعاتش بروزرسانی شد."
        else:
            conn.execute("""
                INSERT INTO products (
                    name, code, barcode, base_price, price,
                    stock, min_stock, category, description, image_id, price_updated_at
                )
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                data["name"], data["code"], data["barcode"], data["base_price"],
                final_price, data["stock"], data["min_stock"], data["category"],
                data["description"], data["image_id"], now
            ))
            action_msg = "✅ کالای جدید با موفقیت ثبت شد."

        conn.commit()
    except sqlite3.IntegrityError:
        conn.rollback()
        conn.close()
        await update.message.reply_text("❌ این کد کالا قبلاً ثبت شده است.")
        return ConversationHandler.END

    conn.close()
    context.user_data.pop("add", None)

    await update.message.reply_text(
        f"{action_msg}\n\n"
        f"📦 {data['name']}\n"
        f"💰 قیمت نهایی: {money(final_price)}\n"
        f"📦 موجودی: {data['stock']}\n"
        f"⚠️ حد هشدار: {data['min_stock']} عدد",
        reply_markup=main_menu(update.effective_user.id)
    )
    return ConversationHandler.END


# =========================================================
# IMPORT PRODUCTS FROM EXCEL (NO DUPLICATES)
# =========================================================

IMPORT_FILE_STATE = 50


async def import_excel_start(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return ConversationHandler.END

    await query.answer()

    text = (
        "📤 <b>ایمپورت و آپدیت کالا از فایل اکسل (.xlsx)</b>\n\n"
        "فایل اکسل خود را ارسال کنید. اگر کالایی (با همان کد یا همان نام) از قبل در ربات باشد، <b>تکراری اضافه نمی‌شود</b> بلکه آپدیت می‌گردد.\n\n"
        "📋 <b>ستون‌های قالب استاندارد (از راست به چپ):</b>\n"
        "1️⃣ نام کالا (اجباری)\n"
        "2️⃣ کد کالا (اختیاری)\n"
        "3️⃣ بارکد (اختیاری)\n"
        "4️⃣ قیمت پایه به تومان (اجباری)\n"
        "5️⃣ موجودی (اجباری)\n"
        "6️⃣ حد هشدار موجودی (اختیاری - پیش‌فرض ۳)\n"
        "7️⃣ دسته‌بندی (اختیاری)\n"
        "8️⃣ توضیحات (اختیاری)\n\n"
        "👇 <i>فایل اکسل را بفرست یا برای انصراف /cancel بزن:</i>"
    )

    markup = InlineKeyboardMarkup([
        [InlineKeyboardButton("📄 دانلود فایل اکسل نمونه", callback_data="sample_excel")],
        [InlineKeyboardButton("❌ انصراف", callback_data="home")]
    ])

    await query.message.reply_text(text, parse_mode="HTML", reply_markup=markup)
    return IMPORT_FILE_STATE


async def send_sample_excel(update, context):
    query = update.callback_query
    await query.answer("در حال ساخت فایل نمونه...")

    wb = Workbook()
    ws = wb.active
    ws.title = "Products_Import"
    ws.sheet_view.rightToLeft = True

    ws.append([
        "نام کالا", "کد کالا", "بارکد",
        "قیمت پایه", "موجودی", "حد هشدار", "دسته‌بندی", "توضیحات"
    ])
    ws.append([
        "ریسه LED تراکم 240 نکسو NEXO", "NX-240", "6260000001",
        2200000, 30, 5, "ریسه نوری", "12 ولت - رول 10 متری"
    ])
    ws.append([
        "صفحه دی او بی پرژکتوری 100 وات LY", "DOB-100W", "6260000002",
        700000, 20, 3, "صفحه دی او بی", "220 ولت - نور سفید"
    ])

    bio = io.BytesIO()
    wb.save(bio)
    bio.seek(0)

    await query.message.reply_document(
        document=bio,
        filename="sample_import_products.xlsx",
        caption="📄 <b>فایل نمونه ایمپورت کالا (همراه با ستون حد هشدار)</b>",
        parse_mode="HTML"
    )
    return IMPORT_FILE_STATE


def clean_cell(val):
    if val is None:
        return None
    s = str(val).strip()
    if not s or s.lower() == "none":
        return None
    if s.endswith(".0") and s[:-2].isdigit():
        return s[:-2]
    return s


def parse_number(val, default=0.0):
    if val is None:
        return default
    s = str(val).replace(",", "").replace("٬", "").strip()
    if not s:
        return default
    return float(s)


async def import_excel_receive(update, context):
    doc = update.message.document
    if not doc or not doc.file_name.lower().endswith(".xlsx"):
        await update.message.reply_text("❌ لطفاً یک فایل اکسل معتبر با پسوند <b>.xlsx</b> ارسال کنید (یا /cancel بزنید):", parse_mode="HTML")
        return IMPORT_FILE_STATE

    status_msg = await update.message.reply_text("⏳ در حال دریافت و پردازش فایل اکسل...")

    try:
        tg_file = await doc.get_file()
        bio = io.BytesIO()
        await tg_file.download_to_memory(out=bio)
        bio.seek(0)

        wb = load_workbook(bio, data_only=True)
        ws = wb.active
        rows = list(ws.iter_rows(values_only=True))

        if len(rows) < 2:
            await status_msg.edit_text("❌ فایل اکسل خالی است یا فقط ردیف عنوان دارد.")
            return ConversationHandler.END

        headers = [str(h).strip() if h is not None else "" for h in rows[0]]
        is_exported_format = ("شناسه" in headers and "قیمت نهایی" in headers)
        has_min_stock_col = ("حد هشدار" in headers)

        conn = db()
        remove_duplicate_products(conn)
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        markup = get_markup()

        added = 0
        updated = 0
        errors = 0

        for row in rows[1:]:
            if not row or all(c is None or str(c).strip() == "" for c in row):
                continue

            try:
                if is_exported_format:
                    raw_name = clean_cell(row[1]) if len(row) > 1 else None
                    code = clean_cell(row[2]) if len(row) > 2 else None
                    barcode = clean_cell(row[3]) if len(row) > 3 else None
                    base_price = parse_number(row[4], 0.0) if len(row) > 4 else 0.0
                    stock = int(parse_number(row[6], 0)) if len(row) > 6 else 0
                    if has_min_stock_col:
                        min_stock = int(parse_number(row[7], DEFAULT_MIN_STOCK)) if len(row) > 7 else DEFAULT_MIN_STOCK
                        category = clean_cell(row[8]) if len(row) > 8 else None
                        description = clean_cell(row[9]) if len(row) > 9 else None
                    else:
                        min_stock = DEFAULT_MIN_STOCK
                        category = clean_cell(row[7]) if len(row) > 7 else None
                        description = clean_cell(row[8]) if len(row) > 8 else None
                else:
                    raw_name = clean_cell(row[0]) if len(row) > 0 else None
                    code = clean_cell(row[1]) if len(row) > 1 else None
                    barcode = clean_cell(row[2]) if len(row) > 2 else None
                    base_price = parse_number(row[3], 0.0) if len(row) > 3 else 0.0
                    stock = int(parse_number(row[4], 0)) if len(row) > 4 else 0
                    if has_min_stock_col:
                        min_stock = int(parse_number(row[5], DEFAULT_MIN_STOCK)) if len(row) > 5 else DEFAULT_MIN_STOCK
                        category = clean_cell(row[6]) if len(row) > 6 else None
                        description = clean_cell(row[7]) if len(row) > 7 else None
                    else:
                        min_stock = DEFAULT_MIN_STOCK
                        category = clean_cell(row[5]) if len(row) > 5 else None
                        description = clean_cell(row[6]) if len(row) > 6 else None

                name = normalize_name(raw_name)
                if not name or base_price < 0 or stock < 0:
                    errors += 1
                    continue

                final_price = base_price * (1 + markup / 100.0)

                existing = None
                if code:
                    existing = conn.execute("SELECT id, code FROM products WHERE code=?", (code,)).fetchone()
                if not existing:
                    existing = conn.execute("SELECT id, code FROM products WHERE TRIM(name)=?", (name,)).fetchone()

                if existing:
                    final_code = code or existing["code"]
                    conn.execute("""
                        UPDATE products
                        SET name=?, code=?, barcode=?, base_price=?, price=?,
                            stock=?, min_stock=?, category=?, description=?, price_updated_at=?
                        WHERE id=?
                    """, (
                        name, final_code, barcode, base_price, final_price,
                        stock, min_stock, category, description, now, existing["id"]
                    ))
                    updated += 1
                else:
                    conn.execute("""
                        INSERT INTO products (
                            name, code, barcode, base_price, price,
                            stock, min_stock, category, description, price_updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    """, (
                        name, code, barcode, base_price, final_price,
                        stock, min_stock, category, description, now
                    ))
                    added += 1

            except Exception:
                errors += 1

        conn.commit()
        conn.close()

        result_text = (
            "✅ <b>عملیات ایمپورت اکسل به پایان رسید!</b>\n\n"
            f"➕ کالاهای جدید اضافه‌شده: <b>{added}</b>\n"
            f"🔄 کالاهای قبلی بروزرسانی‌شده: <b>{updated}</b>\n"
        )
        if errors > 0:
            result_text += f"⚠️ ردیف‌های رد شده (ناقص/خطا): <b>{errors}</b>\n"

        await status_msg.edit_text(result_text, parse_mode="HTML")

    except Exception as e:
        await status_msg.edit_text(f"❌ خطا در خواندن فایل اکسل:\n<code>{e}</code>", parse_mode="HTML")

    return ConversationHandler.END


# =========================================================
# EDIT PRODUCT
# =========================================================

EDIT_FIND = 20
EDIT_FIELD = 21
EDIT_VALUE = 22


async def edit_start(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return ConversationHandler.END

    await query.answer()
    await query.message.reply_text("نام، کد یا بارکد کالایی که می‌خواهی ویرایش کنی را وارد کن:")
    return EDIT_FIND


async def edit_find(update, context):
    text = update.message.text.strip()
    conn = db()
    rows = conn.execute("""
        SELECT * FROM products
        WHERE name LIKE ? OR code LIKE ? OR barcode LIKE ?
        ORDER BY id DESC LIMIT 40
    """, (f"%{text}%", f"%{text}%", f"%{text}%")).fetchall()
    conn.close()

    if not rows:
        await update.message.reply_text("❌ کالایی پیدا نشد.")
        return ConversationHandler.END

    buttons = [
        [InlineKeyboardButton(f"{r['name']} | {money(r['price'])}", callback_data=f"editpick_{r['id']}")]
        for r in rows
    ]
    await update.message.reply_text("کالایی که می‌خواهی ویرایش کنی را انتخاب کن:", reply_markup=InlineKeyboardMarkup(buttons))
    return EDIT_FIELD


async def edit_pick(update, context):
    query = update.callback_query
    await query.answer()
    product_id = int(query.data.split("_")[1])
    product = get_product(product_id)

    if not product:
        await query.message.reply_text("❌ کالا پیدا نشد.")
        return ConversationHandler.END

    context.user_data["edit_id"] = product_id
    await query.message.reply_text(
        f"✏️ ویرایش: {product['name']}\n\nکدام قسمت را می‌خواهی تغییر بدهی؟",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("نام", callback_data="ef_name"), InlineKeyboardButton("کد", callback_data="ef_code")],
            [InlineKeyboardButton("بارکد", callback_data="ef_barcode"), InlineKeyboardButton("قیمت پایه", callback_data="ef_base")],
            [InlineKeyboardButton("موجودی", callback_data="ef_stock"), InlineKeyboardButton("⚠️ حد هشدار", callback_data="ef_min_stock")],
            [InlineKeyboardButton("دسته", callback_data="ef_category"), InlineKeyboardButton("توضیحات", callback_data="ef_description")],
            [InlineKeyboardButton("عکس", callback_data="ef_image"), InlineKeyboardButton("❌ لغو", callback_data="home")]
        ])
    )
    return EDIT_VALUE


async def edit_field(update, context):
    query = update.callback_query
    await query.answer()
    field = query.data.replace("ef_", "")
    context.user_data["edit_field"] = field

    messages = {
        "name": "نام جدید را وارد کن:",
        "code": "کد جدید را وارد کن:",
        "barcode": "بارکد جدید را وارد کن:",
        "base": "قیمت پایه جدید را وارد کن:",
        "stock": "موجودی جدید را وارد کن:",
        "min_stock": "حد هشدار جدید را وارد کن (موجودی به چه عددی رسید هشدار بدهد؟):",
        "category": "دسته جدید را وارد کن:",
        "description": "توضیحات جدید را وارد کن:",
        "image": "عکس جدید را بفرست:"
    }
    await query.message.reply_text(messages[field])
    return EDIT_VALUE


async def edit_value(update, context):
    product_id = context.user_data.get("edit_id")
    field = context.user_data.get("edit_field")

    if not product_id or not field:
        await update.message.reply_text("❌ عملیات ویرایش منقضی شده است.")
        return ConversationHandler.END

    product = get_product(product_id)
    if not product:
        await update.message.reply_text("❌ کالا پیدا نشد.")
        return ConversationHandler.END

    conn = db()
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    try:
        if field == "image":
            if not update.message.photo:
                await update.message.reply_text("❌ لطفاً عکس بفرست.")
                conn.close()
                return EDIT_VALUE
            value = update.message.photo[-1].file_id
            conn.execute("UPDATE products SET image_id=? WHERE id=?", (value, product_id))

        elif field == "base":
            value = float(update.message.text.replace(",", "").replace("٬", "").strip())
            if value < 0:
                raise ValueError
            final_price = value * (1 + get_markup() / 100)
            conn.execute("""
                UPDATE products SET base_price=?, price=?, price_updated_at=? WHERE id=?
            """, (value, final_price, now, product_id))

        elif field in ("stock", "min_stock"):
            value = int(update.message.text.strip())
            if value < 0:
                raise ValueError
            conn.execute(f"UPDATE products SET {field}=? WHERE id=?", (value, product_id))

        elif field in ("name", "code", "barcode", "category", "description"):
            value = update.message.text.strip()
            if value == "/skip":
                value = None
            elif field == "name":
                value = normalize_name(value)
            conn.execute(f"UPDATE products SET {field}=? WHERE id=?", (value, product_id))
        else:
            raise ValueError

        conn.commit()
    except ValueError:
        conn.rollback()
        conn.close()
        await update.message.reply_text("❌ مقدار واردشده نامعتبر است.\nدوباره وارد کن:")
        return EDIT_VALUE
    except sqlite3.IntegrityError:
        conn.rollback()
        conn.close()
        await update.message.reply_text("❌ این کد قبلاً برای کالای دیگری ثبت شده.")
        return ConversationHandler.END

    conn.close()
    context.user_data.pop("edit_id", None)
    context.user_data.pop("edit_field", None)

    await update.message.reply_text("✅ ویرایش با موفقیت انجام شد.", reply_markup=main_menu(update.effective_user.id))
    return ConversationHandler.END


# =========================================================
# GLOBAL PRICE PERCENTAGE
# =========================================================

MARKUP_VALUE = 30


async def markup_start(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return ConversationHandler.END

    await query.answer()
    current = get_markup()
    await query.message.reply_text(
        f"📈 درصد فعلی: {current:g}%\n\n"
        "درصد جدید را وارد کن.\n\n"
        "مثال:\n5 = افزایش ۵٪\n-5 = کاهش ۵٪\n0 = بدون افزایش"
    )
    return MARKUP_VALUE


async def markup_value(update, context):
    try:
        value = float(update.message.text.replace("%", "").strip())
        if value < -100:
            raise ValueError
    except ValueError:
        await update.message.reply_text("❌ درصد نامعتبر است.\nمثلاً 5 یا -5 وارد کن:")
        return MARKUP_VALUE

    set_markup(value)
    await update.message.reply_text(
        f"✅ درصد کلی قیمت روی {value:g}% تنظیم شد.\n\n"
        "قیمت همه کالاها دوباره از روی قیمت پایه محاسبه شد.",
        reply_markup=main_menu(update.effective_user.id)
    )
    return ConversationHandler.END


# =========================================================
# CUSTOM QUANTITY SALE CONVERSATION
# =========================================================

SELL_QTY_STATE = 40


async def sell_qty_start(update, context):
    query = update.callback_query
    if not is_seller(update.effective_user.id):
        await query.answer("⛔ شما دسترسی فروش ندارید.", show_alert=True)
        return ConversationHandler.END

    await query.answer()

    product_id = int(query.data.split("_")[1])
    product = get_product(product_id)

    if not product:
        await query.message.reply_text("❌ کالا پیدا نشد.")
        return ConversationHandler.END

    if product["stock"] <= 0:
        await query.message.reply_text("❌ موجودی این کالا تمام شده است.")
        return ConversationHandler.END

    context.user_data["sell_product_id"] = product_id

    await query.message.reply_text(
        f"📦 کالا: <b>{product['name']}</b>\n"
        f"📦 موجودی فعلی: {product['stock']} عدد\n"
        f"💰 قیمت واحد: {money(product['price'])}\n\n"
        "🔢 <b>تعداد فروش را وارد کن:</b>\n"
        "(برای انصراف /cancel بزن)",
        parse_mode="HTML"
    )
    return SELL_QTY_STATE


async def sell_qty_receive(update, context):
    if not is_seller(update.effective_user.id):
        return ConversationHandler.END

    product_id = context.user_data.get("sell_product_id")
    if not product_id:
        await update.message.reply_text("❌ عملیات منقضی شده است.")
        return ConversationHandler.END

    try:
        qty = int(update.message.text.strip())
        if qty <= 0:
            raise ValueError
    except ValueError:
        await update.message.reply_text("❌ لطفاً یک عدد صحیح و بزرگتر از صفر وارد کن:")
        return SELL_QTY_STATE

    ok, msg = await process_sale_request(product_id, qty, update.effective_user, context)
    if not ok:
        await update.message.reply_text(msg + "\n\nدوباره تعداد را وارد کن یا /cancel بزن:")
        return SELL_QTY_STATE

    context.user_data.pop("sell_product_id", None)
    await update.message.reply_text(msg, parse_mode="HTML", reply_markup=main_menu(update.effective_user.id))
    return ConversationHandler.END


# =========================================================
# CATEGORIES, ALL PRODUCTS (PAGINATED), DETAILS
# =========================================================

def get_categories():
    conn = db()
    rows = conn.execute("""
        SELECT DISTINCT category FROM products
        WHERE category IS NOT NULL AND TRIM(category) != ''
        ORDER BY category
    """).fetchall()
    conn.close()
    return [row["category"] for row in rows]


async def categories(update, context):
    if update.callback_query:
        query = update.callback_query
        await query.answer()
        message = query.message
    else:
        message = update.message

    cats = get_categories()
    if not cats:
        await message.reply_text("📂 هنوز هیچ دسته‌بندی‌ای ثبت نشده.")
        return

    buttons = [[InlineKeyboardButton(c, callback_data=f"catidx_{i}_0")] for i, c in enumerate(cats)]
    buttons.append([InlineKeyboardButton("🏠 خانه", callback_data="home")])
    await message.reply_text("📂 دسته‌بندی‌ها:", reply_markup=InlineKeyboardMarkup(buttons))


async def show_category(update, context):
    query = update.callback_query
    await query.answer()
    parts = query.data.split("_")
    index = int(parts[1])
    page = int(parts[2]) if len(parts) > 2 else 0

    cats = get_categories()
    if index >= len(cats):
        await query.message.reply_text("❌ دسته‌بندی پیدا نشد.")
        return

    category = cats[index]
    conn = db()
    total = conn.execute("SELECT COUNT(*) AS c FROM products WHERE category=?", (category,)).fetchone()["c"]
    rows = conn.execute(
        "SELECT * FROM products WHERE category=? ORDER BY id DESC LIMIT ? OFFSET ?",
        (category, PAGE_SIZE, page * PAGE_SIZE)
    ).fetchall()
    conn.close()

    if not rows and page == 0:
        await query.message.reply_text("❌ کالایی در این دسته وجود ندارد.")
        return

    buttons = [[InlineKeyboardButton(f"{r['name']} | {money(r['price'])} تومان", callback_data=f"product_{r['id']}")] for r in rows]

    nav = []
    if page > 0:
        nav.append(InlineKeyboardButton("➡️ صفحه قبل", callback_data=f"catidx_{index}_{page - 1}"))
    if (page + 1) * PAGE_SIZE < total:
        nav.append(InlineKeyboardButton("⬅️ صفحه بعد", callback_data=f"catidx_{index}_{page + 1}"))
    if nav:
        buttons.append(nav)

    buttons.append([InlineKeyboardButton("⬅️ دسته‌بندی‌ها", callback_data="categories")])
    total_pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE)
    text = f"📂 <b>{category}</b> (کالاها: {total} | صفحه {page + 1} از {total_pages}):"

    if len(parts) > 2:
        try:
            await query.message.edit_text(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))
            return
        except Exception:
            pass

    await query.message.reply_text(text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))


async def all_products(update, context, page=0):
    if update.callback_query:
        query = update.callback_query
        await query.answer()
        message = query.message
    else:
        query = None
        message = update.message

    staff = is_seller(update.effective_user.id)

    conn = db()
    total = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"]
    rows = conn.execute(
        "SELECT * FROM products ORDER BY id DESC LIMIT ? OFFSET ?",
        (PAGE_SIZE, page * PAGE_SIZE)
    ).fetchall()
    conn.close()

    if not rows and page == 0:
        await message.reply_text("📦 هنوز کالایی ثبت نشده است.")
        return

    buttons = []
    for r in rows:
        if staff:
            limit = r["min_stock"] if r["min_stock"] is not None else DEFAULT_MIN_STOCK
            warn_icon = "🚨 " if r["stock"] == 0 else ("⚠️ " if r["stock"] <= limit else "")
            btn_text = f"{warn_icon}{r['name']} | {money(r['price'])} | موجودی {r['stock']}"
        else:
            btn_text = f"📦 {r['name']} | {money(r['price'])} تومان"

        buttons.append([InlineKeyboardButton(btn_text, callback_data=f"product_{r['id']}")])

    nav = []
    if page > 0:
        nav.append(InlineKeyboardButton("➡️ صفحه قبل", callback_data=f"prodpage_{page - 1}"))
    if (page + 1) * PAGE_SIZE < total:
        nav.append(InlineKeyboardButton("⬅️ صفحه بعد", callback_data=f"prodpage_{page + 1}"))
    if nav:
        buttons.append(nav)

    buttons.append([InlineKeyboardButton("🏠 خانه", callback_data="home")])
    total_pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE)
    header_text = f"📦 <b>لیست محصولات</b> (مجموع: {total} کالا | صفحه {page + 1} از {total_pages}):"

    if query and query.data.startswith("prodpage_"):
        try:
            await query.message.edit_text(header_text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))
            return
        except Exception:
            pass

    await message.reply_text(header_text, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(buttons))


async def product_details(update, context):
    query = update.callback_query
    await query.answer()
    product_id = int(query.data.split("_")[1])
    product = get_product(product_id)

    if not product:
        await query.message.reply_text("❌ کالا پیدا نشد.")
        return

    staff = is_seller(update.effective_user.id)

    if staff:
        limit = product["min_stock"] if product["min_stock"] is not None else DEFAULT_MIN_STOCK
        warn_line = stock_warning_text(product)

        text = (
            f"📦 <b>{product['name']}</b>\n\n"
            f"🔢 کد: {product['code'] or '-'}\n"
            f"🏷 بارکد: {product['barcode'] or '-'}\n"
            f"💰 قیمت: {money(product['price'])} تومان\n"
            f"📦 موجودی: {product['stock']} (حد هشدار: {limit})\n"
            f"📂 دسته: {product['category'] or '-'}\n"
            f"🕐 آخرین تغییر قیمت: {fa_date(product['price_updated_at'])}"
            f"{warn_line}\n"
        )
        if product["description"]:
            text += f"\n📝 توضیحات: {product['description']}\n"

        buttons = [
            [
                InlineKeyboardButton("💰 فروش سریع (۱ عدد)", callback_data=f"sell_{product_id}"),
                InlineKeyboardButton("🔢 فروش با تعداد دلخواه", callback_data=f"sellqty_{product_id}")
            ],
            [
                InlineKeyboardButton("⬅️ برگشت به لیست محصولات", callback_data="all_products")
            ]
        ]
    else:
        text = (
            f"📦 <b>{product['name']}</b>\n\n"
            f"💰 قیمت: <b>{money(product['price'])} تومان</b>\n"
        )
        if product["category"]:
            text += f"📂 دسته‌بندی: {product['category']}\n"
        if product["description"]:
            text += f"\n📝 توضیحات:\n{product['description']}\n"

        buttons = [
            [InlineKeyboardButton("⬅️ برگشت به لیست محصولات", callback_data="all_products")]
        ]

    markup = InlineKeyboardMarkup(buttons)

    if product["image_id"]:
        try:
            await query.message.reply_photo(product["image_id"], caption=text, parse_mode="HTML", reply_markup=markup)
            return
        except Exception:
            pass

    await query.message.reply_text(text, parse_mode="HTML", reply_markup=markup)


async def sell_product(update, context):
    query = update.callback_query
    if not is_seller(update.effective_user.id):
        await query.answer("⛔ شما دسترسی ثبت فروش ندارید.", show_alert=True)
        return

    await query.answer()
    product_id = int(query.data.split("_")[1])

    _, msg = await process_sale_request(product_id, 1, update.effective_user, context)
    await query.message.reply_text(msg, parse_mode="HTML")


# =========================================================
# EXCEL EXPORTS
# =========================================================

async def export_products_excel(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return

    await query.answer("در حال آماده‌سازی فایل اکسل...")

    conn = db()
    rows = conn.execute("SELECT * FROM products ORDER BY id DESC").fetchall()
    conn.close()

    wb = Workbook()
    ws = wb.active
    ws.title = "Products"
    ws.sheet_view.rightToLeft = True

    ws.append([
        "شناسه", "نام کالا", "کد کالا", "بارکد",
        "قیمت پایه", "قیمت نهایی", "موجودی", "حد هشدار",
        "دسته‌بندی", "توضیحات", "آخرین تغییر قیمت"
    ])

    for r in rows:
        ws.append([
            r["id"],
            r["name"],
            r["code"] or "",
            r["barcode"] or "",
            r["base_price"],
            r["price"],
            r["stock"],
            r["min_stock"] if r["min_stock"] is not None else DEFAULT_MIN_STOCK,
            r["category"] or "",
            r["description"] or "",
            fa_date(r["price_updated_at"])
        ])

    bio = io.BytesIO()
    wb.save(bio)
    bio.seek(0)

    filename = f"products_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
    await query.message.reply_document(
        document=bio,
        filename=filename,
        caption=f"📥 <b>فایل اکسل لیست کامل کالاها ({len(rows)} کالا)</b>",
        parse_mode="HTML"
    )


async def export_sales_excel(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return

    await query.answer("در حال آماده‌سازی فایل اکسل...")

    conn = db()
    rows = conn.execute("""
        SELECT sa.*, s.name AS seller_name
        FROM sales sa
        LEFT JOIN sellers s ON sa.seller_id = s.user_id
        ORDER BY sa.id DESC
    """).fetchall()
    conn.close()

    wb = Workbook()
    ws = wb.active
    ws.title = "Sales"
    ws.sheet_view.rightToLeft = True

    ws.append([
        "ردیف فروش", "شناسه کالا", "نام کالا", "تعداد",
        "قیمت واحد", "مبلغ کل", "فروشنده", "وضعیت", "تاریخ فروش (شمسی)"
    ])

    status_map = {
        "approved": "تایید شده",
        "pending": "در انتظار تایید",
        "rejected": "رد شده"
    }

    for r in rows:
        ws.append([
            r["id"],
            r["product_id"],
            r["product_name"],
            r["quantity"],
            r["unit_price"],
            r["total_price"],
            r["seller_name"] or r["seller_id"],
            status_map.get(r["status"], r["status"]),
            fa_date(r["sold_at"])
        ])

    bio = io.BytesIO()
    wb.save(bio)
    bio.seek(0)

    filename = f"sales_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
    await query.message.reply_document(
        document=bio,
        filename=filename,
        caption="📥 <b>فایل اکسل گزارش کامل فروش</b>",
        parse_mode="HTML"
    )


# =========================================================
# DELETE (SINGLE & ALL) & REPORT
# =========================================================

async def delete_start(update, context, page=0):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()

    conn = db()
    total = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"]
    rows = conn.execute(
        "SELECT * FROM products ORDER BY id DESC LIMIT ? OFFSET ?",
        (PAGE_SIZE, page * PAGE_SIZE)
    ).fetchall()
    conn.close()

    if not rows and page == 0:
        await query.message.reply_text("📦 هیچ کالایی وجود ندارد.")
        return

    buttons = [[InlineKeyboardButton(f"🗑 {r['name']}", callback_data=f"confirmdelete_{r['id']}")] for r in rows]

    nav = []
    if page > 0:
        nav.append(InlineKeyboardButton("➡️ صفحه قبل", callback_data=f"delpage_{page - 1}"))
    if (page + 1) * PAGE_SIZE < total:
        nav.append(InlineKeyboardButton("⬅️ صفحه بعد", callback_data=f"delpage_{page + 1}"))
    if nav:
        buttons.append(nav)

    buttons.append([InlineKeyboardButton("🏠 خانه", callback_data="home")])
    total_pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE)
    text = f"کالایی که می‌خواهی حذف کنی را انتخاب کن (صفحه {page + 1} از {total_pages}):"

    if query.data.startswith("delpage_"):
        try:
            await query.message.edit_text(text, reply_markup=InlineKeyboardMarkup(buttons))
            return
        except Exception:
            pass

    await query.message.reply_text(text, reply_markup=InlineKeyboardMarkup(buttons))


async def delete_confirm(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()
    product_id = int(query.data.split("_")[1])
    product = get_product(product_id)

    if not product:
        await query.message.reply_text("❌ کالا پیدا نشد.")
        return

    await query.message.reply_text(
        f"⚠️ حذف «{product['name']}» قطعی است؟",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("✅ بله، حذف شود", callback_data=f"deleteyes_{product_id}")],
            [InlineKeyboardButton("❌ لغو", callback_data="delete_product")]
        ])
    )


async def delete_yes(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()
    product_id = int(query.data.split("_")[1])

    conn = db()
    conn.execute("DELETE FROM products WHERE id=?", (product_id,))
    conn.commit()
    conn.close()
    await query.message.reply_text("✅ کالا حذف شد.")


async def delete_all_products_confirm(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("⛔ فقط ادمین دسترسی دارد.", show_alert=True)
        return
    await query.answer()

    conn = db()
    total = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"]
    conn.close()

    if total == 0:
        await query.message.reply_text("📦 انبار در حال حاضر خالی است و هیچ کالایی وجود ندارد.")
        return

    await query.message.reply_text(
        f"🚨 <b>هشدار بسیار مهم!</b>\n\n"
        f"آیا مطمئن هستید که می‌خواهید <b>تمام {total} کالای موجود در ربات</b> را به طور کامل حذف کنید؟\n"
        "<i>(این عمل غیرقابل بازگشت است!)</i>",
        parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("🧨 بله، همه کالاها حذف شوند!", callback_data="delete_all_yes")],
            [InlineKeyboardButton("❌ خیر، انصراف", callback_data="back_admin")]
        ])
    )


async def delete_all_products_yes(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("⛔ فقط ادمین دسترسی دارد.", show_alert=True)
        return
    await query.answer("در حال پاکسازی انبار...")

    conn = db()
    conn.execute("DELETE FROM products")
    conn.execute("DELETE FROM sqlite_sequence WHERE name='products'")
    conn.commit()
    conn.close()

    await query.message.edit_text(
        "✅ <b>تمام کالاها با موفقیت حذف شدند و انبار کاملاً پاکسازی شد.</b>",
        parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("⬅️ برگشت به پنل مدیریت", callback_data="back_admin")]
        ])
    )


async def report(update, context):
    query = update.callback_query
    if not is_admin(update.effective_user.id):
        await query.answer("دسترسی نداری.", show_alert=True)
        return
    await query.answer()

    conn = db()
    total_sales = conn.execute("SELECT COUNT(*) AS c FROM sales WHERE status='approved'").fetchone()["c"]
    total_quantity = conn.execute("SELECT COALESCE(SUM(quantity), 0) AS c FROM sales WHERE status='approved'").fetchone()["c"]
    total_money = conn.execute("SELECT COALESCE(SUM(total_price), 0) AS c FROM sales WHERE status='approved'").fetchone()["c"]
    pending_count = conn.execute("SELECT COUNT(*) AS c FROM sales WHERE status='pending'").fetchone()["c"]

    popular = conn.execute("""
        SELECT product_name, SUM(quantity) AS qty, SUM(total_price) AS total
        FROM sales
        WHERE status='approved'
        GROUP BY product_name ORDER BY qty DESC LIMIT 10
    """).fetchall()
    conn.close()

    text = (
        "📊 <b>گزارش فروش (تایید شده‌ها)</b>\n\n"
        f"🧾 تعداد فروش قطعی: {total_sales}\n"
        f"📦 کل اقلام فروخته‌شده: {total_quantity} عدد\n"
        f"💰 مجموع فروش قطعی: {money(total_money)}\n"
        f"⏳ درخواست‌های در انتظار تایید: {pending_count}\n"
    )

    if popular:
        text += "\n<b>پرفروش‌ترین کالاها:</b>\n"
        for row in popular:
            text += f"• {row['product_name']} — {row['qty']} عدد — {money(row['total'])}\n"

    await query.message.reply_text(
        text,
        parse_mode="HTML",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("📥 دانلود اکسل فروش", callback_data="export_sales")],
            [InlineKeyboardButton("🏠 خانه", callback_data="home")]
        ])
    )


# =========================================================
# TEXT MENU & CALLBACKS
# =========================================================

async def text_menu(update, context):
    user = update.effective_user
    sync_staff_name(user)
    text = update.message.text.strip()
    staff = is_seller(user.id)

    if text == "📦 لیست محصولات":
        await all_products(update, context, page=0)
        return

    if text == "🔎 جستجوی کالا":
        prompt = "نام، کد یا بارکد کالا را وارد کن:" if staff else "🔎 نام کالای مورد نظر را وارد کنید:"
        await update.message.reply_text(prompt)
        context.user_data["search_mode"] = True
        return

    if text == "📂 دسته‌بندی‌ها":
        await categories(update, context)
        return

    if text == "💰 ثبت فروش":
        if not staff:
            await update.message.reply_text("⛔ شما دسترسی ثبت فروش ندارید.")
            return
        await update.message.reply_text("نام، کد یا بارکد کالا را وارد کن:")
        context.user_data["sale_search"] = True
        return

    if text == "⚙️ مدیریت":
        if is_admin(user.id):
            await update.message.reply_text("⚙️ پنل مدیریت", reply_markup=admin_menu())
        else:
            await update.message.reply_text("⛔ شما به پنل مدیریت دسترسی ندارید.")
        return

    if context.user_data.get("search_mode"):
        context.user_data.pop("search_mode", None)
        conn = db()
        if staff:
            rows = conn.execute("""
                SELECT * FROM products
                WHERE name LIKE ? OR code LIKE ? OR barcode LIKE ?
                ORDER BY id DESC LIMIT 40
            """, (f"%{text}%", f"%{text}%", f"%{text}%")).fetchall()
        else:
            rows = conn.execute("""
                SELECT * FROM products
                WHERE name LIKE ? OR category LIKE ?
                ORDER BY id DESC LIMIT 40
            """, (f"%{text}%", f"%{text}%")).fetchall()
        conn.close()

        if not rows:
            await update.message.reply_text("❌ کالایی پیدا نشد.")
            return

        buttons = [[InlineKeyboardButton(f"{r['name']} | {money(r['price'])} تومان", callback_data=f"product_{r['id']}")] for r in rows]
        await update.message.reply_text("🔎 نتایج:", reply_markup=InlineKeyboardMarkup(buttons))
        return

    if context.user_data.get("sale_search"):
        context.user_data.pop("sale_search", None)
        if not staff:
            return

        conn = db()
        rows = conn.execute("""
            SELECT * FROM products
            WHERE name LIKE ? OR code LIKE ? OR barcode LIKE ?
            ORDER BY id DESC LIMIT 30
        """, (f"%{text}%", f"%{text}%", f"%{text}%")).fetchall()
        conn.close()

        if not rows:
            await update.message.reply_text("❌ کالایی پیدا نشد.")
            return

        buttons = []
        for r in rows:
            buttons.append([
                InlineKeyboardButton(f"📦 {r['name']} (موجودی: {r['stock']})", callback_data=f"product_{r['id']}"),
            ])
            buttons.append([
                InlineKeyboardButton("💰 ۱ عدد", callback_data=f"sell_{r['id']}"),
                InlineKeyboardButton("🔢 تعداد دلخواه", callback_data=f"sellqty_{r['id']}")
            ])

        await update.message.reply_text("کالا و نحوه فروش را انتخاب کن:", reply_markup=InlineKeyboardMarkup(buttons))


async def callbacks(update, context):
    query = update.callback_query
    user = update.effective_user
    sync_staff_name(user)
    data = query.data

    if data == "home":
        await query.answer()
        await query.message.reply_text("🏠 منوی اصلی", reply_markup=main_menu(user.id))
        return
    if data == "back_admin":
        await query.answer()
        if is_admin(user.id):
            await query.message.reply_text("⚙️ پنل مدیریت", reply_markup=admin_menu())
        return
    if data == "sync_site":
        await sync_website_products(update, context)
        return
    if data == "delete_all_products":
        await delete_all_products_confirm(update, context)
        return
    if data == "delete_all_yes":
        await delete_all_products_yes(update, context)
        return
    if data.startswith("delstaff_"):
        await delete_staff_callback(update, context)
        return
    if data == "pending_sales":
        await show_pending_sales(update, context)
        return
    if data.startswith("appsale_"):
        await approve_sale_callback(update, context)
        return
    if data.startswith("rejsale_"):
        await reject_sale_callback(update, context)
        return
    if data == "low_stock":
        await show_low_stock(update, context, page=0)
        return
    if data.startswith("lowpage_"):
        page = int(data.split("_")[1])
        await show_low_stock(update, context, page=page)
        return
    if data == "categories":
        await categories(update, context)
        return
    if data.startswith("catidx_"):
        await show_category(update, context)
        return
    if data == "all_products":
        await all_products(update, context, page=0)
        return
    if data.startswith("prodpage_"):
        page = int(data.split("_")[1])
        await all_products(update, context, page=page)
        return
    if data.startswith("product_"):
        await product_details(update, context)
        return
    if data.startswith("sell_"):
        await sell_product(update, context)
        return
    if data == "delete_product":
        await delete_start(update, context, page=0)
        return
    if data.startswith("delpage_"):
        page = int(data.split("_")[1])
        await delete_start(update, context, page=page)
        return
    if data.startswith("confirmdelete_"):
        await delete_confirm(update, context)
        return
    if data.startswith("deleteyes_"):
        await delete_yes(update, context)
        return
    if data == "report":
        await report(update, context)
        return
    if data == "sellers":
        await sellers(update, context)
        return
    if data == "export_products":
        await export_products_excel(update, context)
        return
    if data == "export_sales":
        await export_sales_excel(update, context)
        return
    if data == "sample_excel":
        await send_sample_excel(update, context)
        return

    await query.answer()


async def cancel(update, context):
    context.user_data.clear()
    if update.callback_query:
        await update.callback_query.answer()
    if update.effective_message:
        await update.effective_message.reply_text("❌ عملیات لغو شد.", reply_markup=main_menu(update.effective_user.id))
    return ConversationHandler.END


# =========================================================
# MAIN
# =========================================================

async def main():
    init_db()

    app = (
        Application.builder()
        .token(BOT_TOKEN)
        .post_init(menu_commands)
        .build()
    )

    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("admin", admin_command))
    app.add_handler(CommandHandler("myid", myid_command))

    search_conv = ConversationHandler(
        entry_points=[
            CommandHandler("search", search_start),
            CallbackQueryHandler(search_start, pattern=r"^search$")
        ],
        states={
            SEARCH_QUERY: [MessageHandler(filters.TEXT & ~filters.COMMAND, search_text)]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(search_conv)

    add_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(add_start, pattern=r"^add_product$")],
        states={
            ADD_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_name)],
            ADD_CODE: [MessageHandler(filters.TEXT, add_code)],
            ADD_BARCODE: [MessageHandler(filters.TEXT, add_barcode)],
            ADD_BASE: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_base)],
            ADD_STOCK: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_stock)],
            ADD_MIN_STOCK: [MessageHandler(filters.TEXT, add_min_stock)],
            ADD_CATEGORY: [MessageHandler(filters.TEXT, add_category)],
            ADD_DESCRIPTION: [MessageHandler(filters.TEXT, add_description)],
            ADD_IMAGE: [
                MessageHandler(filters.PHOTO, add_image),
                MessageHandler(filters.TEXT, add_image)
            ]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(add_conv)

    import_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(import_excel_start, pattern=r"^import_excel$")],
        states={
            IMPORT_FILE_STATE: [
                CallbackQueryHandler(send_sample_excel, pattern=r"^sample_excel$"),
                MessageHandler(filters.Document.ALL, import_excel_receive),
                MessageHandler(filters.TEXT & ~filters.COMMAND, import_excel_receive)
            ]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(import_conv)

    edit_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(edit_start, pattern=r"^edit_product$")],
        states={
            EDIT_FIND: [MessageHandler(filters.TEXT & ~filters.COMMAND, edit_find)],
            EDIT_FIELD: [CallbackQueryHandler(edit_pick, pattern=r"^editpick_\d+$")],
            EDIT_VALUE: [
                CallbackQueryHandler(edit_field, pattern=r"^ef_(name|code|barcode|base|stock|min_stock|category|description|image)$"),
                MessageHandler(filters.PHOTO, edit_value),
                MessageHandler(filters.TEXT & ~filters.COMMAND, edit_value)
            ]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(edit_conv)

    markup_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(markup_start, pattern=r"^markup$")],
        states={
            MARKUP_VALUE: [MessageHandler(filters.TEXT & ~filters.COMMAND, markup_value)]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(markup_conv)

    sell_qty_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(sell_qty_start, pattern=r"^sellqty_\d+$")],
        states={
            SELL_QTY_STATE: [MessageHandler(filters.TEXT & ~filters.COMMAND, sell_qty_receive)]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(sell_qty_conv)

    add_staff_conv = ConversationHandler(
        entry_points=[CallbackQueryHandler(add_staff_start, pattern=r"^addstaff_(seller|admin)$")],
        states={
            ADD_STAFF_ID_STATE: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_staff_receive_id)]
        },
        fallbacks=[
            CommandHandler("cancel", cancel),
            CallbackQueryHandler(cancel, pattern=r"^home$")
        ]
    )
    app.add_handler(add_staff_conv)

    app.add_handler(CallbackQueryHandler(callbacks))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, text_menu))

    print("================================")
    print("🤖 BOT IS RUNNING")
    print("================================")

    await app.initialize()
    await app.start()
    await app.updater.start_polling(drop_pending_updates=True)

    try:
        while True:
            await asyncio.sleep(3600)
    except (KeyboardInterrupt, asyncio.CancelledError):
        pass
    finally:
        await app.updater.stop()
        await app.stop()
        await app.shutdown()


if __name__ == "__main__":
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = None

    if loop and loop.is_running():
        loop.create_task(main())
    else:
        asyncio.run(main())