import logging
from aiogram.types import InputRichMessage, RichBlockSectionHeading, RichBlockParagraph, RichTextBold, RichTextUnderline, RichBlockDivider, RichBlockBlockQuotation
from aiogram import Router, F, Bot
from aiogram.types import Message, CallbackQuery, ReactionTypeEmoji
from aiogram.fsm.context import FSMContext
from aiogram.filters import StateFilter

from utils import texts
from utils.date_utils import to_shamsi
from states.forms import ProjectCreation, WalletDeposit, ClientRating, ChatSession, WalletWithdraw, ProjectEditing, PromoRedeem, MilestoneCreation
from database import db
from keyboards import inline, reply
from config import VIP_MONTHLY_PRICE
from handlers.common import check_channel_membership, get_channel_invite_link

logger = logging.getLogger(__name__)
router = Router()

# ── Project Creation Step Back & Cancellation ──────────────────────────

@router.message(StateFilter(ProjectCreation), F.text == "❌ انصراف از ثبت")
async def cancel_project_creation(message: Message, state: FSMContext, db_user):
    await state.clear()
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer("❌ <b>ثبت پروژه لغو شد.</b>", reply_markup=reply.client_main_menu(is_admin=is_admin), parse_mode="HTML")

@router.message(StateFilter(ProjectCreation), F.text == "🔙 مرحله قبلی")
async def previous_step_project_creation(message: Message, state: FSMContext, db_user):
    current_state = await state.get_state()
    if current_state == ProjectCreation.waiting_for_desc.state:
        await state.set_state(ProjectCreation.waiting_for_title)
        await message.answer("📝 <b>مرحله ۱ از ۸: عنوان پروژه</b>\n\nلطفاً یک عنوان کوتاه و جذاب برای پروژه خود انتخاب کنید تا فریلنسرها در اولین نگاه متوجه موضوع شوند.\n\n💡 <i>مثال: طراحی وب‌سایت شرکتی با وردپرس</i>", reply_markup=reply.project_creation_keyboard(is_first_step=True), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_category.state:
        await state.set_state(ProjectCreation.waiting_for_desc)
        await message.answer("📋 <b>مرحله ۲ از ۸: توضیحات پروژه</b>\n\nتوضیحات کامل پروژه، نیازمندی‌ها، مهارت‌های مورد نظر و خروجی نهایی را با جزئیات بنویسید.\n\n⚠️ <i>حداقل ۲۰ کاراکتر وارد کنید. هرچه توضیحات دقیق‌تر باشد، پیشنهادهای بهتری دریافت خواهید کرد.</i>", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_project_type.state:
        await state.set_state(ProjectCreation.waiting_for_category)
        await message.answer("🏷 <b>مرحله ۳ از ۸: دسته‌بندی پروژه</b>\n\nمناسب‌ترین دسته‌بندی را برای پروژه خود از منوی زیر انتخاب کنید:", reply_markup=inline.category_inline_menu(), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_file.state:
        await state.set_state(ProjectCreation.waiting_for_project_type)
        await message.answer("💼 <b>مرحله ۴ از ۸: نوع همکاری</b>\n\nنوع پروژه خود را انتخاب کنید:\n\n🔸 <b>پروژه‌ای (مقطعی):</b> کار به صورت کارمزدی و مقطعی است (تبادل اطلاعات تماس در چت ممنوع).\n👔 <b>تمام‌وقت / استخدام:</b> استخدام فریلنسر به صورت دائم (تبادل راه‌های ارتباطی آزاد است و شامل هزینه ثبت می‌باشد).", reply_markup=reply.project_type_menu_with_back(), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_budget_min.state:
        await state.set_state(ProjectCreation.waiting_for_file)
        await message.answer("📎 <b>مرحله ۵ از ۸: فایل‌های ضمیمه</b>\n\nاگر مستندات، فایل طرح اولیه، سناریو یا تصویری دارید که به درک بهتر پروژه کمک می‌کند، آن را ارسال کنید. در غیر این صورت روی دکمه «⏭ رد کردن» کلیک کنید:", reply_markup=reply.project_creation_keyboard(show_skip=True), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_budget_max.state:
        await state.set_state(ProjectCreation.waiting_for_budget_min)
        await message.answer("💵 <b>مرحله ۶ از ۸: حداقل بودجه (تومان)</b>\n\nحداقل مبلغی را که مایلید برای این پروژه پرداخت کنید به <b>تومان (فقط عدد)</b> وارد کنید:", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_deadline.state:
        await state.set_state(ProjectCreation.waiting_for_budget_max)
        await message.answer("💵 <b>مرحله ۷ از ۸: حداکثر بودجه (تومان)</b>\n\nحداکثر مبلغی را که برای پروژه در نظر گرفته‌اید به <b>تومان (فقط عدد)</b> وارد کنید:", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")
        
    elif current_state == ProjectCreation.waiting_for_confirmation.state:
        await state.set_state(ProjectCreation.waiting_for_deadline)
        await message.answer("📅 <b>مرحله ۸ از ۸: مهلت تحویل</b>\n\nفریلنسر تا چند روز پس از قبولی پروژه فرصت دارد کار را تحویل دهد؟ (فقط عدد)\n\n💡 <i>برای پروژه‌های بدون مهلت عدد ۰ را بفرستید.</i>", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")

# ── Project Creation FSM ──────────────────────────────────────────────

@router.message(F.text == texts.BTN_POST_PROJECT)
async def start_project_creation(message: Message, state: FSMContext, db_user, bot: Bot):
    if not await check_channel_membership(bot, message.from_user.id):
        channel_id = await db.get_setting('channel_id')
        channel_link = await get_channel_invite_link(bot, channel_id)
        return await message.answer(
            texts.CHANNEL_JOIN_REQ,
            reply_markup=inline.join_channel_keyboard(channel_link),
            parse_mode="HTML"
        )
    if not db_user or dict(db_user).get('is_verified', 0) != 1:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        builder.button(text="🔐 شروع احراز هویت", callback_data="profile_start_verification", style="danger")
        return await message.answer(
            "⚠️ <b>حساب کاربری شما احراز هویت نشده است!</b>\n\n"
            "برای ارسال پروژه جدید و استفاده از امکانات کارفرمایی، ابتدا باید هویت خود را در سیستم ثبت کنید.",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
    await state.set_state(ProjectCreation.waiting_for_title)
    await message.answer("📝 <b>مرحله ۱ از ۸: عنوان پروژه</b>\n\nلطفاً یک عنوان کوتاه و جذاب برای پروژه خود انتخاب کنید تا فریلنسرها در اولین نگاه متوجه موضوع شوند.\n\n💡 <i>مثال: طراحی وب‌سایت شرکتی با وردپرس</i>", reply_markup=reply.project_creation_keyboard(is_first_step=True), parse_mode="HTML")

@router.message(ProjectCreation.waiting_for_title)
async def process_title(message: Message, state: FSMContext):
    if len(message.text) < 5:
        return await message.answer("❌ <b>عنوان پروژه خیلی کوتاه است.</b> حداقل ۵ کاراکتر وارد کنید:")
        
    from utils.ai_moderation import moderate_text
    is_clean, reason = moderate_text(message.text)
    if not is_clean:
        return await message.answer(f"⚠️ <b>خطا در عنوان پروژه:</b>\n{reason}\n\nلطفاً عنوان را مجدداً بدون اطلاعات تماس ارسال کنید:")
        
    await state.update_data(title=message.text)
    await state.set_state(ProjectCreation.waiting_for_desc)
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer(
        "📋 <b>مرحله ۲ از ۸: توضیحات پروژه</b>\n\nتوضیحات کامل پروژه، نیازمندی‌ها، مهارت‌های مورد نظر و خروجی نهایی را با جزئیات بنویسید.\n\n⚠️ <i>حداقل ۲۰ کاراکتر وارد کنید. هرچه توضیحات دقیق‌تر باشد، پیشنهادهای بهتری دریافت خواهید کرد.</i>",
        reply_markup=reply.project_creation_keyboard(),
        parse_mode="HTML"
    )

@router.message(ProjectCreation.waiting_for_desc)
async def process_desc(message: Message, state: FSMContext):
    if len(message.text) < 20:
        return await message.answer("❌ <b>توضیحات کافی نیست.</b> حداقل ۲۰ کاراکتر وارد کنید:")
        
    from utils.ai_moderation import moderate_text
    is_clean, reason = moderate_text(message.text)
    if not is_clean:
        return await message.answer(f"⚠️ <b>خطا در توضیحات پروژه:</b>\n{reason}\n\nلطفاً توضیحات پروژه را ویرایش کرده و بدون اطلاعات تماس مجدداً ارسال کنید:")
        
    await state.update_data(description=message.text)
    await state.set_state(ProjectCreation.waiting_for_category)
    try: await message.react([ReactionTypeEmoji(emoji="✍️")])
    except: pass
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer("🏷 <b>مرحله ۳ از ۸: دسته‌بندی پروژه</b>\n\nمناسب‌ترین دسته‌بندی را برای پروژه خود از منوی زیر انتخاب کنید:", reply_markup=inline.category_inline_menu(), parse_mode="HTML")

@router.callback_query(ProjectCreation.waiting_for_category)
async def process_category(callback: CallbackQuery, state: FSMContext):
    category = callback.data.replace('selcat_', '')
    await state.update_data(category=category)
    await state.set_state(ProjectCreation.waiting_for_project_type)
    try: await callback.message.delete()
    except: pass
    await callback.message.answer("💼 <b>مرحله ۴ از ۸: نوع همکاری</b>\n\nنوع پروژه خود را انتخاب کنید:\n\n🔸 <b>پروژه‌ای (مقطعی):</b> کار به صورت کارمزدی و مقطعی است (تبادل اطلاعات تماس در چت ممنوع).\n👔 <b>تمام‌وقت / استخدام:</b> استخدام فریلنسر به صورت دائم (تبادل راه‌های ارتباطی آزاد است و شامل هزینه ثبت می‌باشد).", reply_markup=inline.project_type_inline_menu(), parse_mode="HTML")

@router.callback_query(ProjectCreation.waiting_for_project_type)
async def process_project_type(callback: CallbackQuery, state: FSMContext):
    ptype = callback.data.replace('seltype_', '')
    ptype = 'full_time' if 'full_time' in ptype else 'project_based'
    await state.update_data(type=ptype)
    
    await state.set_state(ProjectCreation.waiting_for_file)
    try: await callback.message.delete()
    except: pass
    await callback.message.answer("📎 <b>مرحله ۵ از ۸: فایل‌های ضمیمه</b>\n\nاگر مستندات، فایل طرح اولیه، سناریو یا تصویری دارید که به درک بهتر پروژه کمک می‌کند، آن را ارسال کنید. در غیر این صورت روی دکمه «⏭ رد کردن» کلیک کنید:", reply_markup=reply.project_creation_keyboard(show_skip=True), parse_mode="HTML")

@router.message(ProjectCreation.waiting_for_file)
async def process_file(message: Message, state: FSMContext):
    if message.text != texts.BTN_SKIP:
        if message.document:
            await state.update_data(file_id=message.document.file_id)
        elif message.photo:
            await state.update_data(file_id=message.photo[-1].file_id)
        elif message.text:
            return await message.answer("❌ لطفاً یک فایل ارسال کنید یا دکمه «⏭ رد کردن» را بزنید.")
    
    await state.set_state(ProjectCreation.waiting_for_budget_min)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer("💵 <b>مرحله ۶ از ۸: حداقل بودجه (تومان)</b>\n\nحداقل مبلغی را که مایلید برای این پروژه پرداخت کنید به <b>تومان (فقط عدد)</b> وارد کنید:", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")

@router.message(ProjectCreation.waiting_for_budget_min)
async def process_budget_min(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    budget_val = int(message.text)
    min_budget = int(await db.get_setting('min_project_budget', '10000'))
    if budget_val < min_budget:
        return await message.answer(f"❌ حداقل بودجه پروژه نمی‌تواند کمتر از {min_budget:,} تومان باشد. دوباره تلاش کنید:")
    await state.update_data(budget_min=budget_val)
    await state.set_state(ProjectCreation.waiting_for_budget_max)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer("💵 <b>مرحله ۷ از ۸: حداکثر بودجه (تومان)</b>\n\nحداکثر مبلغی را که برای پروژه در نظر گرفته‌اید به <b>تومان (فقط عدد)</b> وارد کنید:", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")

@router.message(ProjectCreation.waiting_for_budget_max)
async def process_budget_max(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    data = await state.get_data()
    if int(message.text) < data['budget_min']:
        return await message.answer("❌ حداکثر بودجه باید از حداقل بیشتر باشد.")
    await state.update_data(budget_max=int(message.text))
    await state.set_state(ProjectCreation.waiting_for_deadline)
    try: await message.react([ReactionTypeEmoji(emoji="⏳")])
    except: pass
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer("📅 <b>مرحله ۸ از ۸: مهلت تحویل</b>\n\nفریلنسر تا چند روز پس از قبولی پروژه فرصت دارد کار را تحویل دهد؟ (فقط عدد)\n\n💡 <i>برای پروژه‌های بدون مهلت عدد ۰ را بفرستید.</i>", reply_markup=reply.project_creation_keyboard(), parse_mode="HTML")

@router.message(ProjectCreation.waiting_for_deadline)
async def process_deadline(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    
    deadline = int(message.text)
    await state.update_data(deadline_days=deadline)
    await state.set_state(ProjectCreation.waiting_for_confirmation)
    
    data = await state.get_data()
    type_fa = "👔 تمام‌وقت / استخدام" if data.get('type') == 'full_time' else "💼 پروژه‌ای (مقطعی)"
    file_str = "📎 فایل ضمیمه شده است" if data.get('file_id') else "❌ ندارد"
    deadline_str = f"{deadline} روز" if deadline > 0 else "بدون محدودیت زمانی"
    
    price_info = ""
    if data.get('type') == 'full_time':
        price = await db.get_setting_int('fulltime_project_price', 100000)
        price_info = f"\n⚠️ <b>هزینه ثبت پروژه تمام‌وقت:</b> <code>{price:,}</code> تومان (که در صورت تأیید از موجودی شما کسر خواهد شد)"
        
    summary_text = (
        f"🔍 <b>پیش‌نمایش اطلاعات پروژه شما</b>\n"
        f"━━━━━━━━━━━━━━━━━\n"
        f"📌 <b>عنوان:</b> {data['title']}\n"
        f"🏷 <b>دسته‌بندی:</b> {data['category']}\n"
        f"💼 <b>نوع همکاری:</b> {type_fa}\n"
        f"💵 <b>بودجه پیشنهادی:</b> {data['budget_min']:,} تا {data['budget_max']:,} تومان\n"
        f"📅 <b>مهلت تحویل:</b> {deadline_str}\n"
        f"📎 <b>فایل ضمیمه:</b> {file_str}\n"
        f"━━━━━━━━━━━━━━━━━\n"
        f"📋 <b>توضیحات پروژه:</b>\n"
        f"<blockquote>{data['description']}</blockquote>"
        f"{price_info}\n\n"
        f"آیا اطلاعات پروژه مورد تأیید است و مایل به ارسال جهت بررسی مدیریت هستید؟"
    )
    
    await message.answer(
        summary_text,
        reply_markup=inline.project_creation_confirmation_keyboard(),
        parse_mode="HTML"
    )

@router.callback_query(ProjectCreation.waiting_for_confirmation, F.data == "project_confirm_submit")
async def process_project_confirmation(callback: CallbackQuery, state: FSMContext, db_user, bot: Bot):
    data = await state.get_data()
    
    if data.get('type') == 'full_time':
        price = await db.get_setting_int('fulltime_project_price', 100000)
        user = await db.get_user(callback.from_user.id)
        if user['balance'] < price:
            await state.clear()
            return await callback.message.edit_text(
                f"❌ <b>موجودی کافی نیست</b>\n\nبرای ثبت پروژه تمام‌وقت به مبلغ <b>{price:,} تومان</b> نیاز دارید.\nموجودی فعلی شما: {user['balance']:,} تومان\nلطفاً کیف پول خود را شارژ کرده و دوباره تلاش کنید.",
                reply_markup=inline.wallet_keyboard(role='client'),
                parse_mode="HTML"
            )
        # Deduct balance
        await db.update_balance(callback.from_user.id, -price, 'expense', 'هزینه ثبت پروژه تمام‌وقت')
        
    project_id = await db.create_project(
        client_id=callback.from_user.id,
        title=data['title'],
        description=data['description'],
        budget_min=data['budget_min'],
        budget_max=data['budget_max'],
        deadline_days=data['deadline_days'],
        category=data.get('category', ''),
        file_id=data.get('file_id'),
        project_type=data.get('type', 'project_based')
    )
    
    await state.clear()
    
    # Refresh to client main menu keyboard
    is_admin = callback.from_user.id in __import__('config').ADMIN_IDS
    await callback.message.delete()
    await callback.message.answer(
        "📝 <b>پروژه شما با موفقیت ثبت شد!</b>\n\n"
        f"🆔 شماره پروژه: `#{project_id}`\n\n"
        "⚠️ جهت فعال‌سازی، پروژه برای تایید ادمین ارسال گردید. به محض تایید به شما اطلاع داده خواهد شد.",
        reply_markup=reply.client_main_menu(is_admin=is_admin),
        parse_mode="HTML",
        message_effect_id="5046509860389126442"
    )
    await callback.answer("✅ ثبت شد!")
    
    # Alert Admins
    recipients = await db.get_admin_recipients('projects')
    for admin_id in recipients:
        try:
            await bot.send_message(
                admin_id,
                f"📌 <b>پروژه جدید در انتظار تایید:</b>\n\n"
                f"🆔 شماره پروژه: `#{project_id}`\n"
                f"👔 کارفرما: `{callback.from_user.id}` — {db_user['full_name'] or 'ناشناس'}\n"
                f"🏷 عنوان: <b>{data['title']}</b>\n"
                f"💰 بودجه: <b>{data['budget_min']:,}</b> تا <b>{data['budget_max']:,}</b> تومان\n"
                f"⏱ مهلت: <b>{data['deadline_days']} روز</b>\n\n"
                f"📋 توضیحات:\n{data['description']}",
                reply_markup=inline.admin_project_approval_decision_keyboard(project_id),
                parse_mode="HTML"
            )
        except:
            pass

@router.callback_query(ProjectCreation.waiting_for_confirmation, F.data == "project_confirm_cancel")
async def process_project_cancel_callback(callback: CallbackQuery, state: FSMContext, db_user):
    await state.clear()
    is_admin = callback.from_user.id in __import__('config').ADMIN_IDS
    await callback.message.delete()
    await callback.message.answer(
        "❌ <b>ثبت پروژه لغو شد.</b>",
        reply_markup=reply.client_main_menu(is_admin=is_admin),
        parse_mode="HTML"
    )
    await callback.answer("لغو شد.")

# ── My Projects ────────────────────────────────────────────────────────

async def show_client_projects_page(callback_or_msg, user_id: int, offset: int = 0):
    limit = 8
    total = await db.count_client_projects(user_id)
    if total == 0:
        if isinstance(callback_or_msg, CallbackQuery):
            await callback_or_msg.message.edit_text(texts.NO_PROJECTS, parse_mode="HTML")
        else:
            await callback_or_msg.answer(texts.NO_PROJECTS, parse_mode="HTML")
        return
        
    projects = await db.get_client_projects_paginated(user_id, limit=limit, offset=offset)
    kb = inline.my_projects_keyboard(projects, offset=offset, total=total, limit=limit)
    
    stats = await db.get_client_project_status_stats(user_id)
    html_content = (
        "<h1>📁 پروژه‌های شما</h1>"
        "<hr/>"
        "<h3>📊 خلاصه وضعیت پروژه‌ها:</h3>"
        "<table bordered>"
        f"  <tr><th>🟢 باز و در انتظار پیشنهاد</th><td><b>{stats.get('open', 0)}</b></td></tr>"
        f"  <tr><th>⏳ در حال انجام کار</th><td><b>{stats.get('in_progress', 0) + stats.get('delivered', 0)}</b></td></tr>"
        f"  <tr><th>✅ تکمیل شده</th><td><b>{stats.get('completed', 0)}</b></td></tr>"
        f"  <tr><th>❌ لغو شده</th><td><b>{stats.get('cancelled', 0)}</b></td></tr>"
        "</table>"
        "<br/>"
        f"<p>صفحه {offset // limit + 1} از {(total - 1) // limit + 1}</p>"
    )
    
    rich_msg = InputRichMessage(html=html_content)
    
    if isinstance(callback_or_msg, CallbackQuery):
        await callback_or_msg.message.edit_text(text=None, reply_markup=kb, rich_message=rich_msg)
    else:
        await callback_or_msg.bot.send_rich_message(
            chat_id=callback_or_msg.chat.id,
            rich_message=rich_msg,
            reply_markup=kb
        )

@router.message(F.text == texts.BTN_MY_PROJECTS)
async def my_projects(message: Message, db_user):
    await show_client_projects_page(message, message.from_user.id, offset=0)

@router.callback_query(F.data.startswith("my_projects_page_"))
async def my_projects_page_handler(callback: CallbackQuery):
    offset = int(callback.data.split("_")[-1])
    await show_client_projects_page(callback, callback.from_user.id, offset=offset)
    await callback.answer()

@router.callback_query(F.data.startswith("client_project_"))
async def view_client_project(callback: CallbackQuery):
    parts = callback.data.split("_")
    project_id = int(parts[2])
    offset = int(parts[3]) if len(parts) > 3 else 0
    
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
    project = dict(project)
    bids = await db.get_bids_for_project(project_id)
    
    status = texts.PROJECT_STATUS_MAP.get(project['status'], project['status'])
    deadline = f"{project['deadline_days']} روز" if project['deadline_days'] else "بدون محدودیت"
    category = project.get('category', 'نامشخص') or 'نامشخص'
    type_str = "تمام‌وقت/استخدامی" if project.get('project_type') == 'full_time' else "پروژه‌ای مقطعی"
    file_icon = "📎 دارد" if project.get('file_id') else "ندارد"
    
    title_prefix = "⭐ <b>پروژه ویژه</b><br/>" if project['is_featured'] else ""
    
    html_content = (
        f"{title_prefix}"
        f"<h1>📌 {project['title']}</h1>"
        "<hr/>"
        f"<blockquote>📋 {project['description']}</blockquote>"
        "<br/>"
        "<table bordered>"
        f"  <tr><th>🏷 دسته‌بندی</th><td>{category}</td></tr>"
        f"  <tr><th>💼 نوع همکاری</th><td>{type_str}</td></tr>"
        f"  <tr><th>💰 بودجه</th><td>{project['budget_min']:,} تا {project['budget_max']:,} تومان</td></tr>"
        f"  <tr><th>📅 مهلت تحویل</th><td>{deadline}</td></tr>"
        f"  <tr><th>📊 وضعیت پروژه</th><td>{status}</td></tr>"
        f"  <tr><th>👥 تعداد پیشنهادها</th><td>{len(bids)} پیشنهاد</td></tr>"
        f"  <tr><th>📎 فایل ضمیمه</th><td>{file_icon}</td></tr>"
        "</table>"
    )
    
    if project['status'] == 'delivered' and project['delivery_text']:
        delivery_file_str = "<br/>📎 <i>فایل کار ضمیمه شده است. برای دانلود، از دکمه زیر استفاده کنید.</i>" if project.get('delivery_file_id') else ""
        html_content += (
            "<br/>"
            "<h3>📥 توضیحات فایل تحویل شده فریلنسر:</h3>"
            f"<blockquote>{project['delivery_text']}{delivery_file_str}</blockquote>"
        )
        
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_management_keyboard(
            project_id, 
            project['status'], 
            project['is_featured'], 
            is_fulltime=project.get('is_fulltime', 0),
            has_delivery_file=bool(project.get('delivery_file_id')),
            back_callback_data=f"my_projects_page_{offset}"
        ),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("cancel_project_"))
async def cancel_project(callback: CallbackQuery):
    project_id = int(callback.data.split("_")[-1])
    await callback.message.edit_text(
        "⚠️ آیا مطمئنید که می‌خواهید این پروژه را لغو کنید؟",
        reply_markup=inline.confirm_keyboard(f"confirm_cancel_{project_id}", f"client_project_{project_id}")
    )
    await callback.answer()

@router.callback_query(F.data.startswith("confirm_cancel_"))
async def confirm_cancel_project(callback: CallbackQuery):
    project_id = int(callback.data.split("_")[-1])
    await db.cancel_project(project_id)
    await callback.message.edit_text("✅ پروژه با موفقیت لغو شد.")
    await callback.answer()

@router.callback_query(F.data.startswith("view_bids_"))
async def view_bids(callback: CallbackQuery):
    project_id = int(callback.data.split("_")[-1])
    bids = await db.get_bids_for_project(project_id)
    if not bids:
        await callback.answer("هنوز هیچ پیشنهادی ارسال نشده است.", show_alert=True)
        return
    
    html_content = (
        "<h1>👥 پیشنهادهای دریافتی پروژه</h1>"
        "<hr/>"
        f"<p>تعداد <b>{len(bids)}</b> پیشنهاد برای این پروژه ثبت شده است.</p>"
        "<p><i>روی هر پیشنهاد کلیک کنید تا مشخصات و پروپوزال فریلنسر را ببینید:</i></p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.bids_keyboard(bids, project_id),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("client_sort_bids_menu_"))
async def client_sort_bids_menu_handler(callback: CallbackQuery):
    project_id = int(callback.data.split("_")[-1])
    html_content = (
        "<h1>🔃 مرتب‌سازی پیشنهادها</h1>"
        "<hr/>"
        "<p>جهت بررسی آسان‌تر، روش مرتب‌سازی لیست پیشنهادها را انتخاب کنید:</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.bid_sorting_options_keyboard(project_id),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("client_sort_bids_"))
async def client_sort_bids_handler(callback: CallbackQuery):
    parts = callback.data.split("_")
    project_id = int(parts[3])
    sort_by = "_".join(parts[4:])
    
    bids = await db.get_bids_for_project_sorted(project_id, sort_by=sort_by)
    if not bids:
        await callback.answer("پیشنهادی یافت نشد.", show_alert=True)
        return
        
    sort_names = {
        'vip': 'اولویت با VIP',
        'budget_asc': 'کمترین بودجه',
        'rating_desc': 'بالاترین امتیاز فریلنسر',
        'delivery_asc': 'سریع‌ترین تحویل'
    }
    sort_name = sort_names.get(sort_by, sort_by)
    
    html_content = (
        "<h1>👥 پیشنهادهای دریافتی پروژه</h1>"
        "<hr/>"
        f"<p>تعداد <b>{len(bids)}</b> پیشنهاد (مرتب‌شده بر اساس: <b>{sort_name}</b>) یافت شد.</p>"
        "<p><i>روی هر پیشنهاد کلیک کنید تا مشخصات و پروپوزال فریلنسر را ببینید:</i></p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.bids_keyboard(bids, project_id),
        rich_message=rich_msg
    )
    await callback.answer(f"مرتب‌سازی بر اساس: {sort_name}")

@router.callback_query(F.data.startswith("bid_detail_"))
async def bid_detail(callback: CallbackQuery):
    bid_id = int(callback.data.split("_")[-1])
    bid = await db.get_bid(bid_id)
    if not bid:
        return await callback.answer("پیشنهاد یافت نشد.", show_alert=True)
    user = await db.get_user(bid['freelancer_id'])
    name = user['full_name'] if user else "ناشناس"
    vip_badge = " ⭐" if (user and user['is_vip']) else ""
    
    # Retrieve badges
    badges = await db.get_user_badges_emojis(bid['freelancer_id'])
    badge_label = f" {badges}" if badges else ""
    
    boost_text = ""
    try:
        if bid['boost_bids'] > 0:
            boost_text = f"🚀 <b>این پیشنهاد توسط فریلنسر ویژه (با مصرف {bid['boost_bids']} بید اضافی) ارتقا یافته است.</b>"
    except:
        pass
        
    # Fetch freelancer scorecard
    scorecard = await db.get_freelancer_scorecard(bid['freelancer_id'])
    completed_projects = scorecard['completed_count']
    avg_rating = scorecard['avg_rating']
    on_time_rate = scorecard['on_time_rate']
    response_rate = scorecard['response_rate']
    
    html_content = (
        f"<h1>📨 پیشنهاد فریلنسر: {name}{vip_badge}{badge_label}</h1>"
        "<hr/>"
        "<table bordered>"
        f"  <tr><th>💰 مبلغ پیشنهادی</th><td>{bid['amount']:,} تومان</td></tr>"
        f"  <tr><th>📅 زمان تحویل تعهد شده</th><td>{bid['delivery_days']} روز</td></tr>"
        f"  <tr><th>📊 پروژه‌های موفق</th><td>{completed_projects} پروژه</td></tr>"
        f"  <tr><th>⭐ امتیاز میانگین</th><td>{avg_rating:.1f} از ۵</td></tr>"
        f"  <tr><th>⏱ تحویل به موقع</th><td><b>{on_time_rate}%</b></td></tr>"
        f"  <tr><th>💬 نرخ پاسخ‌دهی</th><td><b>{response_rate}%</b></td></tr>"
        "</table>"
        "<br/>"
        "<h3>📄 متن معرفی و پیشنهاد کاری (Proposal):</h3>"
        f"<blockquote>{bid['proposal']}</blockquote>"
    )
    if boost_text:
        html_content += f"<br/><p>{boost_text}</p>"
        
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.bid_detail_keyboard(bid_id, bid['project_id'], bid['freelancer_id']),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("accept_bid_"))
async def accept_bid(callback: CallbackQuery, bot: Bot, db_user):
    parts = callback.data.split("_")
    bid_id = int(parts[2])
    project_id = int(parts[3])

    bid = await db.get_bid(bid_id)
    project = await db.get_project(project_id)
    if not bid or not project:
        return await callback.answer("خطا در یافتن اطلاعات.", show_alert=True)

    client_id = callback.from_user.id
    project_type = project.get('project_type', 'project_based')

    if project_type == 'full_time':
        success = await db.accept_fulltime_bid(bid_id, project_id, bid['freelancer_id'], client_id)
        if not success:
            return await callback.answer("عملیات ناموفق بود.", show_alert=True)

        await callback.message.edit_text(
            f"✅ <b>پیشنهاد استخدام پذیرفته شد!</b>\n\n"
            f"با توجه به ماهیت <b>تمام‌وقت</b> این همکاری، تراکنش مالی و کسر وجه (Escrow) در ربات انجام نمی‌شود.\n"
            f"طرفین مجاز به تبادل اطلاعات تماس در بخش گفتگو هستند.\n\n"
            f"پروژه استخدام «{project['title']}» با موفقیت آغاز شد.",
            parse_mode="HTML"
        )
    else:
        balance = db_user['balance'] or 0
        if balance < bid['amount']:
            await show_dynamic_payment_options(
                callback=callback,
                required_amount=bid['amount'],
                balance=balance,
                action_type="hire",
                action_meta=f"{bid_id}_{project_id}",
                back_callback=f"bid_detail_{bid_id}"
            )
            return

        builder = InlineKeyboardBuilder()
        builder.button(text="💰 پرداخت یکجا", callback_data=f"pay_full_{bid_id}_{project_id}")
        builder.button(text="📑 پرداخت چندمرحله‌ای (Milestones)", callback_data=f"pay_milestones_{bid_id}_{project_id}")
        builder.button(text="🔙 انصراف", callback_data=f"bid_detail_{bid_id}")
        builder.adjust(1)
        
        await callback.message.edit_text(
            f"💼 <b>نحوه پرداخت پروژه «{project['title']}»</b>\n\n"
            f"مبلغ کل پیشنهاد فریلنسر: <b>{bid['amount']:,} تومان</b>\n\n"
            f"چگونه مایلید پرداخت امن (Escrow) پروژه را مدیریت کنید؟\n"
            f"در پرداخت چندمرحله‌ای می‌توانید پروژه را به فازهای مجزا تقسیم کرده و پس از تایید هر فاز، وجه آن فاز را به فریلنسر آزاد کنید.",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
        await callback.answer()

@router.callback_query(F.data.startswith("pay_full_"))
async def pay_full_handler(callback: CallbackQuery, bot: Bot, db_user):
    parts = callback.data.split("_")
    bid_id = int(parts[2])
    project_id = int(parts[3])
    
    bid = await db.get_bid(bid_id)
    project = await db.get_project(project_id)
    if not bid or not project:
        return await callback.answer("خطا در یافتن اطلاعات.", show_alert=True)
        
    client_id = callback.from_user.id
    balance = db_user['balance'] or 0
    if balance < bid['amount']:
        return await callback.answer("موجودی شما کافی نیست.", show_alert=True)
        
    success = await db.accept_bid(bid_id, project_id, bid['freelancer_id'], client_id, bid['amount'])
    if not success:
        return await callback.answer("عملیات ناموفق بود یا موجودی تغییر کرده است.", show_alert=True)
        
    await callback.message.edit_text(
        f"✅ <b>پیشنهاد پذیرفته شد!</b>\n"
        f"مبلغ <b>{bid['amount']:,} تومان</b> از حساب شما کسر و در صندوق امن پلتفرم (Escrow) بلوکه شد.\n\n"
        f"پروژه «{project['title']}» در حال انجام است.",
        parse_mode="HTML"
    )
    
    # Notify freelancer
    client_name = db_user['full_name'] or "کارفرما"
    try:
        await bot.send_message(
            bid['freelancer_id'],
            texts.bid_accepted_notification(project['title'], client_name, bid['amount']),
            message_effect_id="5104841245755180586",
            reply_markup=inline.freelancer_accepted_project_keyboard(project['project_id'], 'in_progress'),
            parse_mode="HTML"
        )
    except: pass

@router.callback_query(F.data.startswith("pay_milestones_"))
async def pay_milestones_handler(callback: CallbackQuery, state: FSMContext, db_user):
    parts = callback.data.split("_")
    bid_id = int(parts[2])
    project_id = int(parts[3])
    
    bid = await db.get_bid(bid_id)
    project = await db.get_project(project_id)
    if not bid or not project:
        return await callback.answer("خطا در یافتن اطلاعات.", show_alert=True)
        
    await state.update_data(
        bid_id=bid_id,
        project_id=project_id,
        total_amount=bid['amount'],
        remaining_amount=bid['amount'],
        milestones=[]
    )
    
    await state.set_state(MilestoneCreation.waiting_for_amount)
    
    await callback.message.answer(
        f"📑 <b>تعریف پرداخت مرحله‌ای (Milestones)</b>\n\n"
        f"مبلغ کل پروژه: <b>{bid['amount']:,} تومان</b>\n\n"
        f"لطفاً مبلغ **فاز اول** را به تومان وارد کنید:",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(MilestoneCreation.waiting_for_amount)
async def process_milestone_amount(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
        
    amount = int(message.text)
    data = await state.get_data()
    remaining = data['remaining_amount']
    
    if amount <= 0 or amount > remaining:
        return await message.answer(f"❌ مبلغ باید بزرگتر از صفر و حداکثر معادل باقی‌مانده (<b>{remaining:,} تومان</b>) باشد. مجدداً وارد کنید:", parse_mode="HTML")
        
    await state.update_data(temp_amount=amount)
    await state.set_state(MilestoneCreation.waiting_for_title)
    
    phase_num = len(data['milestones']) + 1
    await message.answer(f"📝 لطفاً **عنوان فاز {phase_num}** را وارد کنید (مثال: طراحی قالب):")

@router.message(MilestoneCreation.waiting_for_title)
async def process_milestone_title(message: Message, state: FSMContext, db_user, bot: Bot):
    title = message.text.strip()
    if len(title) < 3:
        return await message.answer("⚠️ عنوان فاز خیلی کوتاه است. مجدداً وارد کنید:")
        
    data = await state.get_data()
    milestones = data['milestones']
    temp_amount = data['temp_amount']
    remaining = data['remaining_amount']
    total_amount = data['total_amount']
    project_id = data['project_id']
    bid_id = data['bid_id']
    
    milestones.append({'title': title, 'amount': temp_amount})
    new_remaining = remaining - temp_amount
    
    if new_remaining > 0:
        await state.update_data(remaining_amount=new_remaining, milestones=milestones)
        
        builder = InlineKeyboardBuilder()
        builder.button(text="✅ اختصاص کل باقی‌مانده به فاز بعدی", callback_data=f"ms_finish_remaining")
        builder.button(text="➕ تعریف فاز جدید", callback_data=f"ms_add_next")
        builder.adjust(1)
        
        await message.answer(
            f"📊 <b>فاز {len(milestones)} با موفقیت ثبت شد:</b>\n"
            f"• عنوان: {title}\n"
            f"• مبلغ: {temp_amount:,} تومان\n\n"
            f"💸 مبلغ کل پروژه: <b>{total_amount:,} تومان</b>\n"
            f"📉 مبلغ باقی‌مانده: <b>{new_remaining:,} تومان</b>\n\n"
            f"چگونه ادامه می‌دهید؟",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
    else:
        await state.clear()
        await execute_milestone_hire(message, db_user, bot, bid_id, project_id, milestones)

async def execute_milestone_hire(message_or_callback, db_user, bot: Bot, bid_id: int, project_id: int, milestones: list):
    bid = await db.get_bid(bid_id)
    project = await db.get_project(project_id)
    if not bid or not project:
        return
        
    client_id = message_or_callback.from_user.id
    success = await db.accept_bid(bid_id, project_id, bid['freelancer_id'], client_id, bid['amount'], milestones_list=milestones)
    
    if not success:
        if isinstance(message_or_callback, CallbackQuery):
            await message_or_callback.answer("عملیات ناموفق بود.", show_alert=True)
        else:
            await message_or_callback.answer("عملیات ناموفق بود.")
        return
        
    milestone_rows = ""
    for m in milestones:
        milestone_rows += f"<tr><td>{m['title']}</td><td>{m['amount']:,} تومان</td></tr>"
        
    html_content = (
        f"<h1>🎉 پروژه «{project['title']}» آغاز شد</h1>"
        "<hr/>"
        "<h3>✅ پیشنهاد فریلنسر با موفقیت پذیرفته شد!</h3>"
        f"<p>مبلغ کل <b>{bid['amount']:,} تومان</b> از موجودی حساب شما کسر و جهت پرداخت امن در صندوق Escrow پلتفرم بلوکه گردید.</p>"
        "<br/>"
        "<h3>📋 فازهای پرداختی تعریف‌شده (Milestones):</h3>"
        "<table bordered striped>"
        "  <tr><th>عنوان فاز</th><th>مبلغ</th></tr>"
        f"  {milestone_rows}"
        "</table>"
    )
    
    rich_msg = InputRichMessage(html=html_content)
    is_admin = message_or_callback.from_user.id in __import__('config').ADMIN_IDS
    kb = reply.client_main_menu(is_admin=is_admin)
    
    if isinstance(message_or_callback, CallbackQuery):
        try: await message_or_callback.message.delete()
        except: pass
        await bot.send_rich_message(
            chat_id=client_id,
            rich_message=rich_msg,
            reply_markup=kb
        )
    else:
        await bot.send_rich_message(
            chat_id=client_id,
            rich_message=rich_msg,
            reply_markup=kb
        )
        
    client_name = db_user['full_name'] or "کارفرما"
    try:
        fl_html = (
            f"<h1>🎉 پروژه «{project['title']}» آغاز شد</h1>"
            "<hr/>"
            "<h3>💎 تبریک! پیشنهاد شما پذیرفته شد و پروژه آغاز گردید.</h3>"
            "<br/>"
            "<table bordered>"
            f"  <tr><th>👤 کارفرما</th><td>{client_name}</td></tr>"
            f"  <tr><th>💰 مبلغ کل پروژه</th><td>{bid['amount']:,} تومان</td></tr>"
            "</table>"
            "<br/>"
            "<p>این پروژه به صورت <b>پرداخت چندمرحله‌ای (Milestones)</b> فازبندی شده است. فازها پس از تایید توسط کارفرما بررسی و آزاد خواهند شد.</p>"
        )
        await bot.send_rich_message(
            chat_id=bid['freelancer_id'],
            rich_message=InputRichMessage(html=fl_html),
            reply_markup=inline.freelancer_accepted_project_keyboard(project['project_id'], 'in_progress')
        )
    except Exception as e:
        import logging
        logging.getLogger(__name__).error(f"Error sending rich message to freelancer: {e}")

async def ms_add_next_handler(callback: CallbackQuery, state: FSMContext):
    await state.set_state(MilestoneCreation.waiting_for_amount)
    data = await state.get_data()
    remaining = data['remaining_amount']
    
    await callback.message.answer(
        f"📑 لطفاً مبلغ فاز بعدی را وارد کنید (حداکثر <b>{remaining:,} تومان</b>):",
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data == "ms_finish_remaining")
async def ms_finish_remaining_handler(callback: CallbackQuery, state: FSMContext, db_user, bot: Bot):
    data = await state.get_data()
    milestones = data['milestones']
    remaining = data['remaining_amount']
    bid_id = data['bid_id']
    project_id = data['project_id']
    
    milestones.append({'title': 'پیاده‌سازی نهایی و تحویل پروژه', 'amount': remaining})
    
    await state.clear()
    await execute_milestone_hire(callback, db_user, bot, bid_id, project_id, milestones)
    await callback.answer()

@router.callback_query(F.data.startswith("client_milestones_"))
async def client_milestones_handler(callback: CallbackQuery):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
        
    milestones = await db.get_project_milestones(project_id)
    
    status_map = {
        'held': '🔴 بلوکه شده',
        'released': '✅ پرداخت شده',
        'refunded': '↩️ مسترد شده'
    }
    
    milestone_rows = ""
    for m in milestones:
        status_str = status_map.get(m['status'], m['status'])
        milestone_rows += f"<tr><td>{m['title']}</td><td>{m['amount']:,} تومان</td><td>{status_str}</td></tr>"
        
    html_content = (
        f"<h1>💰 فازهای پرداخت امن پروژه</h1>"
        f"<h3>📌 {project['title']}</h3>"
        "<hr/>"
        "<table bordered striped>"
        "  <thead>"
        "    <tr>"
        "      <th>عنوان فاز</th>"
        "      <th>مبلغ</th>"
        "      <th>وضعیت</th>"
        "    </tr>"
        "  </thead>"
        "  <tbody>"
        f"    {milestone_rows}"
        "  </tbody>"
        "</table>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    builder = InlineKeyboardBuilder()
    for m in milestones:
        if m['status'] == 'held':
            builder.button(
                text=f"✅ آزادسازی: {m['title'][:15]}",
                callback_data=f"cl_release_ms_{m['milestone_id']}_{project_id}"
            )
            
    builder.button(text="🔙 بازگشت به مدیریت پروژه", callback_data=f"client_project_{project_id}_0", style="danger")
    builder.adjust(1)
    
    await callback.message.edit_text(text=None, reply_markup=builder.as_markup(), rich_message=rich_msg)
    await callback.answer()

@router.callback_query(F.data.startswith("cl_release_ms_"))
async def client_release_ms_handler(callback: CallbackQuery, bot: Bot):
    parts = callback.data.split("_")
    milestone_id = int(parts[3])
    project_id = int(parts[4])
    
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
        
    fl_id = project['chosen_freelancer_id']
    comm_rate = await db.get_user_commission_rate(fl_id) if fl_id else 0.10
    
    success = await db.release_milestone(milestone_id, comm_rate)
    if not success:
        return await callback.answer("خطا در آزادسازی وجه فاز.", show_alert=True)
        
    await callback.answer("✅ وجه فاز با موفقیت به فریلنسر آزاد شد!", show_alert=True)
    
    try:
        async with aiosqlite.connect(db.DB_PATH) as conn:
            conn.row_factory = aiosqlite.Row
            async with conn.execute('SELECT * FROM milestones WHERE milestone_id = ?', (milestone_id,)) as cursor:
                m_row = await cursor.fetchone()
        if m_row:
            m_title = m_row['title']
            m_amount = m_row['amount']
            payout = m_amount - int(m_amount * comm_rate)
            commission = int(m_amount * comm_rate)
            commission_pct = int(comm_rate * 100)
            await bot.send_message(
                fl_id,
                f"🔔 <b>کارفرمای گرامی وجه فاز پروژه را آزاد کرد!</b>\n\n"
                f"📌 پروژه: <b>{project['title']}</b>\n"
                f"📑 فاز: <b>{m_title}</b>\n"
                f"💰 مبلغ دریافتی (پس از کسر کمیسیون): <b>{payout:,} تومان</b>\n\n"
                f"مبلغ به موجودی شما افزوده شد.",
                parse_mode="HTML"
            )
            
            # Send milestone invoice PDF
            from handlers.common import send_invoice_to_parties
            client_user = await db.get_user(project['client_id'])
            freelancer_user = await db.get_user(fl_id)
            cl_name = (client_user['full_name'] or client_user['username'] or str(project['client_id'])) if client_user else str(project['client_id'])
            fl_name = (freelancer_user['full_name'] or freelancer_user['username'] or str(fl_id)) if freelancer_user else str(fl_id)
            
            await send_invoice_to_parties(
                bot=bot,
                project_id=project_id,
                project_title=project['title'],
                client_id=project['client_id'],
                client_name=cl_name,
                freelancer_id=fl_id,
                freelancer_name=fl_name,
                amount=m_amount,
                commission=commission,
                payout=payout,
                commission_pct=commission_pct,
                is_refund=False,
                milestone_title=m_title,
                milestone_id=milestone_id,
            )
    except Exception as e:
        print(f"Error notifying freelancer: {e}")
        
    updated_project = await db.get_project(project_id)
    if updated_project and updated_project['status'] == 'completed':
        # Award gamification rewards
        try:
            bonus, badge = await db.award_gamification_rewards(fl_id)
            if bonus > 0:
                await bot.send_message(
                    fl_id,
                    f"🏆 <b>تبریک فراوان فریلنسر گرامی! شما دستاورد جدیدی کسب کردید!</b>\n\n"
                    f"🏅 نشان کسب‌شده: <b>{badge}</b>\n"
                    f"🎁 هدیه شما: <b>{bonus} کانکت (بید) اضافی</b> به موجودی شما افزوده شد.\n\n"
                    f"موفقیت‌های بیشتر شما آرزوی ماست! 🚀",
                    parse_mode="HTML"
                )
        except Exception as e:
            print(f"Error awarding gamification rewards in milestone release: {e}")
            
        await callback.message.edit_text(
            f"🎉 <b>تمامی فازهای پروژه «{project['title']}» با موفقیت آزاد و پرداخت گردید.</b>\n\n"
            f"پروژه با موفقیت به پایان رسید. لطفاً نظر و امتیاز خود را ثبت کنید:",
            reply_markup=inline.stars_rating_keyboard(project_id),
            parse_mode="HTML"
        )
    else:
        await client_milestones_handler(callback)

    # Notify freelancer
    client_name = db_user['full_name'] or "کارفرما"
    try:
        await bot.send_message(
            bid['freelancer_id'],
            texts.bid_accepted_notification(project['title'], client_name, bid['amount']) if project_type != 'full_time' else (
                f"🎉 <b>تبریک! پیشنهاد همکاری شما در پروژه تمام‌وقت پذیرفته شد!</b>\n\n"
                f"📌 پروژه: <b>{project['title']}</b>\n"
                f"👤 کارفرما: <b>{client_name}</b>\n\n"
                f"این پروژه استخدامی با موفقیت آغاز شده است. لطفاً از دکمه‌های زیر جهت گفتگو با کارفرما استفاده کنید:"
            ),
            reply_markup=inline.freelancer_accepted_project_keyboard(project['project_id'], 'in_progress'),
            parse_mode="HTML"
        )
    except Exception:
        pass
    await callback.answer("✅ پیشنهاد پذیرفته شد!")

async def complete_project(callback: CallbackQuery, bot: Bot, db_user, state: FSMContext):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project or project['status'] not in ('in_progress', 'delivered'):
        return await callback.answer("این پروژه قابل تکمیل نیست.", show_alert=True)

    # Get accepted bid
    bids = await db.get_bids_for_project(project_id)
    accepted_bid = next((b for b in bids if b['status'] == 'accepted'), None)
    if not accepted_bid:
        return await callback.answer("پیشنهاد پذیرفته‌شده‌ای یافت نشد.", show_alert=True)

    project_type = project.get('project_type', 'project_based')

    if project_type == 'full_time':
        await db.complete_fulltime_project(project_id)
        
        # Award gamification rewards
        try:
            bonus, badge = await db.award_gamification_rewards(accepted_bid['freelancer_id'])
            if bonus > 0:
                await bot.send_message(
                    accepted_bid['freelancer_id'],
                    f"🏆 <b>تبریک فراوان فریلنسر گرامی! شما دستاورد جدیدی کسب کردید!</b>\n\n"
                    f"🏅 نشان کسب‌شده: <b>{badge}</b>\n"
                    f"🎁 هدیه شما: <b>{bonus} کانکت (بید) اضافی</b> به موجودی شما افزوده شد.\n\n"
                    f"موفقیت‌های بیشتر شما آرزوی ماست! 🚀",
                    parse_mode="HTML"
                )
        except Exception as e:
            logger.error(f"Error awarding gamification rewards: {e}")

        # Notify freelancer
        try:
            from aiogram.utils.keyboard import InlineKeyboardBuilder
            builder = InlineKeyboardBuilder()
            builder.button(text="⭐ ثبت امتیاز برای کارفرما", callback_data=f"rate_client_start_{project_id}")
            
            await bot.send_message(
                accepted_bid['freelancer_id'],
                f"🎉 <b>پروژه استخدامی با موفقیت به پایان رسید!</b>\n\n"
                f"📌 پروژه: <b>{project['title']}</b>\n\n"
                f"همکاری استخدامی شما در این پروژه توسط کارفرما خاتمه یافته و وضعیت پروژه به تکمیل‌شده تغییر یافت. با تشکر از فعالیت شما.",
                reply_markup=builder.as_markup(),
                parse_mode="HTML",
                message_effect_id="5046509860389126442"
            )
        except Exception:
            pass

        # Save details to state for rating flow
        await state.update_data(
            project_id=project_id,
            freelancer_id=accepted_bid['freelancer_id'],
            payout=0,
            commission=0,
            commission_pct=0
        )
        await state.set_state(ClientRating.waiting_for_stars)

        text = (
            f"🎉 <b>پروژه استخدام با موفقیت خاتمه یافت!</b>\n\n"
            f"⭐ <b>ثبت امتیاز فریلنسر:</b>\n"
            f"لطفاً بر اساس همکاری انجام شده، به فریلنسر امتیاز دهید:"
        )
    else:
        commission_rate = await db.get_user_commission_rate(accepted_bid['freelancer_id'])
        commission_pct = int(commission_rate * 100)
        commission = int(accepted_bid['amount'] * commission_rate)
        payout = accepted_bid['amount'] - commission

        await db.complete_project(
            project_id=project_id,
            freelancer_id=accepted_bid['freelancer_id'],
            amount=accepted_bid['amount'],
            commission_rate=commission_rate,
            client_id=callback.from_user.id,
        )

        # Award gamification rewards
        try:
            bonus, badge = await db.award_gamification_rewards(accepted_bid['freelancer_id'])
            if bonus > 0:
                await bot.send_message(
                    accepted_bid['freelancer_id'],
                    f"🏆 <b>تبریک فراوان فریلنسر گرامی! شما دستاورد جدیدی کسب کردید!</b>\n\n"
                    f"🏅 نشان کسب‌شده: <b>{badge}</b>\n"
                    f"🎁 هدیه شما: <b>{bonus} کانکت (بید) اضافی</b> به موجودی شما افزوده شد.\n\n"
                    f"موفقیت‌های بیشتر شما آرزوی ماست! 🚀",
                    parse_mode="HTML"
                )
        except Exception as e:
            logger.error(f"Error awarding gamification rewards: {e}")

        # Notify freelancer
        try:
            from aiogram.utils.keyboard import InlineKeyboardBuilder
            builder = InlineKeyboardBuilder()
            builder.button(text="💰 مشاهده کیف پول", callback_data="wallet_menu_back_freelancer")
            builder.button(text="⭐ ثبت امتیاز برای کارفرما", callback_data=f"rate_client_start_{project_id}")
            builder.adjust(1)
            await bot.send_message(
                accepted_bid['freelancer_id'],
                (f"🎉 <b>پروژه با موفقیت تأیید و تسویه شد!</b>\n\n"
                 f"📌 پروژه: <b>{project['title']}</b>\n"
                 f"💰 مبلغ واریز شده: <b>{payout:,} تومان</b> (پس از کسر کمیسیون)\n\n"
                 f"مبلغ فوق به موجودی کیف پول شما افزوده شد. برای انتقال یا برداشت وجه از دکمه زیر استفاده کنید:"),
                reply_markup=builder.as_markup(),
                parse_mode="HTML",
                message_effect_id="5104841245755180586"
            )
        except Exception:
            pass

        # Save details to state for rating flow
        await state.update_data(
            project_id=project_id,
            freelancer_id=accepted_bid['freelancer_id'],
            payout=payout,
            commission=commission,
            commission_pct=commission_pct
        )
        await state.set_state(ClientRating.waiting_for_stars)

        text = (
            f"🎉 <b>پروژه با موفقیت تکمیل و تسویه شد!</b>\n\n"
            f"💰 مبلغ پرداختی به فریلنسر: <b>{payout:,} تومان</b>\n"
            f"📊 کمیسیون پلتفرم ({commission_pct}٪): <b>{commission:,} تومان</b>\n\n"
            f"⭐ <b>ثبت امتیاز فریلنسر:</b>\n"
            f"لطفاً بر اساس کیفیت کار تحویل شده، به فریلنسر امتیاز دهید:"
        )
        
    await callback.message.edit_text(text, reply_markup=inline.stars_rating_keyboard(project_id), parse_mode="HTML")
    await callback.answer("✅ پروژه تسویه شد!")

@router.callback_query(F.data.startswith("rate_"))
async def process_stars_rating(callback: CallbackQuery, state: FSMContext):
    parts = callback.data.split("_")
    stars = int(parts[1])
    project_id = int(parts[2])

    await state.update_data(rating=stars)
    await state.set_state(ClientRating.waiting_for_review)

    await callback.message.edit_text(
        f"⭐ شما امتیاز <b>{stars} ستاره</b> را انتخاب کردید.\n\n"
        f"📄 لطفا نظر یا توضیحات خود را درباره همکاری با این فریلنسر بنویسید (اختیاری):",
        reply_markup=inline.skip_rating_review_keyboard(project_id),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data.startswith("skip_review_"))
async def skip_review(callback: CallbackQuery, state: FSMContext, db_user, bot: Bot):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    freelancer_id = data.get('freelancer_id')
    await state.clear()

    await db.submit_rating(project_id, rating, "")
    
    role = db_user['role'] or 'client'
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="🔙 بازگشت به منوی اصلی", callback_data=f"role_{role}", style="success")

    await callback.message.edit_text(
        "✅ <b>امتیاز شما با موفقیت و بدون نظر متنی ثبت شد.</b>\nممنون از همکاری شما!",
        reply_markup=builder.as_markup(),
        parse_mode="HTML"
    )
    await callback.answer()
    
    # Notify freelancer
    if freelancer_id:
        try:
            project = await db.get_project(project_id)
            project_title = project['title'] if project else f"#{project_id}"
            await bot.send_message(
                freelancer_id,
                f"⭐️ <b>امتیاز جدید ثبت شد!</b>\n\n"
                f"کارفرما برای پروژه «{project_title}» امتیاز <b>{rating} ستاره</b> را برای شما ثبت کرد.",
                parse_mode="HTML"
            )
        except Exception:
            pass

@router.message(ClientRating.waiting_for_review)
async def process_review_text(message: Message, state: FSMContext, db_user, bot: Bot):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    freelancer_id = data.get('freelancer_id')
    await state.clear()

    try: await message.react([ReactionTypeEmoji(emoji="⭐")])
    except: pass
    await db.submit_rating(project_id, rating, message.text)
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'client'
    kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)

    await message.answer(
        "✅ <b>امتیاز و نظر ارزشمند شما با موفقیت ثبت شد.</b>\nممنون از همکاری شما!",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014"
    )
    
    # Notify freelancer
    if freelancer_id:
        try:
            project = await db.get_project(project_id)
            project_title = project['title'] if project else f"#{project_id}"
            await bot.send_message(
                freelancer_id,
                f"⭐️ <b>بازخورد جدید از کارفرما دریافت شد!</b>\n"
                f"\n"
                f"📌 پروژه: <b>{project_title}</b>\n"
                f"🏅 امتیاز: <b>{rating} ستاره</b> {'⭐' * rating}\n"
                f"💬 نظر کارفرما:\n<blockquote>«{message.text}»</blockquote>\n"
                f"\n"
                f"این بازخورد در پروفایل شما ثبت گردید.",
                parse_mode="HTML"
            )
        except Exception:
            pass

@router.callback_query(F.data.startswith("revision_project_"))
async def request_revision(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project or project['status'] != 'delivered':
        return await callback.answer("این پروژه در وضعیت تحویل شده نیست.", show_alert=True)

    await db.request_project_revisions(project_id)

    # Get accepted bid to notify freelancer
    bids = await db.get_bids_for_project(project_id)
    accepted_bid = next((b for b in bids if b['status'] == 'accepted'), None)
    if accepted_bid:
        try:
            await bot.send_message(
                accepted_bid['freelancer_id'],
                f"⚠️ <b>کارفرما برای پروژه «{project['title']}» درخواست اصلاحات داده است.</b>\n\n"
                f"لطفاً جزئیات را هماهنگ کرده و پس از اعمال تغییرات، کار را مجدداً ارسال کنید.",
                parse_mode="HTML"
            )
        except Exception:
            pass

    await callback.message.edit_text("✅ درخواست اصلاحات به فریلنسر ارسال شد و وضعیت پروژه به «در حال انجام» بازگشت.")
    await callback.answer("اصلاحات درخواست شد.")

# ── Wallet ──────────────────────────────────────────────────────────────

async def refresh_wallet_ui(message_or_callback, user_id: int):
    user = await db.get_user(user_id)
    role = user['role'] if user else 'client'
    balance = user['balance'] or 0
    txs = await db.get_transactions(user_id, limit=5)
    
    tx_table = "<table bordered striped><tr><th>توضیحات تراکنش</th><th>مبلغ (تومان)</th></tr>"
    for tx in txs:
        sign = "+" if tx['amount'] > 0 else ""
        formatted_amount = f"{sign}{tx['amount']:,}"
        tx_table += f"<tr><td>{tx['description']}</td><td>{formatted_amount}</td></tr>"
    tx_table += "</table>"
    if not txs:
        tx_table = "<p>📭 <i>شما هنوز هیچ تراکنشی ثبت نکرده‌اید.</i></p>"
        
    html_content = (
        "<h1>💰 کیف پول اختصاصی شما | ایکس‌لنسر</h1>"
        "<hr/>"
        f"<h3>💵 موجودی فعال: <b>{balance:,} تومان</b></h3>"
        "<br/>"
        "<h3>📋 تاریخچه آخرین تراکنش‌ها:</h3>"
        f"{tx_table}"
    )
    
    rich_msg = InputRichMessage(html=html_content)
    bot = message_or_callback.bot
    
    if isinstance(message_or_callback, CallbackQuery):
        await message_or_callback.message.edit_text(text=None, reply_markup=inline.wallet_keyboard(role), rich_message=rich_msg)
    else:
        await bot.send_rich_message(
            chat_id=user_id,
            rich_message=rich_msg,
            reply_markup=inline.wallet_keyboard(role)
        )

@router.message(F.text == texts.BTN_WALLET)
async def show_wallet(message: Message, db_user):
    await refresh_wallet_ui(message, message.from_user.id)



@router.callback_query(F.data == "wallet_redeem_promo")
async def start_redeem_promo(callback: CallbackQuery, state: FSMContext):
    await state.set_state(PromoRedeem.waiting_for_code)
    await callback.message.answer(
        "🎁 <b>ثبت کد هدیه / تخفیف</b>\n\nلطفاً کد هدیه خود را وارد کنید:",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(PromoRedeem.waiting_for_code)
async def process_redeem_promo_code(message: Message, state: FSMContext, db_user):
    code = message.text.upper().strip()
    await state.clear()
    
    success, msg = await db.redeem_promo_code(message.from_user.id, code)
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'client'
    kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)
    
    if success:
        try: await message.react([ReactionTypeEmoji(emoji="🎁")])
        except: pass
        await message.answer(msg, reply_markup=kb, parse_mode="HTML", message_effect_id="5046509860389126442")
    else:
        try: await message.react([ReactionTypeEmoji(emoji="❌")])
        except: pass
        await message.answer(msg, reply_markup=kb, parse_mode="HTML")
        
    await refresh_wallet_ui(message, message.from_user.id)

@router.callback_query(F.data == "wallet_deposit_custom")
async def start_deposit_custom(callback: CallbackQuery, state: FSMContext):
    await state.set_state(WalletDeposit.waiting_for_amount)
    await callback.message.answer(
        "💵 <b>لطفاً مبلغ مورد نظر خود را به تومان وارد کنید:</b>",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(WalletDeposit.waiting_for_amount)
async def process_deposit_amount(message: Message, state: FSMContext, db_user):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    amount = int(message.text)
    min_deposit = int(await db.get_setting('min_wallet_deposit', '10000'))
    if amount < min_deposit:
        return await message.answer(f"❌ حداقل مبلغ شارژ کیف پول {min_deposit:,} تومان می‌باشد.")
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    
    card_active = (await db.get_setting('card_to_card_enabled', '1')) == '1'
    zarinpal_active = (await db.get_setting('zarinpal_enabled', '1')) == '1'
    zibal_active = (await db.get_setting('zibal_enabled', '1')) == '1'
    
    if not (card_active or zarinpal_active or zibal_active):
        await state.clear()
        return await message.answer("❌ در حال حاضر هیچ روش پرداختی فعال نیست. لطفاً بعداً تلاش کنید یا با پشتیبانی تماس بگیرید.")
        
    await state.clear()
    
    msg = (
        f"💵 <b>درخواست شارژ حساب کاربری</b>\n\n"
        f"مبلغ درخواستی: <b>{amount:,} تومان</b>\n\n"
        f"لطفاً روش پرداخت مورد نظر خود را انتخاب کنید:"
    )
    
    await message.answer(
        msg,
        reply_markup=inline.deposit_methods_keyboard(amount, card_active, zarinpal_active, zibal_active),
        parse_mode="HTML"
    )

@router.message(WalletDeposit.waiting_for_receipt)
async def process_deposit_receipt(message: Message, state: FSMContext, bot: Bot, db_user):
    if not message.photo:
        return await message.answer("❌ لطفاً تصویر رسید یا فیش واریزی خود را ارسال کنید (ارسال عکس).")
        
    photo_id = message.photo[-1].file_id
    data = await state.get_data()
    amount = data.get("deposit_amount", 0)
    
    deposit_id = await db.create_deposit_request(message.from_user.id, amount, photo_id)
    await state.clear()
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] if db_user else 'freelancer'
    kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)
    
    try:
        await message.react([ReactionTypeEmoji(emoji="👍")])
    except:
        pass
        
    await message.answer(
        "✅ <b>رسید پرداخت شما دریافت شد!</b>\n\nدرخواست شارژ شما با موفقیت برای مدیریت ارسال گردید. به محض بررسی و تأیید رسید توسط بخش مالی، کیف پول شما شارژ خواهد شد.",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014"
    )
    
    # Notify admins
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="✅ تایید فیش و شارژ", callback_data=f"admin_deposit_approve_{deposit_id}", style="success")
    builder.button(text="❌ رد فیش", callback_data=f"admin_deposit_reject_{deposit_id}", style="danger")
    builder.adjust(1)
    admin_kb = builder.as_markup()
    
    admin_msg = (
        f"💳 <b>درخواست شارژ حساب کاربری</b>\n\n"
        f"👤 کاربر: <a href='tg://user?id={message.from_user.id}'>{message.from_user.full_name}</a> (<code>{message.from_user.id}</code>)\n"
        f"💰 مبلغ درخواستی: <b>{amount:,} تومان</b>\n"
        f"🆔 شناسه درخواست: <code>#{deposit_id}</code>"
    )
    
    recipients = await db.get_admin_recipients('payments')
    for admin_id in recipients:
        try:
            await bot.send_photo(admin_id, photo_id, caption=admin_msg, reply_markup=admin_kb, parse_mode="HTML")
        except Exception:
            pass

@router.callback_query(F.data.startswith("deposit_method_card_"))
async def process_card_deposit_select(callback: CallbackQuery, state: FSMContext):
    try:
        amount = int(callback.data.split("_")[-1])
    except (ValueError, IndexError):
        return await callback.answer("❌ خطا در خواندن مبلغ پرداخت.", show_alert=True)
        
    await state.update_data(deposit_amount=amount)
    await state.set_state(WalletDeposit.waiting_for_receipt)
    
    admin_card = await db.get_setting('admin_card') or "۶۲۱۹-۸۶۱۰-۱۲۳۴-۵۶۷۸"
    admin_card_name = await db.get_setting('admin_card_name') or "پشتیبانی ایکس‌لنسر"
    
    msg = (
        f"💳 <b>اطلاعات پرداخت کارت‌به‌کارت</b>\n\n"
        f"لطفاً مبلغ <b>{amount:,} تومان</b> را به شماره کارت زیر واریز کنید:\n\n"
        f"💳 <code>{admin_card}</code>\n"
        f"👤 به نام: <b>{admin_card_name}</b>\n\n"
        f"📸 پس از واریز، <b>عکس رسید یا اسکرین‌شات فیش پرداخت</b> را در همین چت ارسال کنید تا مورد بررسی مدیریت قرار گیرد."
    )
    
    try:
        await callback.message.react([ReactionTypeEmoji(emoji="💳")])
    except:
        pass
        
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="🔙 انصراف", callback_data="wallet_menu_back", style="danger")
    
    await callback.message.edit_text(msg, parse_mode="HTML", reply_markup=builder.as_markup())
    await callback.answer()

async def show_dynamic_payment_options(callback: CallbackQuery, required_amount: int, balance: int, action_type: str, action_meta: str, back_callback: str):
    remaining = required_amount - balance
    
    text = (
        f"👛 <b>کسر از کیف پول یا پرداخت مستقیم؟</b>\n\n"
        f"💰 مبلغ مورد نیاز: <b>{required_amount:,} تومان</b>\n"
        f"💵 موجودی کیف پول شما: <b>{balance:,} تومان</b>\n\n"
    )
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    
    if balance > 0:
        text += (
            f"شما می‌توانید مبلغ <b>{balance:,} تومان</b> را از کیف پول خود کسر کرده و مابقی (<b>{remaining:,} تومان</b>) را آنلاین پرداخت کنید، "
            f"یا کل مبلغ را مستقیماً بپردازید:"
        )
        builder.button(
            text=f"👛 کسر از کیف پول + پرداخت {remaining:,} تومان",
            callback_data=f"pay_opt_partial_{remaining}_{action_type}_{action_meta}"
        )
    else:
        text += "جهت انجام این تراکنش، لطفاً کل مبلغ را به صورت مستقیم آنلاین پرداخت کنید:"
        
    builder.button(
        text=f"💳 پرداخت مستقیم {required_amount:,} تومان",
        callback_data=f"pay_opt_direct_{required_amount}_{action_type}_{action_meta}"
    )
    
    builder.button(text="🔙 بازگشت", callback_data=back_callback, style="danger")
    builder.adjust(1)
    
    await callback.message.edit_text(text, reply_markup=builder.as_markup(), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("pay_opt_"))
async def process_pay_option_select(callback: CallbackQuery):
    parts = callback.data.split("_")
    pay_type = parts[2]
    pay_amount = int(parts[3])
    action_type = parts[4]
    action_meta = parts[5]
    
    card_active = (await db.get_setting('card_to_card_enabled', '1')) == '1'
    zarinpal_active = (await db.get_setting('zarinpal_enabled', '1')) == '1'
    zibal_active = (await db.get_setting('zibal_enabled', '1')) == '1'
    
    if not (card_active or zarinpal_active or zibal_active):
        return await callback.answer("❌ در حال حاضر هیچ درگاهی فعال نیست.", show_alert=True)
        
    msg = (
        f"💵 <b>انتخاب روش پرداخت</b>\n\n"
        f"مبلغ پرداخت: <b>{pay_amount:,} تومان</b>\n\n"
        f"لطفاً یکی از روش‌های زیر را جهت پرداخت انتخاب کنید:"
    )
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    if zarinpal_active:
        builder.button(text="💎 درگاه آنلاین زرین‌پال", callback_data=f"deposit_method_zarinpal_{pay_amount}_{action_type}_{action_meta}")
    if zibal_active:
        builder.button(text="🌀 درگاه آنلاین زیبال", callback_data=f"deposit_method_zibal_{pay_amount}_{action_type}_{action_meta}")
    if card_active:
        builder.button(text="💳 کارت به کارت (دستی)", callback_data=f"deposit_method_card_{pay_amount}")
        
    builder.button(text="🔙 انصراف", callback_data="wallet_menu_back", style="danger")
    builder.adjust(1)
    
    await callback.message.edit_text(msg, reply_markup=builder.as_markup(), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("deposit_method_zarinpal_"))
async def process_zarinpal_deposit_select(callback: CallbackQuery):
    parts = callback.data.split("_")
    try:
        amount = int(parts[3])
    except (ValueError, IndexError):
        return await callback.answer("❌ خطا در خواندن مبلغ پرداخت.", show_alert=True)
        
    action_type = parts[4] if len(parts) > 4 else ""
    action_meta = parts[5] if len(parts) > 5 else ""

    user_id = callback.from_user.id
    
    merchant = await db.get_setting('zarinpal_merchant', 'zarinpal')
    sandbox = (await db.get_setting('zarinpal_sandbox', '1')) == '1'
    callback_domain = await db.get_setting('payment_callback_domain', 'http://127.0.0.1:8000')
    
    callback_url = f"{callback_domain}/payment/verify/zarinpal?user_id={user_id}&amount={amount}"
    if action_type:
        callback_url += f"&action_type={action_type}&action_meta={action_meta}"
        
    await callback.message.edit_text("⏳ در حال ایجاد لینک پرداخت زرین‌پال...")
    
    from utils.payment import zarinpal_request
    authority = await zarinpal_request(
        merchant_id=merchant,
        amount_tomans=amount,
        description=f"شارژ کیف پول ایکس‌لنسر کاربر #{user_id}",
        callback_url=callback_url,
        sandbox=sandbox
    )
    
    if not authority:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        builder.button(text="🔙 بازگشت به کیف پول", callback_data="wallet_menu_back", style="danger")
        return await callback.message.edit_text(
            "❌ خطا در ایجاد لینک پرداخت زرین‌پال. لطفاً مجدداً تلاش کنید یا روش دیگری را انتخاب کنید.",
            reply_markup=builder.as_markup()
        )
        
    await db.create_online_payment(authority, user_id, amount, 'zarinpal')
    
    payment_url = f"https://sandbox.zarinpal.com/pg/StartPay/{authority}" if sandbox else f"https://www.zarinpal.com/pg/StartPay/{authority}"
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="💳 پرداخت آنلاین زرین‌پال", url=payment_url)
    verify_callback = f"online_verify_zarinpal_{authority}_{amount}"
    if action_type:
        verify_callback += f"_{action_type}_{action_meta}"
    builder.button(text="🔍 بررسی وضعیت پرداخت", callback_data=verify_callback)
    builder.button(text="🔙 انصراف", callback_data="wallet_menu_back", style="danger")
    builder.adjust(1)
    
    msg = (
        f"💎 <b>پرداخت آنلاین زرین‌پال</b>\n\n"
        f"مبلغ شارژ: <b>{amount:,} تومان</b>\n\n"
        f"برای پرداخت روی دکمه زیر کلیک کنید. پس از اتمام پرداخت در مرورگر، به ربات برگشته و دکمه بررسی وضعیت را فشار دهید."
    )
    
    await callback.message.edit_text(msg, reply_markup=builder.as_markup(), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("deposit_method_zibal_"))
async def process_zibal_deposit_select(callback: CallbackQuery):
    parts = callback.data.split("_")
    try:
        amount = int(parts[3])
    except (ValueError, IndexError):
        return await callback.answer("❌ خطا در خواندن مبلغ پرداخت.", show_alert=True)
        
    action_type = parts[4] if len(parts) > 4 else ""
    action_meta = parts[5] if len(parts) > 5 else ""

    user_id = callback.from_user.id
    
    merchant = await db.get_setting('zibal_merchant', 'zibal')
    callback_domain = await db.get_setting('payment_callback_domain', 'http://127.0.0.1:8000')
    
    callback_url = f"{callback_domain}/payment/verify/zibal?user_id={user_id}&amount={amount}"
    if action_type:
        callback_url += f"&action_type={action_type}&action_meta={action_meta}"
        
    await callback.message.edit_text("⏳ در حال ایجاد لینک پرداخت زیبال...")
    
    from utils.payment import zibal_request
    track_id = await zibal_request(
        merchant_id=merchant,
        amount_tomans=amount,
        callback_url=callback_url,
        description=f"شارژ کیف پول ایکس‌لنسر کاربر #{user_id}"
    )
    
    if not track_id:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        builder.button(text="🔙 بازگشت به کیف پول", callback_data="wallet_menu_back", style="danger")
        return await callback.message.edit_text(
            "❌ خطا در ایجاد لینک پرداخت زیبال. لطفاً مجدداً تلاش کنید یا روش دیگری را انتخاب کنید.",
            reply_markup=builder.as_markup()
        )
        
    await db.create_online_payment(track_id, user_id, amount, 'zibal')
    
    payment_url = f"https://gateway.zibal.ir/start/{track_id}"
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="💳 پرداخت آنلاین زیبال", url=payment_url)
    verify_callback = f"online_verify_zibal_{track_id}_{amount}"
    if action_type:
        verify_callback += f"_{action_type}_{action_meta}"
    builder.button(text="🔍 بررسی وضعیت پرداخت", callback_data=verify_callback)
    builder.button(text="🔙 انصراف", callback_data="wallet_menu_back", style="danger")
    builder.adjust(1)
    
    msg = (
        f"🌀 <b>پرداخت آنلاین زیبال</b>\n\n"
        f"مبلغ شارژ: <b>{amount:,} تومان</b>\n\n"
        f"برای پرداخت روی دکمه زیر کلیک کنید. پس از اتمام پرداخت در مرورگر، به ربات برگشته و دکمه بررسی وضعیت را فشار دهید."
    )
    
    await callback.message.edit_text(msg, reply_markup=builder.as_markup(), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("online_verify_"))
async def process_online_verify_callback(callback: CallbackQuery, db_user, bot: Bot):
    try:
        parts = callback.data.split("_")
        gateway = parts[2]
        payment_id = parts[3]
        amount = int(parts[4])
    except (IndexError, ValueError):
        return await callback.answer("❌ خطا در پردازش اطلاعات تراکنش.", show_alert=True)
        
    action_type = parts[5] if len(parts) > 5 else ""
    action_meta = parts[6] if len(parts) > 6 else ""

    user_id = callback.from_user.id
    
    payment = await db.get_online_payment(payment_id)
    if not payment:
        return await callback.answer("تراکنش یافت نشد.", show_alert=True)
        
    if payment['status'] == 'completed':
        return await callback.answer("✅ این تراکنش قبلاً تأیید و شارژ شده است.", show_alert=True)
        
    await callback.message.edit_text("⏳ در حال استعلام وضعیت پرداخت...")
    
    success = False
    ref_id = ""
    error_msg = ""
    
    if gateway == 'zarinpal':
        merchant = await db.get_setting('zarinpal_merchant', 'zarinpal')
        sandbox = (await db.get_setting('zarinpal_sandbox', '1')) == '1'
        from utils.payment import zarinpal_verify
        success, ref_id = await zarinpal_verify(merchant, amount, payment_id, sandbox)
    elif gateway == 'zibal':
        merchant = await db.get_setting('zibal_merchant', 'zibal')
        from utils.payment import zibal_verify
        success, ref_id = await zibal_verify(merchant, payment_id)
        
    if success:
        await db.update_online_payment_status(payment_id, 'completed')
        await db.update_balance(user_id, amount, "deposit", f"شارژ آنلاین ({gateway}) - کد رهگیری {ref_id}")
        
        action_text = ""
        if action_type:
            from utils.web_server import execute_pending_action
            action_text = await execute_pending_action(bot, user_id, action_type, action_meta)
            
        is_admin = user_id in __import__('config').ADMIN_IDS
        role = db_user['role'] if db_user else 'freelancer'
        kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)
        
        if action_text:
            msg = (
                f"🎉 <b>پرداخت و اقدام مالی با موفقیت تایید شد!</b>\n\n"
                f"💰 مبلغ <b>{amount:,} تومان</b> به کیف پول شما افزوده شد.\n"
                f"🧾 کد رهگیری: <code>{ref_id}</code>\n\n"
                f"{action_text}"
            )
        else:
            msg = (
                f"🎉 <b>پرداخت شما با موفقیت تایید شد!</b>\n\n"
                f"💰 مبلغ <b>{amount:,} تومان</b> به کیف پول شما افزوده شد.\n"
                f"🧾 کد رهگیری: <code>{ref_id}</code>"
            )
            
        await callback.message.answer(msg, reply_markup=kb, parse_mode="HTML")
        await callback.message.delete()
        await callback.answer("🎉 پرداخت تایید شد!")
    else:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        payment_url = f"https://sandbox.zarinpal.com/pg/StartPay/{payment_id}" if (gateway == 'zarinpal' and (await db.get_setting('zarinpal_sandbox', '1')) == '1') else (f"https://www.zarinpal.com/pg/StartPay/{payment_id}" if gateway == 'zarinpal' else f"https://gateway.zibal.ir/start/{payment_id}")
        builder.button(text="💳 تلاش مجدد پرداخت", url=payment_url)
        builder.button(text="🔍 بررسی وضعیت مجدد", callback_data=callback.data)
        builder.button(text="🔙 انصراف", callback_data="wallet_menu_back", style="danger")
        builder.adjust(1)
        
        await callback.message.edit_text(
            f"❌ <b>تایید پرداخت ناموفق بود</b>\n\n"
            f"علت خطا: <code>{ref_id}</code>\n\n"
            f"اگر پرداخت را انجام داده‌اید چند لحظه صبر کرده و دکمه بررسی وضعیت را بزنید، یا در صورت تمایل تراکنش را تکرار کنید.",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
        await callback.answer("❌ پرداخت هنوز تایید نشده است.")

async def show_tx_history_page(callback: CallbackQuery, offset: int):
    user_id = callback.from_user.id
    limit = 10
    total = await db.count_user_transactions(user_id)
    txs = await db.get_user_transactions_paginated(user_id, limit=limit, offset=offset)
    
    total_pages = (total - 1) // limit + 1 if total > 0 else 1
    current_page = offset // limit + 1
    
    tx_rows = ""
    for i, tx in enumerate(txs, start=offset + 1):
        sign = "+" if tx['amount'] > 0 else ""
        tx_rows += f"<tr><td>{i}</td><td>{sign}{tx['amount']:,} تومان</td><td>{tx['description']}</td><td>{to_shamsi(tx['created_at'])}</td></tr>"
        
    html_content = (
        f"<h1>📜 تاریخچه کامل تراکنش‌های شما</h1>"
        f"<h3>صفحه {current_page} از {total_pages} (تعداد کل تراکنش‌ها: {total})</h3>"
        "<hr/>"
        "<table bordered striped>"
        "  <thead>"
        "    <tr>"
        "      <th>ردیف</th>"
        "      <th>مبلغ</th>"
        "      <th>توضیحات</th>"
        "      <th>تاریخ</th>"
        "    </tr>"
        "  </thead>"
        "  <tbody>"
        f"    {tx_rows if tx_rows else '<tr><td colspan=4>تراکنشی ثبت نشده است.</td></tr>'}"
        "  </tbody>"
        "</table>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.transactions_list_keyboard(offset=offset, total=total, limit=limit),
        rich_message=rich_msg
    )

@router.callback_query(F.data == "wallet_tx_history")
async def wallet_tx_history_handler(callback: CallbackQuery):
    await show_tx_history_page(callback, offset=0)
    await callback.answer()

@router.callback_query(F.data.startswith("tx_history_page_"))
async def tx_history_page_handler(callback: CallbackQuery):
    offset = int(callback.data.split("_")[-1])
    await show_tx_history_page(callback, offset=offset)
    await callback.answer()

@router.callback_query(F.data == "wallet_menu_back")
async def wallet_menu_back_handler(callback: CallbackQuery):
    await refresh_wallet_ui(callback, callback.from_user.id)
    await callback.answer()

# ── VIP ──────────────────────────────────────────────────────────────────

@router.message(F.text == texts.BTN_VIP)
async def show_vip(message: Message, db_user):
    user_id = message.from_user.id
    user = await db.get_user(user_id)
    vip_level = await db.check_vip_level(user_id)
    
    vip_names = {1: "برنزی 🥉", 2: "نقره‌ای 🥈", 3: "طلایی 🥇"}
    
    c1 = await db.get_setting('commission_bronze', '5')
    c2 = await db.get_setting('commission_silver', '2')
    c3 = await db.get_setting('commission_gold', '0')
    commissions = {1: f"{c1}٪", 2: f"{c2}٪", 3: f"{c3}٪"}
    
    p1 = int(await db.get_setting('vip_bronze_price', 50000))
    p2 = int(await db.get_setting('vip_silver_price', 100000))
    p3 = int(await db.get_setting('vip_gold_price', 200000))
    
    s_bronze = int(await db.get_setting('connects_monthly_bronze', '50'))
    s_silver = int(await db.get_setting('connects_monthly_silver', '100'))
    s_gold = int(await db.get_setting('connects_monthly_gold', '200'))
    
    if vip_level > 0 and user and user['vip_expiry']:
        from utils.date_utils import to_shamsi
        from datetime import datetime
        
        remaining_days = 0
        remaining_hours = 0
        shamsi_expiry = "نامشخص"
        try:
            expiry_dt = datetime.fromisoformat(user['vip_expiry'])
            now = datetime.now()
            if expiry_dt > now:
                diff = expiry_dt - now
                remaining_days = diff.days
                remaining_hours = diff.seconds // 3600
                shamsi_expiry = to_shamsi(expiry_dt)
        except Exception:
            pass
            
        current_allowance = {1: s_bronze, 2: s_silver, 3: s_gold}.get(vip_level, 0)
        html_content = (
            f"<h1>🌟 وضعیت اشتراک ویژه (VIP) شما</h1>"
            "<hr/>"
            "<table bordered>"
            f"  <tr><th>سطح اشتراک فعال</th><td><b>{vip_names.get(vip_level, 'VIP')}</b></td></tr>"
            f"  <tr><th>تاریخ انقضا</th><td><code>{shamsi_expiry}</code></td></tr>"
            f"  <tr><th>زمان باقی‌مانده</th><td><code>{remaining_days}</code> روز و <code>{remaining_hours}</code> ساعت</td></tr>"
            "</table>"
            "<br/>"
            "<h3>💎 مزایای فعال شما:</h3>"
            "<blockquote>"
            f"• سهمیه بید ماهانه: <b>{current_allowance} کانکت</b><br/>"
            f"• کمیسیون <b>{commissions.get(vip_level, '۰٪')}</b> در پروژه‌های انجام شده (به جای ۱۰٪)<br/>"
            "• نمایش <b>اولویت‌دار</b> پیشنهادات شما در صدر لیست کارفرما"
            "</blockquote>"
            "<br/>"
            "<p>💡 برای ارتقا یا تمدید اشتراک خود یکی از سطوح زیر را انتخاب کنید:</p>"
        )
        rich_msg = InputRichMessage(html=html_content)
        await message.bot.send_rich_message(
            chat_id=message.chat.id,
            rich_message=rich_msg,
            reply_markup=inline.vip_tier_keyboard(is_extension=True)
        )
    else:
        html_content = (
            f"<h1>💎 پکیج‌های ویژه VIP ایکس‌لنسر</h1>"
            "<hr/>"
            "<p>با ارتقای حساب کاربری خود، از رقبایتان متمایز شوید و سود بیشتری کسب کنید!</p>"
            "<br/>"
            "<table bordered striped>"
            "  <thead>"
            "    <tr>"
            "      <th>ویژگی پکیج</th>"
            "      <th>🥉 برنزی</th>"
            "      <th>🥈 نقره‌ای</th>"
            "      <th>🥇 طلایی</th>"
            "    </tr>"
            "  </thead>"
            "  <tbody>"
            "    <tr>"
            "      <td>قیمت ماهانه</td>"
            f"      <td>{p1:,} تومان</td>"
            f"      <td>{p2:,} تومان</td>"
            f"      <td>{p3:,} تومان</td>"
            "    </tr>"
            "    <tr>"
            "      <td>کمیسیون پلتفرم</td>"
            f"      <td>{c1}٪</td>"
            f"      <td>{c2}٪</td>"
            f"      <td>{c3}٪ (صفر)</td>"
            "    </tr>"
            "    <tr>"
            "      <td>سهمیه بید ماهانه</td>"
            f"      <td>{s_bronze} کانکت</td>"
            f"      <td>{s_silver} کانکت</td>"
            f"      <td>{s_gold} کانکت</td>"
            "    </tr>"
            "    <tr>"
            "      <td>اولویت نمایش</td>"
            "      <td>عادی</td>"
            "      <td>اولویت‌دار</td>"
            "      <td>اولویت حداکثری</td>"
            "    </tr>"
            "  </tbody>"
            "</table>"
            "<br/>"
            "<p>💡 لطفاً سطح اشتراک مورد نظر خود را جهت ارتقا انتخاب کنید:</p>"
        )
        rich_msg = InputRichMessage(html=html_content)
        await message.bot.send_rich_message(
            chat_id=message.chat.id,
            rich_message=rich_msg,
            reply_markup=inline.vip_tier_keyboard(is_extension=False)
        )

@router.callback_query(F.data.startswith("select_vip_tier_"))
async def select_vip_tier(callback: CallbackQuery):
    tier = int(callback.data.split("_")[-1])
    
    tier_names = {1: "برنزی 🥉", 2: "نقره‌ای 🥈", 3: "طلایی 🥇"}
    name = tier_names.get(tier, "ویژه")
    
    p1 = int(await db.get_setting('vip_bronze_price', 50000))
    p2 = int(await db.get_setting('vip_silver_price', 100000))
    p3 = int(await db.get_setting('vip_gold_price', 200000))
    
    base_price = {1: p1, 2: p2, 3: p3}.get(tier, p1)
    
    pr_1 = base_price
    pr_3 = int(base_price * 2.7)
    pr_6 = base_price * 5
    pr_12 = base_price * 9
    
    import random
    d1 = random.randint(20, 35)
    d3 = random.randint(30, 45)
    d6 = random.randint(40, 55)
    d12 = random.randint(50, 65)
    
    f1 = round((pr_1 / (1 - d1/100)) / 1000) * 1000
    f3 = round((pr_3 / (1 - d3/100)) / 1000) * 1000
    f6 = round((pr_6 / (1 - d6/100)) / 1000) * 1000
    f12 = round((pr_12 / (1 - d12/100)) / 1000) * 1000
    
    text = (
        f"💎 <b>انتخاب مدت زمان اشتراک {name}</b>\n\n"
        f"قیمت‌ها همراه با تخفیف ویژه به شرح زیر است:\n\n"
        f"🔹 <b>۱ ماهه:</b> <s>{f1:,}</s> <b>{pr_1:,} تومان</b> (🔥 {d1}٪ تخفیف)\n"
        f"🔹 <b>۳ ماهه:</b> <s>{f3:,}</s> <b>{pr_3:,} تومان</b> (🔥 {d3}٪ تخفیف)\n"
        f"🔹 <b>۶ ماهه:</b> <s>{f6:,}</s> <b>{pr_6:,} تومان</b> (🔥 {d6}٪ تخفیف)\n"
        f"🔹 <b>۱۲ ماهه:</b> <s>{f12:,}</s> <b>{pr_12:,} تومان</b> (🔥 {d12}٪ تخفیف استثنایی)\n\n"
        f"👇 یکی از پلن‌های زیر را انتخاب کنید:"
    )
    
    await callback.message.edit_text(text, reply_markup=inline.vip_duration_keyboard(tier), parse_mode="HTML")

@router.callback_query(F.data == "back_to_vip_tiers")
async def back_to_vip_tiers(callback: CallbackQuery, db_user):
    from handlers.freelancer import show_vip as freelancer_show_vip
    try:
        await callback.message.delete()
    except:
        pass
    # Pass user_id directly instead of mutating callback.message
    await freelancer_show_vip(callback.message, user_id=callback.from_user.id)


@router.callback_query(F.data.startswith("buy_vip_"))
async def buy_vip(callback: CallbackQuery, db_user):
    parts = callback.data.split("_")
    level = int(parts[2])
    months = int(parts[3])
    
    p1 = int(await db.get_setting('vip_bronze_price', 50000))
    p2 = int(await db.get_setting('vip_silver_price', 100000))
    p3 = int(await db.get_setting('vip_gold_price', 200000))
    
    base_price = {1: p1, 2: p2, 3: p3}.get(level, p1)
    
    price_map = {
        1: base_price,
        3: int(base_price * 2.7),
        6: base_price * 5,
        12: base_price * 9
    }
    
    price = price_map.get(months, base_price)
    
    names = {1: "برنزی", 2: "نقره‌ای", 3: "طلایی"}
    name = f"{names.get(level, 'ویژه')} ({months} ماهه)"
    
    s_bronze = int(await db.get_setting('connects_monthly_bronze', '50'))
    s_silver = int(await db.get_setting('connects_monthly_silver', '100'))
    s_gold = int(await db.get_setting('connects_monthly_gold', '200'))
    base_bonus = {1: s_bronze, 2: s_silver, 3: s_gold}.get(level, s_bronze)
    # Give a bit of extra bonus for longer months
    bonus_multiplier = {1: 1, 3: 3.5, 6: 8, 12: 20}.get(months, 1)
    bonus = int(base_bonus * bonus_multiplier)
    
    balance = db_user['balance'] or 0
    if balance < price:
        role = db_user['role'] or 'freelancer'
        await show_dynamic_payment_options(
            callback=callback,
            required_amount=price,
            balance=balance,
            action_type="vip",
            action_meta=f"{level}_{months}",
            back_callback=f"role_{role}"
        )
        return
        
    await db.update_balance(callback.from_user.id, -price, "subscription", f"خرید اشتراک VIP {name}")
    await db.activate_vip(callback.from_user.id, level=level, months=months)
    await db.update_connects(callback.from_user.id, bonus, "vip_bonus", f"هدیه فعالسازی اشتراک VIP {name}")
    
    role = db_user['role'] or 'freelancer'
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="🔙 بازگشت به منوی اصلی", callback_data=f"role_{role}", style="success")
    
    await callback.message.edit_text(
        f"🎉 <b>اشتراک VIP {name} شما با موفقیت فعال شد!</b>\n\n"
        f"🪙 تعداد <b>{bonus} کانکت (بید)</b> هدیه به حساب شما افزوده شد.",
        reply_markup=builder.as_markup(),
        parse_mode="HTML"
    )
    await callback.answer(f"🎉 VIP {name} فعال شد!")

# ── Freelancer Profile View (Client Side) ───────────────────────────────

@router.callback_query(F.data.startswith("client_view_fl_"))
async def view_freelancer_profile_by_client(callback: CallbackQuery):
    parts = callback.data.split("_")
    fl_id = int(parts[3])
    bid_id = int(parts[4])
    
    fl_user = await db.get_user(fl_id)
    if not fl_user:
        return await callback.answer("مشخصات فریلنسر یافت نشد.", show_alert=True)
        
    vip_level = await db.check_vip_level(fl_id)
    vip_names = {0: "👤 کاربر عادی", 1: "🥉 برنزی", 2: "🥈 نقره‌ای", 3: "🥇 طلایی"}
    vip_status = vip_names.get(vip_level, "👤 کاربر عادی")
    
    # Fetch ratings & completed projects count
    import aiosqlite
    async with aiosqlite.connect(db.DB_PATH) as conn:
        conn.row_factory = aiosqlite.Row
        async with conn.execute(
            '''SELECT COUNT(*) as total_projects, 
                       COALESCE(AVG(rating_to_freelancer), 0) as avg_rating 
                FROM projects 
                WHERE chosen_freelancer_id = ? AND status = 'completed' AND rating_to_freelancer IS NOT NULL''',
            (fl_id,)
        ) as cursor:
            stats = await cursor.fetchone()
            completed_projects = stats['total_projects'] if stats else 0
            avg_rating = stats['avg_rating'] if stats else 0.0
            
    skills = fl_user['skills'] or "_ثبت نشده_"
    bio = fl_user['bio'] or "_ثبت نشده_"
    
    # Fetch reviews
    reviews = await db.get_freelancer_reviews(fl_id, limit=3)
    reviews_html = ""
    for rev in reviews:
        reviews_html += f"<p>• <b>{rev['client_name']}</b> (⭐ {rev['rating_to_freelancer']}):<br/><i>{rev['review_to_freelancer']}</i></p>"
    if not reviews_html:
        reviews_html = "<p>_نظری ثبت نشده است_</p>"
        
    html_content = (
        f"<h1>👤 پروفایل فریلنسر: {fl_user['full_name'] or fl_user['username'] or 'کاربر'}</h1>"
        "<hr/>"
        "<table bordered>"
        f"  <tr><th>🆔 شناسه کاربری</th><td>{fl_id}</td></tr>"
        f"  <tr><th>🏅 وضعیت حساب</th><td>{vip_status}</td></tr>"
        f"  <tr><th>📊 پروژه‌های تکمیل شده</th><td>{completed_projects}</td></tr>"
        f"  <tr><th>⭐ امتیاز میانگین</th><td>{avg_rating:.1f} از ۵</td></tr>"
        "</table>"
        "<br/>"
        "<h3>💻 تخصص‌ها و مهارت‌ها:</h3>"
        f"<blockquote>{skills}</blockquote>"
        "<h3>📄 درباره فریلنسر:</h3>"
        f"<blockquote>{bio}</blockquote>"
        "<h3>💬 دیدگاه‌های کارفرمایان قبلی:</h3>"
        f"<blockquote>{reviews_html}</blockquote>"
    )
    
    rich_msg = InputRichMessage(html=html_content)
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text="🔙 بازگشت به پیشنهاد", callback_data=f"bid_detail_{bid_id}", style="danger")
    builder.adjust(1)
    
    await callback.message.edit_text(text=None, reply_markup=builder.as_markup(), rich_message=rich_msg)
    await callback.answer()

@router.callback_query(F.data.startswith("promote_project_"))
async def promote_project_handler(callback: CallbackQuery, db_user):
    project_id = int(callback.data.split("_")[-1])
    featured_project_price = await db.get_setting_int("featured_project_price", 50000)
    balance = db_user['balance'] or 0
    if balance < featured_project_price:
        await show_dynamic_payment_options(
            callback=callback,
            required_amount=featured_project_price,
            balance=balance,
            action_type="promo",
            action_meta=str(project_id),
            back_callback=f"client_project_{project_id}"
        )
        return
        
    success = await db.promote_project(project_id, callback.from_user.id, featured_project_price)
    if not success:
        return await callback.answer("عملیات ناموفق بود.", show_alert=True)
        
    await callback.answer("🎉 پروژه شما با موفقیت ویژه شد و در صدر جستجوها قرار گرفت!", show_alert=True)
    try:
        await callback.message.answer(
            "🔥 <b>پروژه شما با موفقیت ویژه شد!</b>\nاین پروژه اکنون با اولویت بالاتر به فریلنسرها نمایش داده می‌شود.",
            parse_mode="HTML",
            message_effect_id="5104858069654301597"
        )
    except:
        pass
    
    # Refresh project view
    project = await db.get_project(project_id)
    if project:
        project = dict(project)
    bids = await db.get_bids_for_project(project_id)
    card = texts.project_card(project)
    card += f"\n👥 <b>تعداد پیشنهادها:</b> {len(bids)}"
    if project['status'] == 'delivered' and project['delivery_text']:
        card += f"\n\n📥 <b>توضیحات فایل تحویل شده فریلنسر:</b>\n{project['delivery_text']}"
        if project.get('delivery_file_id'):
            card += f"\n📎 <i>فایل کار ضمیمه شده است. برای دانلود، از دکمه زیر استفاده کنید.</i>"
    await callback.message.edit_text(
        card,
        reply_markup=inline.project_management_keyboard(
            project_id, 
            project['status'], 
            project['is_featured'], 
            is_fulltime=project.get('is_fulltime', 0),
            has_delivery_file=bool(project.get('delivery_file_id')),
            back_callback_data="my_projects_page_0"
        ),
        parse_mode="HTML"
    )

@router.callback_query(F.data.startswith("upgrade_fulltime_"))
async def upgrade_fulltime_handler(callback: CallbackQuery, db_user):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    
    if not project or project['client_id'] != callback.from_user.id:
        return await callback.answer("دسترسی غیرمجاز.", show_alert=True)
        
    if project.get('is_fulltime', 0):
        return await callback.answer("این پروژه قبلاً تمام‌وقت شده است.", show_alert=True)
        
    price = int(await db.get_setting('fulltime_price', 20000))
    balance = db_user['balance'] or 0
    
    if balance < price:
        await show_dynamic_payment_options(
            callback=callback,
            required_amount=price,
            balance=balance,
            action_type="upgrade_ft",
            action_meta=str(project_id),
            back_callback=f"client_project_{project_id}"
        )
        return
        
    # Deduct balance and upgrade
    await db.update_balance(callback.from_user.id, -price, "service", f"هزینه ارتقا به تمام‌وقت برای پروژه {project_id}")
    await db.update_project_fulltime(project_id, True)
    
    await callback.answer("✅ پروژه شما با موفقیت به تمام‌وقت ارتقا یافت!", show_alert=True)
    
    # Reload project
    try:
        await my_project_detail_callback(callback)
    except:
        pass

# ── Mutual Cancellation / Dispute Resolution ────────────────────────────

@router.callback_query(F.data.startswith("request_cancel_"))
async def request_cancel_handler(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer("پروژه یافت نشده است.", show_alert=True)
        
    user_id = callback.from_user.id
    if project['cancel_requested_by']:
        if project['cancel_requested_by'] == user_id:
            return await callback.answer("⚠️ شما قبلاً درخواست لغو را داده‌اید و منتظر پاسخ طرف مقابل هستید.", show_alert=True)
        else:
            # The other party requested cancel, and now this user requests cancel too -> Approve!
            await db.refund_project(project_id)
            await callback.message.edit_text("✅ پروژه با موافقت طرفین لغو و وجه آن به کارفرما مسترد گردید.")
            
            try:
                role_back = "wallet_menu_back" if other_id == project['client_id'] else "wallet_menu_back_freelancer"
                from aiogram.utils.keyboard import InlineKeyboardBuilder
                builder = InlineKeyboardBuilder()
                builder.button(text="💰 مشاهده کیف پول", callback_data=role_back)
                
                await bot.send_message(
                    other_id,
                    f"✅ <b>لغو توافقی پروژه انجام شد</b>\n"
                    f"\n"
                    f"📌 پروژه: <b>{project['title']}</b>\n"
                    f"📊 وضعیت: 🔴 <b>لغو شده</b>\n"
                    f"\n"
                    f"با موافقت طرفین پروژه لغو شد و وجه آن بازگردانده شد. برای بررسی موجودی دکمه زیر را کلیک کنید:",
                    reply_markup=builder.as_markup(),
                    parse_mode="HTML"
                )
            except: pass
            await callback.answer("پروژه با موفقیت لغو شد.")
            return
            
    await db.request_project_cancel(project_id, user_id)
    await callback.answer("✅ درخواست لغو توافقی ارسال گردید.", show_alert=True)
    
    # Notify the other party
    other_id = project['chosen_freelancer_id'] if user_id == project['client_id'] else project['client_id']
    role_str = "کارفرما" if user_id == project['client_id'] else "فریلنسر"
    
    notify_text = (
        f"⚠️ <b>درخواست لغو توافقی پروژه</b>\n"
        f"\n"
        f"📌 پروژه: <b>{project['title']}</b>\n"
        f"👤 فرستنده درخواست: <b>{role_str}</b>\n"
        f"\n"
        f"کارفرما/فریلنسر درخواست لغو توافقی پروژه را داده است. در صورت موافقت شما، پروژه لغو شده و هزینه درگیر در پرداخت امن تماماً به کارفرما بازگردانده می‌شود.\n\n"
        f"لطفاً موافقت یا مخالفت خود را اعلام کنید:"
    )
    try:
        await bot.send_message(
            other_id,
            notify_text,
            reply_markup=inline.cancel_resolution_keyboard(project_id),
            parse_mode="HTML"
        )
    except:
        pass

@router.callback_query(F.data.startswith("cancel_approve_"))
async def cancel_approve_handler(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project or project['status'] not in ('in_progress', 'delivered'):
        return await callback.answer("پروژه در وضعیت فعال نیست.", show_alert=True)
        
    await db.refund_project(project_id)
    await callback.message.edit_text("✅ با لغو پروژه موافقت شد. پروژه لغو و وجه آن به حساب کارفرما برگشت داده شد.")
    
    # Notify requester
    other_id = project['client_id'] if callback.from_user.id == project['chosen_freelancer_id'] else project['chosen_freelancer_id']
    try:
        role_back = "wallet_menu_back" if other_id == project['client_id'] else "wallet_menu_back_freelancer"
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        builder.button(text="💰 مشاهده کیف پول", callback_data=role_back)
        
        await bot.send_message(
            other_id,
            f"✅ <b>موافقت با لغو پروژه</b>\n"
            f"\n"
            f"📌 پروژه: <b>{project['title']}</b>\n"
            f"\n"
            f"طرف مقابل با درخواست لغو توافقی پروژه موافقت کرد. وجه پرداختی به کیف پول کارفرما بازگردانده شد.",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
    except: pass
    await callback.answer()

@router.callback_query(F.data.startswith("cancel_reject_"))
async def cancel_reject_handler(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer()
        
    await db.reject_project_cancel(project_id)
    await callback.message.edit_text("❌ شما با لغو پروژه مخالفت کردید. پروژه به حالت عادی ادامه می‌یابد.")
    
    # Notify requester
    other_id = project['client_id'] if callback.from_user.id == project['chosen_freelancer_id'] else project['chosen_freelancer_id']
    try:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        workspace_role_cb = f"async_chat_client_{project_id}" if other_id == project['chosen_freelancer_id'] else f"async_chat_fl_{project_id}"
        builder.button(text="💬 گفتگو با طرف مقابل", callback_data=workspace_role_cb)
        
        await bot.send_message(
            other_id,
            f"❌ <b>مخالفت با لغو پروژه</b>\n"
            f"\n"
            f"📌 پروژه: <b>{project['title']}</b>\n"
            f"\n"
            f"طرف مقابل با درخواست لغو توافقی پروژه مخالفت کرد. پروژه کماکان فعال است و می‌توانید با گفتگو روند کار را پیش ببرید.",
            reply_markup=builder.as_markup(),
            parse_mode="HTML"
        )
    except: pass
    await callback.answer()

# ── Chat Routing Initiation ─────────────────────────────────────────────

@router.callback_query(F.data.startswith("chat_start_"))
async def chat_start_handler(callback: CallbackQuery, bot: Bot, state: FSMContext):
    bid_id = int(callback.data.split("_")[-1])
    try:
        await callback.message.edit_text(
            text=str(callback.message.text) + "\n\n💬 <i>شما وارد گفتگو شدید.</i>",
            reply_markup=None,
            parse_mode="HTML"
        )
    except:
        pass
        
    from database import db
    bid = await db.get_bid(bid_id)
    if not bid:
        return await callback.answer("پیشنهاد یافت نشد.", show_alert=True)
    project = await db.get_project(bid['project_id'])
    if not project:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
        
    client_id = callback.from_user.id
    freelancer_id = bid['freelancer_id']
    project_id = project['project_id']
    
    import hashlib
    import config
    from aiogram.types import WebAppInfo
    from keyboards import inline
    
    bot_domain = await db.get_setting('bot_domain', 'http://127.0.0.1:8000')
    
    client_token = hashlib.sha256(f"Xlancer_Secret_{project_id}_{client_id}".encode()).hexdigest()
    freelancer_token = hashlib.sha256(f"Xlancer_Secret_{project_id}_{freelancer_id}".encode()).hexdigest()
    
    kb_client = inline.InlineKeyboardBuilder()
    kb_client.button(text="ورود به چت‌روم پروژه 💬", web_app=WebAppInfo(url=f"{bot_domain}/chat?project_id={project_id}&user_id={client_id}&token={client_token}"))
    
    kb_freelancer = inline.InlineKeyboardBuilder()
    kb_freelancer.button(text="ورود به چت‌روم پروژه 💬", web_app=WebAppInfo(url=f"{bot_domain}/chat?project_id={project_id}&user_id={freelancer_id}&token={freelancer_token}"))
    
    # Inform client
    await callback.message.answer(
        f"💬 <b>چت‌روم اختصاصی پروژه ایجاد شد.</b>\n\n📌 موضوع پروژه: <b>{project['title']}</b>\n\nبرای گفتگو با فریلنسر روی دکمه زیر کلیک کنید.",
        reply_markup=kb_client.as_markup(),
        parse_mode="HTML"
    )
    
    # Inform freelancer
    try:
        await bot.send_message(
            freelancer_id,
            f"💬 <b>کارفرما چت‌روم اختصاصی برای پروژه «{project['title']}» را فعال کرد.</b>\n\nبرای گفتگو روی دکمه زیر کلیک کنید.",
            reply_markup=kb_freelancer.as_markup(),
            parse_mode="HTML"
        )
    except:
        pass
        
    await callback.answer("چت‌روم اختصاصی آماده شد!")

# ── Project Dispute Arbitration ─────────────────────────────────────────

@router.callback_query(F.data.startswith("dispute_project_"))
async def dispute_project_handler(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
        
    import aiosqlite
    # Update project status to disputed in database
    async with aiosqlite.connect(db.DB_PATH) as conn:
        await conn.execute("UPDATE projects SET status = 'disputed' WHERE project_id = ?", (project_id,))
        # Fetch escrow amount
        async with conn.execute('SELECT amount, freelancer_id FROM escrow WHERE project_id = ?', (project_id,)) as cursor:
            row = await cursor.fetchone()
            amount = row[0] if row else 0
            freelancer_id = row[1] if row else 0
        await conn.commit()
        
    await db.create_dispute_chat(project_id, project['client_id'], freelancer_id)
        
    # Notify client and freelancer
    user_html = (
        f"<h1>⚖️ ورود به وضعیت داوری (Dispute)</h1>"
        "<hr/>"
        f"<h3>📌 پروژه: «{project['title']}»</h3>"
        f"<p>مدیران ایکس‌لنسر به زودی موضوع را بررسی خواهند کرد.</p>"
        "<br/>"
        "<table bordered>"
        f"  <tr><th>💰 مبلغ Escrow بلوکه‌شده</th><td>{amount:,} تومان</td></tr>"
        "</table>"
    )
    user_rich_msg = InputRichMessage(html=user_html)
    
    try:
        await bot.send_rich_message(project['client_id'], rich_message=user_rich_msg)
        if freelancer_id:
            await bot.send_rich_message(freelancer_id, rich_message=user_rich_msg)
    except: pass
    
    # Notify admins
    recipients = await db.get_admin_recipients('disputes')
    for admin_id in recipients:
        try:
            admin_html = (
                f"<h1>⚖️ درخواست داوری و شکایت جدید</h1>"
                "<hr/>"
                "<table bordered>"
                f"  <tr><th>📌 پروژه</th><td>#{project_id} — {project['title']}</td></tr>"
                f"  <tr><th>👔 کارفرما ID</th><td><code>{project['client_id']}</code></td></tr>"
                f"  <tr><th>💻 فریلنسر ID</th><td><code>{freelancer_id}</code></td></tr>"
                f"  <tr><th>💰 مبلغ بلوکه‌شده</th><td>{amount:,} تومان</td></tr>"
                "</table>"
                "<br/>"
                "<h3>⚖️ دستورات داوری ادمین:</h3>"
                f"<code>/releaseescrow {project_id}</code> (پرداخت به فریلنسر)<br/>"
                f"<code>/refund {project_id}</code> (مرجوعی به کارفرما)"
            )
            await bot.send_rich_message(admin_id, rich_message=InputRichMessage(html=admin_html))
        except: pass
        
    await callback.message.edit_text(f"⚖️ پروژه وارد وضعیت شکایت و داوری شد. منتظر تصمیم ادمین بمانید.")
    await callback.answer("شکایت ثبت شد.")

@router.callback_query(F.data == "wallet_withdraw_start")
async def start_withdrawal(callback: CallbackQuery, state: FSMContext, db_user):
    balance = db_user['balance'] or 0
    min_withdraw = int(await db.get_setting('min_wallet_withdraw', '10000'))
    if balance < min_withdraw:
        return await callback.answer(f"❌ حداقل مبلغ قابل برداشت {min_withdraw:,} تومان می‌باشد.", show_alert=True)
    
    await state.set_state(WalletWithdraw.waiting_for_amount)
    await callback.message.answer(
        f"💵 <b>درخواست تسویه حساب و برداشت وجه</b>\n\n"
        f"موجودی فعلی شما: <b>{balance:,} تومان</b>\n"
        f"مبلغ مورد نظر خود را به تومان وارد کنید (فقط عدد):",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(WalletWithdraw.waiting_for_amount)
async def process_withdraw_amount(message: Message, state: FSMContext, db_user):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    
    amount = int(message.text)
    balance = db_user['balance'] or 0
    min_withdraw = int(await db.get_setting('min_wallet_withdraw', '10000'))
    if amount < min_withdraw:
        return await message.answer(f"❌ حداقل مبلغ قابل برداشت {min_withdraw:,} تومان می‌باشد.")
    if amount > balance:
        return await message.answer(f"❌ موجودی شما کافی نیست. حداکثر مبلغ قابل برداشت: {balance:,} تومان")
    
    await state.update_data(amount=amount)
    await state.set_state(WalletWithdraw.waiting_for_card)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    await message.answer(
        "💳 لطفاً شماره کارت ۱۶ رقمی خود را وارد کنید:\n_(فقط عدد بدون خط تیره)_",
        parse_mode="HTML"
    )

@router.message(WalletWithdraw.waiting_for_card)
async def process_withdraw_card(message: Message, state: FSMContext):
    card = message.text.strip().replace(" ", "").replace("-", "")
    if not card.isdigit() or len(card) != 16:
        return await message.answer("❌ شماره کارت نامعتبر است. شماره کارت باید دقیقاً ۱۶ رقم باشد. دوباره تلاش کنید:")
    
    await state.update_data(card_number=card)
    await state.set_state(WalletWithdraw.waiting_for_shaba)
    try: await message.react([ReactionTypeEmoji(emoji="💳")])
    except: pass
    await message.answer(
        "🏦 لطفاً شماره شبای خود را بدون IR وارد کنید:\n_(فقط ۲۴ رقم عددی)_",
        parse_mode="HTML"
    )

@router.message(WalletWithdraw.waiting_for_shaba)
async def process_withdraw_shaba(message: Message, state: FSMContext, bot: Bot, db_user):
    shaba = message.text.strip().upper().replace(" ", "").replace("IR", "")
    if not shaba.isdigit() or len(shaba) != 24:
        return await message.answer("❌ شماره شبا نامعتبر است. شماره شبا باید دقیقاً ۲۴ رقم باشد. دوباره تلاش کنید:")
    
    data = await state.get_data()
    amount = data['amount']
    card_number = data['card_number']
    
    # Save to database
    withdrawal_id = await db.create_withdrawal_request(
        user_id=message.from_user.id,
        amount=amount,
        card_number=card_number,
        shaba=shaba
    )
    
    await state.clear()
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'freelancer'
    kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)
    
    if not withdrawal_id:
        return await message.answer("❌ خطا در ثبت درخواست. موجودی شما کافی نیست یا مشکلی پیش آمده است.", reply_markup=kb)
        
    await message.answer(
        f"✅ <b>درخواست تسویه حساب با موفقیت ثبت شد!</b>\n\n"
        f"📌 شناسه درخواست: `#{withdrawal_id}`\n"
        f"💰 مبلغ: <b>{amount:,} تومان</b> (تا زمان واریز در حالت تعلیق قرار گرفت)\n"
        f"💳 شماره کارت: `{card_number}`\n"
        f"🏦 شماره شبا: `IR{shaba}`\n\n"
        f"درخواست شما توسط مدیریت بررسی و طی ۲۴ ساعت آینده واریز خواهد شد.",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014" # Thumbs up effect
    )
    
    # Alert Admins
    recipients = await db.get_admin_recipients('payments')
    for admin_id in recipients:
        try:
            await bot.send_message(
                admin_id,
                f"📥 <b>درخواست جدید تسویه حساب (برداشت وجه):</b>\n\n"
                f"🆔 شناسه درخواست: `#{withdrawal_id}`\n"
                f"👤 کاربر: `{message.from_user.id}` — {db_user['full_name'] or db_user['username'] or 'ناشناس'}\n"
                f"💰 مبلغ: <b>{amount:,} تومان</b>\n"
                f"💳 شماره کارت: `{card_number}`\n"
                f"🏦 شبا: `IR{shaba}`\n\n"
                f"پرداخت از پنل ادمین با دستورات یا دکمه‌های زیر:\n"
                f"تایید: `/approvewithdraw {withdrawal_id}`\n"
                f"رد: `/rejectwithdraw {withdrawal_id}`",
                reply_markup=inline.admin_withdrawal_keyboard(withdrawal_id),
                parse_mode="HTML"
            )
        except:
            pass


# ── Client Viewing Freelancer Portfolios ────────────────────────────────

async def send_client_portfolio_slide(callback: CallbackQuery, fl_id: int, bid_id: int, index: int):
    portfolios = await db.get_user_portfolios(fl_id)
    if not portfolios:
        return await callback.answer("😔 این فریلنسر هنوز هیچ نمونه‌کاری ثبت نکرده است.", show_alert=True)
        
    if index < 0:
        index = len(portfolios) - 1
    elif index >= len(portfolios):
        index = 0
        
    item = portfolios[index]
    url_str = item['project_url'] if item['project_url'] else "ثبت نشده"
    
    text = (
        f"📂 <b>نمونه‌کار فریلنسر ({index + 1} از {len(portfolios)})</b>\n\n"
        f"📝 <b>عنوان:</b> {item['title']}\n"
        f"📄 <b>توضیحات:</b>\n{item['description']}\n\n"
        f"🔗 <b>لینک دمو/پروژه:</b> {url_str}"
    )
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    
    if len(portfolios) > 1:
        builder.button(text="⬅️ قبلی", callback_data=f"cl_port_swipe_{fl_id}_{bid_id}_{index - 1}")
        builder.button(text="بعدی ➡️", callback_data=f"cl_port_swipe_{fl_id}_{bid_id}_{index + 1}")
        builder.adjust(2, 1)
    else:
        builder.adjust(1)
        
    builder.button(text="🔙 بازگشت به پیشنهاد", callback_data=f"bid_detail_{bid_id}", style="danger")
    kb = builder.as_markup()
    
    file_id = item.get('file_id')
    is_current_msg_media = bool(callback.message.photo or callback.message.document or callback.message.video)
    
    if file_id:
        from aiogram.types import InputMediaPhoto
        if is_current_msg_media:
            try:
                await callback.message.edit_media(
                    media=InputMediaPhoto(media=file_id, caption=text, parse_mode="HTML"),
                    reply_markup=kb
                )
            except Exception:
                try: await callback.message.delete()
                except: pass
                await callback.message.answer_photo(photo=file_id, caption=text, reply_markup=kb, parse_mode="HTML")
        else:
            try: await callback.message.delete()
            except: pass
            await callback.message.answer_photo(photo=file_id, caption=text, reply_markup=kb, parse_mode="HTML")
    else:
        if is_current_msg_media:
            try: await callback.message.delete()
            except: pass
            await callback.message.answer(text, reply_markup=kb, parse_mode="HTML")
        else:
            try:
                await callback.message.edit_text(text, reply_markup=kb, parse_mode="HTML")
            except Exception:
                await callback.message.answer(text, reply_markup=kb, parse_mode="HTML")

@router.callback_query(F.data.startswith("client_view_portfolios_"))
async def client_view_portfolios(callback: CallbackQuery):
    parts = callback.data.split("_")
    fl_id = int(parts[3])
    bid_id = int(parts[4])
    await send_client_portfolio_slide(callback, fl_id, bid_id, 0)

@router.callback_query(F.data.startswith("cl_port_swipe_"))
async def client_portfolio_swipe(callback: CallbackQuery):
    parts = callback.data.split("_")
    fl_id = int(parts[3])
    bid_id = int(parts[4])
    index = int(parts[5])
    await send_client_portfolio_slide(callback, fl_id, bid_id, index)


# ── Project Editing FSM ───────────────────────────────────────────────

@router.callback_query(F.data.startswith("client_edit_project_"))
async def start_project_editing(callback: CallbackQuery, state: FSMContext):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if not project or project['status'] != 'open':
        return await callback.answer("این پروژه قابل ویرایش نیست.", show_alert=True)
        
    await state.update_data(edit_project_id=project_id)
    await state.set_state(ProjectEditing.waiting_for_title)
    
    await callback.message.answer(
        f"✏️ <b>ویرایش پروژه #{project_id}</b>\n\n"
        f"عنوان فعلی: «{project['title']}»\n"
        f"لطفا عنوان جدید پروژه را بفرستید:",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(ProjectEditing.waiting_for_title)
async def process_edit_title(message: Message, state: FSMContext):
    if len(message.text) < 5:
        return await message.answer("عنوان پروژه خیلی کوتاه است. حداقل ۵ کاراکتر وارد کنید.")
    await state.update_data(title=message.text)
    await state.set_state(ProjectEditing.waiting_for_desc)
    try: await message.react([ReactionTypeEmoji(emoji="✍️")])
    except: pass
    await message.answer("📋 لطفا توضیحات کامل جدید پروژه را بفرستید:")

@router.message(ProjectEditing.waiting_for_desc)
async def process_edit_desc(message: Message, state: FSMContext):
    if len(message.text) < 20:
        return await message.answer("توضیحات کافی نیست. حداقل ۲۰ کاراکتر وارد کنید.")
    await state.update_data(description=message.text)
    await state.set_state(ProjectEditing.waiting_for_budget_min)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    await message.answer("💵 لطفا حداقل بودجه جدید را به تومان وارد کنید (فقط عدد):")

@router.message(ProjectEditing.waiting_for_budget_min)
async def process_edit_budget_min(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    budget_val = int(message.text)
    min_budget = int(await db.get_setting('min_project_budget', '10000'))
    if budget_val < min_budget:
        return await message.answer(f"❌ حداقل بودجه پروژه نمی‌تواند کمتر از {min_budget:,} تومان باشد. دوباره تلاش کنید:")
    await state.update_data(budget_min=budget_val)
    await state.set_state(ProjectEditing.waiting_for_budget_max)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    await message.answer("💵 لطفا حداکثر بودجه جدید را به تومان وارد کنید (فقط عدد):")

@router.message(ProjectEditing.waiting_for_budget_max)
async def process_edit_budget_max(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    data = await state.get_data()
    if int(message.text) < data['budget_min']:
        return await message.answer("❌ حداکثر بودجه باید از حداقل بیشتر باشد.")
    await state.update_data(budget_max=int(message.text))
    await state.set_state(ProjectEditing.waiting_for_deadline)
    try: await message.react([ReactionTypeEmoji(emoji="⏳")])
    except: pass
    await message.answer("📅 مهلت جدید تحویل چند روز است؟ (فقط عدد):")

@router.message(ProjectEditing.waiting_for_deadline)
async def process_edit_deadline(message: Message, state: FSMContext, db_user):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
        
    data = await state.get_data()
    pid = data['edit_project_id']
    await state.clear()
    
    await db.update_project_details(
        project_id=pid,
        title=data['title'],
        description=data['description'],
        budget_min=data['budget_min'],
        budget_max=data['budget_max'],
        deadline_days=int(message.text)
    )
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'client'
    kb = reply.client_main_menu(is_admin=is_admin) if role == 'client' else reply.freelancer_main_menu(is_admin=is_admin)
    
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer(
        f"✅ پروژه #{pid} با موفقیت ویرایش و بروزرسانی شد!",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014"
    )

@router.callback_query(F.data.startswith("async_chat_fl_"))
async def async_chat_fl_handler(callback: CallbackQuery, bot: Bot, state: FSMContext):
    pid = int(callback.data.split("_")[-1])
    from database import db
    project = await db.get_project(pid)
    if not project or not project['chosen_freelancer_id']:
        return await callback.answer("پروژه یا فریلنسر یافت نشد.", show_alert=True)
        
    client_id = callback.from_user.id
    project_id = project['project_id']
    
    import hashlib
    from aiogram.types import WebAppInfo
    from keyboards import inline
    
    bot_domain = await db.get_setting('bot_domain', 'http://127.0.0.1:8000')
    client_token = hashlib.sha256(f"Xlancer_Secret_{project_id}_{client_id}".encode()).hexdigest()
    
    kb = inline.InlineKeyboardBuilder()
    kb.button(text="ورود به چت‌روم پروژه 💬", web_app=WebAppInfo(url=f"{bot_domain}/chat?project_id={project_id}&user_id={client_id}&token={client_token}"))
    
    await callback.message.answer(
        f"💬 <b>ارسال پیام به فریلنسر در چت‌روم اختصاصی</b>\nلطفاً از طریق دکمه زیر وارد چت‌روم شوید:",
        reply_markup=kb.as_markup(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data.startswith("client_download_delivery_"))
async def client_download_delivery(callback: CallbackQuery, bot: Bot):
    project_id = int(callback.data.split("_")[-1])
    project = await db.get_project(project_id)
    if project:
        project = dict(project)
    if not project or not project.get('delivery_file_id'):
        return await callback.answer("❌ فایلی برای این پروژه تحویل داده نشده است.", show_alert=True)
        
    await callback.answer("⏳ در حال ارسال فایل...")
    try:
        file_id = project['delivery_file_id']
        await bot.send_document(
            chat_id=callback.message.chat.id,
            document=file_id,
            caption=f"📎 <b>فایل تحویل شده برای پروژه:</b> {project['title']}",
            parse_mode="HTML"
        )
    except Exception as e:
        # Try sending as photo if it was originally uploaded as photo
        try:
            await bot.send_photo(
                chat_id=callback.message.chat.id,
                photo=file_id,
                caption=f"📎 <b>فایل تحویل شده برای پروژه:</b> {project['title']}",
                parse_mode="HTML"
            )
        except Exception:
            await callback.message.answer("❌ خطا در ارسال فایل. ممکن است فایل از سرورهای تلگرام حذف شده باشد.")


@router.callback_query(F.data.startswith("feature_project_"))
async def feature_project_handler(callback: CallbackQuery, db_user):
    project_id = int(callback.data.split("_")[2])
    cost = 50000
    if db_user['balance'] < cost:
        return await callback.answer("❌ موجودی کیف پول شما برای ویژه کردن پروژه کافی نیست.", show_alert=True)
    
    # Check if already featured
    project = await db.get_project(project_id)
    if project['is_featured']:
        return await callback.answer("این پروژه قبلاً ویژه شده است.", show_alert=True)
        
    await db.update_balance(db_user['user_id'], -cost, 'fee', f"هزینه ویژه کردن پروژه #{project_id}")
    
    async with aiosqlite.connect(db.DB_PATH) as conn:
        await conn.execute("UPDATE projects SET is_featured = 1 WHERE project_id = ?", (project_id,))
        await conn.commit()
        
    await callback.answer("⭐ پروژه شما با موفقیت ویژه شد و در صدر لیست قرار گرفت!", show_alert=True)
    await view_client_project(callback)


@router.message(F.text == "🛒 خرید خدمات آماده")
async def browse_services(message: Message):
    total = await db.count_all_services()
    if total == 0:
        return await message.answer("📭 در حال حاضر هیچ خدمتی توسط فریلنسرها ثبت نشده است.")
    services = await db.get_all_services(limit=10, offset=0)
    await message.answer(
        f"🛒 <b>فروشگاه خدمات (Gigs)</b>\nدر این بخش می‌توانید خدمات آماده فریلنسرها را مستقیماً بخرید.",
        reply_markup=inline.service_list_keyboard(services, 0, total, is_freelancer=False),
        parse_mode="HTML"
    )

@router.callback_query(F.data.startswith("services_page_"))
async def services_page_handler(callback: CallbackQuery):
    offset = int(callback.data.split("_")[2])
    total = await db.count_all_services()
    services = await db.get_all_services(limit=10, offset=offset)
    await callback.message.edit_reply_markup(reply_markup=inline.service_list_keyboard(services, offset, total, is_freelancer=False))

@router.callback_query(F.data.startswith("view_service_"))
async def view_service_handler(callback: CallbackQuery):
    service_id = int(callback.data.split("_")[2])
    svc = await db.get_service(service_id)
    
    html_content = (
        f"<h1>🛍 {svc['title']}</h1>"
        "<hr/>"
        f"<p>👤 فریلنسر: {svc['freelancer_name']}</p>"
        f"<blockquote>{svc['description']}</blockquote>"
        f"<p>💵 قیمت ثابت: <b>{svc['price']:,} تومان</b></p>"
        f"<p>📅 تعهد تحویل: {svc['delivery_days']} روز</p>"
    )
    
    if svc['file_id']:
        if svc['file_id'].startswith("http"):
            html_content += f"<p>🔗 نمونه‌کار: {svc['file_id']}</p>"
        else:
            try: await callback.message.answer_document(svc['file_id'], caption="فایل پیوست خدمت")
            except: pass
            
    rich_msg = InputRichMessage(html=html_content)
    await callback.message.edit_text(text=None, reply_markup=inline.service_action_keyboard(service_id, False), rich_message=rich_msg)
    await callback.answer()

@router.callback_query(F.data.startswith("buy_service_"))
async def buy_service_handler(callback: CallbackQuery, db_user, bot: Bot):
    service_id = int(callback.data.split("_")[2])
    svc = await db.get_service(service_id)
    
    if db_user['balance'] < svc['price']:
        return await callback.answer("❌ موجودی کیف پول شما برای خرید این خدمت کافی نیست.", show_alert=True)
        
    # Deduct from client
    await db.update_balance(db_user['user_id'], -svc['price'], 'escrow_lock', f"قفل امانت برای خرید خدمت #{service_id}")
    
    # Create project bypass
    async with aiosqlite.connect(db.DB_PATH) as conn:
        cursor = await conn.execute(
            "INSERT INTO projects (client_id, title, description, budget_min, budget_max, deadline_days, status, chosen_freelancer_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
            (db_user['user_id'], f"خرید خدمت: {svc['title']}", svc['description'], svc['price'], svc['price'], svc['delivery_days'], 'in_progress', svc['freelancer_id'])
        )
        pid = cursor.lastrowid
        await conn.execute("INSERT INTO escrow (project_id, amount, status) VALUES (?, ?, ?)", (pid, svc['price'], 'locked'))
        await conn.commit()
        
    await callback.answer("✅ خدمت با موفقیت خریداری شد و پروژه استارت خورد!", show_alert=True)
    await callback.message.answer(f"🎉 پرداخت امن برای پروژه «{svc['title']}» انجام شد.\nمی‌توانید از بخش پروژه‌های من با فریلنسر چت کنید.")
    
    # Notify freelancer
    try:
        await bot.send_message(
            svc['freelancer_id'],
            f"🛒 <b>یک سفارش جدید!</b>\n\nکاربری خدمت «{svc['title']}» شما را خریداری کرد و مبلغ <b>{svc['price']:,} تومان</b> در امانت قفل شد.\nلطفاً فوراً از بخش پروژه‌های من کار را شروع کنید.",
            parse_mode="HTML",
            message_effect_id="5104841245755180586"
        )
    except: pass
