import io
import re
import time

import telebot
from telebot import types

from telebot import apihelper

apihelper.proxy = {
    'http': 'socks5h://127.0.0.1:10808',
    'https': 'socks5h://127.0.0.1:10808'
}

from config import *
import database as db

if BOT_TOKEN == "PASTE_BOT_TOKEN_HERE":
    raise RuntimeError("توکن ربات را در config.py یا متغیر محیطی BOT_TOKEN وارد کنید.")

bot = telebot.TeleBot(BOT_TOKEN, parse_mode="HTML")
db.init_db()
user_state = {}

# همه‌ی اعضای گروه مدیریت (GROUP_ID) می‌توانند ورود بلیت را تأیید کنند.
# اگر خواستید کسی خارج از گروه هم دسترسی داشته باشد، در config.py تعریف کنید:
# CHECKER_IDS = [123456789, 987654321]   (اختیاری)
CHECKER_IDS = globals().get("CHECKER_IDS", [])

# نتیجه‌ی عضویت در گروه چند ثانیه نگه داشته می‌شود تا برای هر پیام به تلگرام درخواست نرود
MEMBER_CACHE_SECONDS = 120
_member_cache = {}  # user_id -> (is_member, زمان بررسی)

# کد جدید: ۵ حرف/رقم انگلیسی. الگوی دوم برای بلیت‌های قدیمی (قبل از کد کوتاه) است.
TICKET_CODE_RE = re.compile(r"^([A-Z0-9]{5}|CH-\d{6}-\d{2}(-[0-9A-F]{6})?)$")
_bot_username = None

# جدول تبدیل ارقام فارسی و عربی به انگلیسی
DIGITS_MAP = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩",
                           "01234567890123456789")

STATUS_LABELS = {
    "waiting_receipt": "در انتظار رسید",
    "pending_review": "در انتظار بررسی ادمین",
    "approved": "تأیید شده",
    "rejected": "رد شده",
}


def is_admin(user_id):
    return user_id in ADMIN_IDS


def is_group_member(user_id):
    """بررسی می‌کند کاربر عضو گروه مدیریت هست یا نه."""
    now = time.time()
    cached = _member_cache.get(user_id)
    if cached and now - cached[1] < MEMBER_CACHE_SECONDS:
        return cached[0]
    try:
        status = bot.get_chat_member(GROUP_ID, user_id).status
        is_member = status in ("creator", "administrator", "member", "restricted")
    except Exception as e:
        print("Error checking group membership:", e)
        # در صورت خطای شبکه، نتیجه‌ی قبلی (اگر بود) حفظ می‌شود و ذخیره‌ی تازه انجام نمی‌شود
        return cached[0] if cached else False
    _member_cache[user_id] = (is_member, now)
    return is_member


def is_checker(user_id):
    return (
        user_id in CHECKER_IDS
        or user_id in ADMIN_IDS
        or is_group_member(user_id)
    )


def main_menu():
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.row("🎟 خرید بلیت", "📋 سفارش‌های من")
    kb.row("❤️ درباره خیریه", "☎️ پشتیبانی")
    return kb


def admin_order_keyboard(order_id):
    kb = types.InlineKeyboardMarkup()
    kb.row(
        types.InlineKeyboardButton("✅ تأیید پرداخت", callback_data=f"approve:{order_id}"),
        types.InlineKeyboardButton("❌ رد پرداخت", callback_data=f"reject:{order_id}")
    )
    return kb

def order_status_keyboard(status):
    kb = types.InlineKeyboardMarkup()

    if status == "approved":
        kb.add(
            types.InlineKeyboardButton(
                "✅ پرداخت تأیید شد",
                callback_data="status_done"
            )
        )

    elif status == "rejected":
        kb.add(
            types.InlineKeyboardButton(
                "❌ پرداخت رد شد",
                callback_data="status_done"
            )
        )

    return kb


def show_selection_keyboard(shows):
    kb = types.InlineKeyboardMarkup()

    for show in shows:
        label = f"{show['title']} | {show['show_date']}"
        kb.add(
            types.InlineKeyboardButton(
                label[:60],
                callback_data=f"buyshow:{show['id']}"
            )
        )

    kb.add(
        types.InlineKeyboardButton(
            "❌ انصراف از رزرو",
            callback_data="cancel_reservation"
        )
    )

    return kb

def admin_shows_keyboard(shows):
    kb = types.InlineKeyboardMarkup()
    for show in shows:
        sold = db.count_sold(show["id"])
        kb.row(
            types.InlineKeyboardButton(
                f"👥 ثبت‌نام‌ها #{show['id']} ({sold}/{show['capacity']})",
                callback_data=f"showstats:{show['id']}"
            ),
            types.InlineKeyboardButton(
                f"🗑 حذف #{show['id']}",
                callback_data=f"delshow:{show['id']}"
            )
        )
        if show["active"]:
            kb.add(
                types.InlineKeyboardButton(
                    f"⛔ غیرفعال‌سازی سانس #{show['id']}",
                    callback_data=f"deactshow:{show['id']}"
                )
            )
    return kb


@bot.message_handler(commands=["start"])
def start(message):
    # لینک QR بلیت: t.me/<bot>?start=chk_<code>
    parts = (message.text or "").split(maxsplit=1)
    if len(parts) == 2 and parts[1].startswith("chk_") and is_checker(message.from_user.id):
        send_ticket_check(message.chat.id, parts[1][4:], message.from_user.id)
        return
    bot.send_message(message.chat.id,
        f"سلام 🌱\nبه ربات فروش بلیت اکران خیریه «{CHARITY_NAME}» خوش آمدید.\n"
        "با خرید بلیت، در این کار خیر سهیم می‌شوید.", reply_markup=main_menu())


@bot.message_handler(commands=["admin"])
def admin_help(message):
    if not is_admin(message.from_user.id):
        return
    bot.send_message(message.chat.id,
        "🛠 <b>پنل مدیریت</b>\n/add_show : ایجاد سانس جدید\n"
        "/shows : مشاهده، آمار ثبت‌نام، حذف و غیرفعال‌سازی سانس‌ها\n"
        "/export_excel : دریافت گزارش اکسل\n/gate : پنل ورودی و آمار ورود\n/cancel : لغو عملیات جاری")


@bot.message_handler(commands=["cancel"])
def cancel(message):
    user_state.pop(message.from_user.id, None)
    bot.send_message(message.chat.id, "عملیات لغو شد.", reply_markup=main_menu())


# ---------------------- ورود به سالن (چک بلیت) ----------------------

def normalize_code(text):
    # فاصله‌ها حذف می‌شوند و حروف کوچک/بزرگ فرقی ندارند
    return re.sub(r"\s+", "", (text or "")).translate(DIGITS_MAP).upper()


def render_ticket(code, checker_id, head=None):
    """متن و دکمه‌های نمایش اطلاعات یک بلیت را می‌سازد."""
    t = db.get_ticket(normalize_code(code))
    if not t:
        return "❌ <b>بلیت نامعتبر است.</b>\nاین کد در سیستم ثبت نشده است.", None

    tickets = db.list_order_tickets(t["order_id"])
    unused = [x for x in tickets if not x["used"]]
    entered = len(tickets) - len(unused)

    kb = types.InlineKeyboardMarkup()
    if t["used"]:
        if head is None:
            head = (f"⛔ <b>این بلیت قبلاً استفاده شده!</b>\n"
                    f"زمان ورود: {t['used_at']}")
        if is_admin(checker_id):
            kb.add(types.InlineKeyboardButton(
                "↩️ لغو ورود (ثبت اشتباه)",
                callback_data=f"undocheckin:{t['code']}"
            ))
    else:
        if head is None:
            head = "✅ <b>بلیت معتبر است و هنوز استفاده نشده.</b>"
        kb.add(types.InlineKeyboardButton(
            "🚪 تأیید ورود",
            callback_data=f"checkin:{t['code']}"
        ))
        if len(unused) > 1:
            kb.add(types.InlineKeyboardButton(
                f"🚪 ورود همه ({len(unused)} بلیت باقی‌مانده این سفارش)",
                callback_data=f"checkinall:{t['order_id']}"
            ))

    text = (
        f"{head}\n\n"
        f"🎬 <b>{t['show_title']}</b>\n"
        f"سینما: {t['show_cinema']}\n"
        f"زمان: {t['show_date']}\n"
        f"خریدار: {t['buyer_name']}\n"
        f"سفارش #{t['order_id']} | بلیت: <code>{t['code']}</code>\n"
        f"وضعیت سفارش: {entered} از {len(tickets)} بلیت وارد شده"
    )
    return text, (kb if kb.keyboard else None)


def send_ticket_check(chat_id, code, checker_id):
    text, kb = render_ticket(code, checker_id)
    bot.send_message(chat_id, text, reply_markup=kb)


def edit_ticket_message(call, code, head=None):
    text, kb = render_ticket(code, call.from_user.id, head)
    try:
        bot.edit_message_text(
            text,
            chat_id=call.message.chat.id,
            message_id=call.message.message_id,
            reply_markup=kb
        )
    except Exception as e:
        print("Error editing ticket message:", e)


def send_ticket_qrs(user_id, codes):
    """برای هر بلیت یک QR می‌فرستد (اگر کتابخانه qrcode نصب باشد)."""
    global _bot_username
    try:
        import qrcode
    except ImportError:
        return
    try:
        if not _bot_username:
            _bot_username = bot.get_me().username
        for code in codes:
            img = qrcode.make(f"https://t.me/{_bot_username}?start=chk_{code}")
            buf = io.BytesIO()
            img.save(buf, format="PNG")
            buf.seek(0)
            bot.send_photo(user_id, buf, caption=f"🎫 <code>{code}</code>")
    except Exception as e:
        print("Error sending QR:", e)


@bot.message_handler(commands=["gate"])
def gate_help(message):
    if not is_checker(message.from_user.id):
        return
    bot.send_message(
        message.chat.id,
        "🚪 <b>پنل ورودی</b>\n"
        "کد بلیت را همین‌جا بفرستید یا QR بلیت را با دوربین گوشی اسکن کنید.\n"
        "/gate_stats : آمار ورود به تفکیک سانس"
    )


@bot.message_handler(commands=["gate_stats"])
def gate_stats(message):
    if not is_checker(message.from_user.id):
        return
    shows = db.list_shows()
    if not shows:
        bot.send_message(message.chat.id, "هنوز سانسی ثبت نشده است.")
        return
    lines = ["📊 <b>آمار ورود</b>"]
    for s in shows:
        st = db.entry_stats(s["id"])
        lines.append(
            f"\n#{s['id']} | {s['title']}\n"
            f"وارد شده: {st['entered']} از {st['total']} بلیت صادرشده"
        )
    bot.send_message(message.chat.id, "\n".join(lines))


@bot.message_handler(
    func=lambda m: bool(m.text)
    and m.from_user.id not in user_state
    and bool(TICKET_CODE_RE.match(normalize_code(m.text)))
    and is_checker(m.from_user.id),
    content_types=["text"]
)
def gate_code_message(message):
    send_ticket_check(message.chat.id, message.text, message.from_user.id)


@bot.callback_query_handler(func=lambda call: call.data.startswith("checkin:"))
def checkin_callback(call):
    if not is_checker(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ندارید.")
        return
    code = call.data.split(":", 1)[1]
    if db.check_in(code, call.from_user.id):
        bot.answer_callback_query(call.id, "ورود ثبت شد ✅")
        edit_ticket_message(call, code, "✅ <b>ورود ثبت شد.</b>")
    else:
        bot.answer_callback_query(call.id, "این بلیت قبلاً استفاده شده!", show_alert=True)
        edit_ticket_message(call, code)


@bot.callback_query_handler(func=lambda call: call.data.startswith("checkinall:"))
def checkin_all_callback(call):
    if not is_checker(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ندارید.")
        return
    order_id = int(call.data.split(":", 1)[1])
    tickets = db.list_order_tickets(order_id)
    if not tickets:
        bot.answer_callback_query(call.id, "بلیتی برای این سفارش پیدا نشد.", show_alert=True)
        return
    count = db.check_in_order(order_id, call.from_user.id)
    if count:
        bot.answer_callback_query(call.id, f"ورود {count} بلیت ثبت شد ✅")
        edit_ticket_message(call, tickets[0]["code"], f"✅ <b>ورود {count} بلیت ثبت شد.</b>")
    else:
        bot.answer_callback_query(call.id, "همه‌ی بلیت‌های این سفارش قبلاً استفاده شده‌اند!", show_alert=True)
        edit_ticket_message(call, tickets[0]["code"])


@bot.callback_query_handler(func=lambda call: call.data.startswith("undocheckin:"))
def undo_checkin_callback(call):
    if not is_admin(call.from_user.id):
        bot.answer_callback_query(call.id, "فقط ادمین می‌تواند ورود را لغو کند.", show_alert=True)
        return
    code = call.data.split(":", 1)[1]
    if db.undo_check_in(code):
        bot.answer_callback_query(call.id, "ورود لغو شد.")
        edit_ticket_message(call, code, "↩️ <b>ثبت ورود لغو شد؛ بلیت دوباره قابل استفاده است.</b>")
    else:
        bot.answer_callback_query(call.id, "این بلیت ورود ثبت‌شده‌ای ندارد.")


@bot.message_handler(commands=["add_show"])
def add_show_start(message):
    if not is_admin(message.from_user.id):
        return
    user_state[message.from_user.id] = {"step": "show_title"}
    bot.send_message(message.chat.id, "عنوان فیلم یا نام سانس را وارد کنید.\nبرای لغو: /cancel")

@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "show_title",
    content_types=["text"]
)
def get_show_title(message):
    if not is_admin(message.from_user.id):
        return

    title = message.text.strip()

    if not title:
        bot.send_message(message.chat.id, "عنوان فیلم نمی‌تواند خالی باشد.")
        return

    user_state[message.from_user.id].update({
        "show_title": title,
        "step": "show_cinema"
    })

    bot.send_message(message.chat.id, "نام سینما را وارد کنید:")


@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "show_cinema",
    content_types=["text"]
)
def get_show_cinema(message):
    if not is_admin(message.from_user.id):
        return

    cinema = message.text.strip()

    if not cinema:
        bot.send_message(message.chat.id, "نام سینما نمی‌تواند خالی باشد.")
        return

    user_state[message.from_user.id].update({
        "show_cinema": cinema,
        "step": "show_date"
    })

    bot.send_message(
        message.chat.id,
        "تاریخ و ساعت سانس را وارد کنید.\n"
        "مثال: ۱۴۰۵/۰۹/۲۰ - ساعت ۱۸:۰۰"
    )

@bot.message_handler(commands=["shows"])
def show_admin_list(message):
    if not is_admin(message.from_user.id):
        return
    shows = db.list_shows()
    if not shows:
        bot.send_message(message.chat.id, "هنوز سانسی ثبت نشده است.")
        return
    lines = ["🎬 <b>فهرست سانس‌ها</b>"]
    for s in shows:
        status = "فعال ✅" if s["active"] else "غیرفعال ⛔"
        remaining = db.remaining_capacity(s["id"])
        lines.append(
    f"\n#{s['id']} | {s['title']}\n"
    f"سینما: {s['cinema']}\n"
    f"زمان: {s['show_date']}\n"
    f"قیمت بلیت: {s['ticket_price']:,} تومان\n"
    f"ظرفیت: {s['capacity']} | باقی‌مانده: {remaining}\n"
    f"وضعیت: {status}")
    bot.send_message(message.chat.id, "\n".join(lines), reply_markup=admin_shows_keyboard(shows))


@bot.message_handler(commands=["export_excel"])
def export_excel(message):
    if not is_admin(message.from_user.id):
        return
    from export_excel import export_orders
    path = export_orders(db.all_orders())
    with open(path, "rb") as f:
        bot.send_document(message.chat.id, f, caption="گزارش سفارش‌ها و خریداران")


@bot.message_handler(func=lambda m: m.text == "🎟 خرید بلیت")
def buy_start(message):
    shows = db.list_shows(active_only=True)
    if not shows:
        bot.send_message(message.chat.id, "در حال حاضر سانس فعالی برای فروش وجود ندارد.")
        return
    user_state[message.from_user.id] = {"step": "choose_show"}
    bot.send_message(message.chat.id, "🎬 لطفاً سانس موردنظر را انتخاب کنید:", reply_markup=show_selection_keyboard(shows))


@bot.callback_query_handler(func=lambda call: call.data.startswith("buyshow:"))
def choose_show(call):
    show_id = int(call.data.split(":", 1)[1])
    show = db.get_show(show_id)

    if not show or not show["active"]:
        bot.answer_callback_query(
            call.id,
            "این سانس دیگر فعال نیست.",
            show_alert=True
        )
        return

    remaining = db.remaining_capacity(show_id)

    if remaining <= 0:
        bot.answer_callback_query(
            call.id,
            "ظرفیت این سانس تکمیل شده است.",
            show_alert=True
        )
        return

    user_state[call.from_user.id] = {
        "step": "quantity",
        "show_id": show_id,
        "cancel_message_id": call.message.message_id
    }

    bot.answer_callback_query(call.id, "سانس انتخاب شد.")

    bot.edit_message_text(
        f"🎬 <b>{show['title']}</b>\n"
        f"سینما: {show['cinema']}\n"
        f"زمان: {show['show_date']}\n"
        f"قیمت هر بلیت: <b>{show['ticket_price']:,} تومان</b>\n"
        f"🪑 ظرفیت باقی‌مانده: <b>{remaining} بلیت</b>\n\n"
        f"🎟 چند بلیت می‌خواهید؟ (عدد ۱ تا {min(10, remaining)})",
        chat_id=call.message.chat.id,
        message_id=call.message.message_id,
        reply_markup=cancel_keyboard()
    )
        
@bot.message_handler(func=lambda m: m.text == "📋 سفارش‌های من")
def my_orders(message):
    rows = db.list_user_orders(message.from_user.id)
    if not rows:
        bot.send_message(message.chat.id, "هنوز سفارشی ثبت نکرده‌اید.")
        return
    labels = STATUS_LABELS
    text = "📋 <b>سفارش‌های شما</b>\n\n"
    for r in rows:
        show_title = r["show_title"] or EVENT_TITLE
        show_date = r["show_date"] or SHOW_DATE
        text += (f"سفارش #{r['id']} | {show_title}\nزمان: {show_date}\n"
                 f"{r['quantity']} بلیت | {r['total_price']:,} تومان\n"
                 f"وضعیت: {labels.get(r['status'], r['status'])}\n")
        if r["ticket_codes"]:
            text += f"کدها: {r['ticket_codes']}\n"
        text += "\n"
    bot.send_message(message.chat.id, text)


@bot.message_handler(func=lambda m: m.text == "❤️ درباره خیریه")
def about(message):
    bot.send_message(message.chat.id, f"این ربات برای حمایت از {CHARITY_NAME} و فروش بلیت اکران خیریه راه‌اندازی شده است.")


@bot.message_handler(func=lambda m: m.text == "☎️ پشتیبانی")
def support(message):
    text = f"برای پشتیبانی با {SUPPORT_USERNAME} در ارتباط باشید." if SUPPORT_USERNAME else "برای پشتیبانی، به ادمین ربات پیام دهید."
    bot.send_message(message.chat.id, text)


# مراحل ساخت سانس توسط ادمین
@bot.message_handler(func=lambda m: m.from_user.id in user_state and user_state[m.from_user.id].get("step") == "show_date", content_types=["text"])
def get_show_date(message):
    if not is_admin(message.from_user.id):
        return

    show_date = message.text.strip()

    if not show_date:
        bot.send_message(message.chat.id, "تاریخ و ساعت نمی‌تواند خالی باشد. دوباره وارد کنید:")
        return

    user_state[message.from_user.id].update({
        "show_date": show_date,
        "step": "show_price"
    })

    bot.send_message(
        message.chat.id,
        "💰 قیمت هر بلیت این سانس را به تومان وارد کنید:\nمثال: ۱۵۰۰۰۰"
    )
    
@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "show_price",
    content_types=["text"]
)
def get_show_price(message):
    if not is_admin(message.from_user.id):
        return

    try:
        price_text = message.text.strip()

        # تبدیل اعداد فارسی و عربی به انگلیسی
        price_text = price_text.translate(DIGITS_MAP)

        price_text = price_text.replace(",", "").replace("،", "").replace("٬", "")
        price = int(price_text)

        if price <= 0:
            raise ValueError

    except (ValueError, TypeError):
        bot.send_message(
            message.chat.id,
            "لطفاً قیمت معتبر و بزرگ‌تر از صفر وارد کنید."
        )
        return

    user_state[message.from_user.id].update({
        "show_price": price,
        "step": "show_capacity"
    })

    bot.send_message(
        message.chat.id,
        "🪑 ظرفیت سالن (تعداد کل صندلی‌ها) را وارد کنید:\nمثال: ۲۰۰"
    )


@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "show_capacity",
    content_types=["text"]
)
def get_show_capacity(message):
    if not is_admin(message.from_user.id):
        return

    try:
        capacity_text = message.text.strip().translate(DIGITS_MAP)
        capacity_text = capacity_text.replace(",", "").replace("،", "").replace("٬", "")
        capacity = int(capacity_text)

        if capacity <= 0:
            raise ValueError

    except (ValueError, TypeError):
        bot.send_message(
            message.chat.id,
            "لطفاً ظرفیت معتبر (عدد بزرگ‌تر از صفر) وارد کنید."
        )
        return

    data = user_state[message.from_user.id]

    show_id = db.add_show(
        data["show_title"],
        data["show_cinema"],
        data["show_date"],
        data["show_price"],
        capacity
    )

    user_state.pop(message.from_user.id, None)

    bot.send_message(
        message.chat.id,
        f"✅ سانس جدید با موفقیت ایجاد شد.\n"
        f"شناسه: #{show_id}\n"
        f"عنوان: {data['show_title']}\n"
        f"سینما: {data['show_cinema']}\n"
        f"زمان: {data['show_date']}\n"
        f"قیمت هر بلیت: {data['show_price']:,} تومان\n"
        f"ظرفیت سالن: {capacity} نفر"
    )
       
# مراحل خرید بلیت
@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "quantity",
    content_types=["text"]
)
def get_quantity(message):
    data = user_state[message.from_user.id]

    remaining = db.remaining_capacity(data["show_id"])

    if remaining <= 0:
        remove_cancel_button(message.chat.id, data["cancel_message_id"])
        user_state.pop(message.from_user.id, None)
        bot.send_message(
            message.chat.id,
            "متأسفانه ظرفیت این سانس تکمیل شد.",
            reply_markup=main_menu()
        )
        return

    max_quantity = min(10, remaining)

    try:
        quantity_text = message.text.strip().translate(DIGITS_MAP)

        quantity = int(quantity_text)

        if not 1 <= quantity <= max_quantity:
            raise ValueError

    except (ValueError, TypeError):
        bot.send_message(
            message.chat.id,
            f"لطفاً عددی بین ۱ تا {max_quantity} وارد کنید."
        )
        return

    data.update({
        "quantity": quantity,
        "step": "name"
    })

    remove_cancel_button(
        message.chat.id,
        data["cancel_message_id"]
    )

    msg = bot.send_message(
        message.chat.id,
        "👤 لطفاً نام و نام خانوادگی خریدار را وارد کنید:",
        reply_markup=cancel_keyboard()
    )

    data["cancel_message_id"] = msg.message_id
    
    
@bot.message_handler(
    func=lambda m: m.from_user.id in user_state
    and user_state[m.from_user.id].get("step") == "name",
    content_types=["text"]
)
def get_name(message):
    name = message.text.strip()

    if not name:
        bot.send_message(message.chat.id, "نام نمی‌تواند خالی باشد.")
        return

    data = user_state[message.from_user.id]

    data.update({
        "buyer_name": name,
        "step": "phone"
    })

    remove_cancel_button(
        message.chat.id,
        data["cancel_message_id"]
    )

    msg = bot.send_message(
        message.chat.id,
        "📱 لطفاً شماره تلفن همراه خریدار را وارد کنید:",
        reply_markup=cancel_keyboard()
    )

    data["cancel_message_id"] = msg.message_id

@bot.message_handler(func=lambda m: m.from_user.id in user_state and user_state[m.from_user.id].get("step") == "phone", content_types=["text"])
def get_phone(message):
    data = user_state[message.from_user.id]
    phone = message.text.strip()
    if len(phone) < 8:
        bot.send_message(message.chat.id, "شماره واردشده معتبر به نظر نمی‌رسد؛ دوباره وارد کنید.")
        return
    show = db.get_show(data["show_id"])
    if not show or not show["active"]:
        user_state.pop(message.from_user.id, None)
        bot.send_message(message.chat.id, "متأسفانه این سانس غیرفعال شده است. لطفاً خرید را از ابتدا انجام دهید.")
        return
    order_id = db.create_order(
        message.from_user.id,
        message.from_user.username,
        data["buyer_name"],
        phone,
        data["quantity"],
        show["ticket_price"],
        show
    )

    # اگر در فاصله‌ی انتخاب تعداد تا الان، بقیه بلیت‌ها را گرفته باشند
    if order_id is None:
        remove_cancel_button(
            message.chat.id,
            data["cancel_message_id"]
        )
        user_state.pop(message.from_user.id, None)
        bot.send_message(
            message.chat.id,
            "متأسفانه ظرفیت باقی‌مانده کمتر از تعداد درخواستی شده است. "
            "لطفاً دوباره خرید را شروع کنید.",
            reply_markup=main_menu()
        )
        return

    data.update({
        "order_id": order_id,
        "step": "receipt"
    })

    remove_cancel_button(
        message.chat.id,
        data["cancel_message_id"]
    )

    total = data["quantity"] * show["ticket_price"]

    msg = bot.send_message(
        message.chat.id,
        f"🧾 <b>پیش‌فاکتور سفارش #{order_id}</b>\n"
        f"سانس: {show['title']}\n"
        f"سینما: {show['cinema']}\n"
        f"زمان: {show['show_date']}\n"
        f"تعداد: {data['quantity']}\n"
        f"مبلغ کل: <b>{total:,} تومان</b>\n\n"
        f"شماره کارت: <code>{CARD_NUMBER}</code>\n"
        f"به نام: {CARD_HOLDER}\n\n"
        "پس از واریز، تصویر رسید را همین‌جا به‌صورت عکس ارسال کنید.",
        reply_markup=cancel_keyboard()
    )

    data["cancel_message_id"] = msg.message_id

@bot.message_handler(content_types=["photo"])
def receive_receipt(message):
    data = user_state.get(message.from_user.id)
    if not data or data.get("step") != "receipt":
        return
    file_id = message.photo[-1].file_id
    order_id = data["order_id"]
    if not db.set_receipt(order_id, file_id):
        bot.send_message(message.chat.id, "رسید این سفارش قبلاً ثبت شده است.")
        remove_cancel_button(
            message.chat.id,
            data["cancel_message_id"]
        )        
        user_state.pop(message.from_user.id, None)
        return
    order = db.get_order(order_id)
    caption = (f"🧾 <b>رسید پرداخت جدید | سفارش #{order_id}</b>\n"
               f"سانس: {order['show_title']}\nسینما: {order['show_cinema']}\nزمان: {order['show_date']}\n"
               f"نام: {order['buyer_name']}\nتلفن: {order['phone']}\n"
               f"تعداد: {order['quantity']}\nمبلغ: {order['total_price']:,} تومان\n"
               f"شناسه تلگرام: <code>{order['user_id']}</code>")
    try:
        bot.send_photo(
            GROUP_ID,
            file_id,
            caption=caption,
            reply_markup=admin_order_keyboard(order_id)
        )
    except Exception as e:
        print("Error sending receipt to group:", e)
    user_state.pop(message.from_user.id, None)
    bot.send_message(message.chat.id, "رسید شما ثبت شد و برای بررسی به ادمین ارسال شد. پس از تأیید، بلیت صادر می‌شود.", reply_markup=main_menu())


@bot.callback_query_handler(func=lambda call: call.data.startswith("deactshow:"))
def deactivate_show_callback(call):
    if not is_admin(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ادمین ندارید.")
        return
    show_id = int(call.data.split(":", 1)[1])
    if db.deactivate_show(show_id):
        bot.answer_callback_query(call.id, "سانس غیرفعال شد.")
        bot.send_message(call.message.chat.id, f"سانس #{show_id} غیرفعال شد. سفارش‌های قبلی آن حفظ شده‌اند.")
    else:
        bot.answer_callback_query(call.id, "این سانس وجود ندارد یا قبلاً غیرفعال شده است.")


@bot.callback_query_handler(func=lambda call: call.data.startswith("showstats:"))
def show_stats_callback(call):
    if not is_admin(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ادمین ندارید.")
        return

    show_id = int(call.data.split(":", 1)[1])
    show = db.get_show(show_id)

    if not show:
        bot.answer_callback_query(call.id, "این سانس وجود ندارد.", show_alert=True)
        return

    rows = db.count_registrations(show_id)

    lines = [f"👥 <b>ثبت‌نام‌های سانس #{show_id}</b> | {show['title']}"]

    if not rows:
        lines.append("هنوز سفارشی برای این سانس ثبت نشده است.")

    for r in rows:
        lines.append(
            f"{STATUS_LABELS.get(r['status'], r['status'])}: "
            f"{r['orders']} سفارش ({r['tickets']} بلیت)"
        )

    lines.append(
        f"\nظرفیت: {show['capacity']} | "
        f"اشغال‌شده: {db.count_sold(show_id)} | "
        f"باقی‌مانده: {db.remaining_capacity(show_id)}"
    )

    bot.answer_callback_query(call.id)
    bot.send_message(call.message.chat.id, "\n".join(lines))


@bot.callback_query_handler(func=lambda call: call.data.startswith("delshow:"))
def delete_show_ask(call):
    if not is_admin(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ادمین ندارید.")
        return

    show_id = int(call.data.split(":", 1)[1])

    kb = types.InlineKeyboardMarkup()
    kb.row(
        types.InlineKeyboardButton("✅ بله، حذف شود", callback_data=f"delshow_yes:{show_id}"),
        types.InlineKeyboardButton("↩️ خیر", callback_data="delshow_no")
    )

    bot.answer_callback_query(call.id)
    bot.send_message(
        call.message.chat.id,
        f"⚠️ سانس #{show_id} و <b>همه سفارش‌های آن</b> برای همیشه حذف می‌شود. مطمئن هستید؟",
        reply_markup=kb
    )


@bot.callback_query_handler(func=lambda call: call.data.startswith("delshow_yes:"))
def delete_show_confirm(call):
    if not is_admin(call.from_user.id):
        bot.answer_callback_query(call.id, "شما دسترسی ادمین ندارید.")
        return

    show_id = int(call.data.split(":", 1)[1])
    ok = db.delete_show(show_id)

    bot.answer_callback_query(call.id, "حذف شد." if ok else "سانس پیدا نشد.")
    bot.edit_message_text(
        f"🗑 سانس #{show_id} حذف شد." if ok else "این سانس قبلاً حذف شده است.",
        chat_id=call.message.chat.id,
        message_id=call.message.message_id
    )


@bot.callback_query_handler(func=lambda call: call.data == "delshow_no")
def delete_show_no(call):
    bot.answer_callback_query(call.id, "لغو شد.")
    try:
        bot.delete_message(call.message.chat.id, call.message.message_id)
    except Exception as e:
        print("Error deleting message:", e)


@bot.callback_query_handler(func=lambda call: call.data.startswith(("approve:", "reject:")))
def review_order(call):
    if call.message.chat.id != GROUP_ID:
        bot.answer_callback_query(
            call.id,
            "این عملیات فقط از طریق گروه مدیریت امکان‌پذیر است."
        )
        return
    action, order_id_text = call.data.split(":")
    order_id = int(order_id_text)
    order = db.get_order(order_id)
    if not order:
        bot.answer_callback_query(call.id, "سفارش پیدا نشد.")
        return
    if action == "approve":
        # کد هر بلیت بر اساس شناسه یکتای سفارش ساخته می‌شود.
        # در نتیجه کدهای یک سفارش با سفارش‌های دیگر تداخل ندارند.
        codes = db.generate_codes(order["quantity"])

        if not db.approve_order(order_id, codes):
            bot.answer_callback_query(call.id, "این سفارش قبلاً بررسی شده است.")
            return

        bot.edit_message_reply_markup(
            chat_id=call.message.chat.id,
            message_id=call.message.message_id,
            reply_markup=order_status_keyboard("approved")
        )        
                
        event_title = order["show_title"] or EVENT_TITLE
        cinema_name = order["show_cinema"] or CINEMA_NAME
        show_date = order["show_date"] or SHOW_DATE

        ticket_text = (
            f"🎉 <b>پرداخت شما تأیید شد!</b>\n"
            f"🎟 <b>بلیت‌های اکران خیریه</b>\n\n"
            f"نام خریدار: {order['buyer_name']}\n"
            f"عنوان سانس: {event_title}\n"
            f"سینما: {cinema_name}\n"
            f"تاریخ و ساعت: {show_date}\n"
            f"تعداد بلیت: {order['quantity']}\n"
            f"شماره سفارش: #{order_id}\n\n"
            f"<b>شماره بلیت‌های شما:</b>\n"
        )

        ticket_text += "\n".join(
            f"🎫 <code>{code}</code>" for code in codes
        )

        ticket_text += (
            "\n\nلطفاً این پیام و شماره بلیت‌ها را هنگام ورود به سینما "
            "به مسئول پذیرش نشان دهید.\nهر بلیت فقط یک‌بار قابل استفاده است."
        )

        try:
            bot.send_message(order["user_id"], ticket_text)
            send_ticket_qrs(order["user_id"], codes)
        except Exception:
            # اگر کاربر ربات را مسدود کرده باشد، پرداخت در دیتابیس تأیید شده است؛
            # ادمین کدها را می‌بیند و می‌تواند آن‌ها را به خریدار برساند.
            bot.send_message(
                call.message.chat.id,
                "پرداخت تأیید شد، اما ارسال پیام به خریدار ممکن نشد. "
                "احتمالاً کاربر ربات را مسدود کرده است."
            )

        bot.answer_callback_query(call.id, "پرداخت تأیید و کد بلیت صادر شد.")
        bot.send_message(
            call.message.chat.id,
            f"✅ سفارش #{order_id} تأیید شد.\n"
            f"کدهای بلیت: {', '.join(codes)}"
        )
    else:
        if not db.reject_order(order_id):
            bot.answer_callback_query(call.id, "این سفارش قبلاً بررسی شده است.")
            return
        
        bot.edit_message_reply_markup(
            chat_id=call.message.chat.id,
            message_id=call.message.message_id,
            reply_markup=order_status_keyboard("rejected")
        )        
        bot.send_message(order["user_id"], f"پرداخت سفارش #{order_id} تأیید نشد. لطفاً برای پیگیری با پشتیبانی تماس بگیرید.")
        bot.answer_callback_query(call.id, "پرداخت رد شد.")
        bot.send_message(call.message.chat.id, f"سفارش #{order_id} رد شد.")


def cancel_keyboard():
    keyboard = types.InlineKeyboardMarkup()
    keyboard.add(
        types.InlineKeyboardButton(
            "❌ انصراف از رزرو",
            callback_data="cancel_reservation"
        )
    )
    return keyboard

@bot.callback_query_handler(
    func=lambda call: call.data == "cancel_reservation"
)
def cancel_reservation(call):

    user_id = call.from_user.id

    if call.message.chat.type != "private":
        bot.answer_callback_query(
            call.id,
            "این گزینه فقط در گفت‌وگوی خصوصی قابل استفاده است."
        )
        return

    data = user_state.get(user_id)

    if not data:
        bot.answer_callback_query(
            call.id,
            "فرایند رزرو فعالی ندارید."
        )
        return

    user_state.pop(user_id, None)

    bot.answer_callback_query(
        call.id,
        "رزرو لغو شد."
    )

    bot.send_message(
        call.message.chat.id,
        "❌ <b>فرایند رزرو لغو شد.</b>\n\n"
        "هر زمان خواستید می‌توانید دوباره برای خرید بلیت اقدام کنید.",
        reply_markup=main_menu()
    )
    

def remove_cancel_button(chat_id, message_id):
    try:
        bot.edit_message_reply_markup(
            chat_id=chat_id,
            message_id=message_id,
            reply_markup=None
        )
    except Exception as e:
        print("Error removing cancel button:", e)


print("Cinema charity bot is running...")
bot.infinity_polling(skip_pending=True)