from aiogram import Router, F, Bot
from aiogram.types import Message, CallbackQuery, ReactionTypeEmoji, PollAnswer, Poll, InputRichMessage
from aiogram.fsm.context import FSMContext
from handlers.common import check_channel_membership, get_channel_invite_link

from utils import texts
from utils.logger import get_logger
from states.forms import BidSubmission, ProjectDelivery, FreelancerProfile, ProjectSearch, FreelancerPortfolio, FreelancerRating, BidEditing, BidBoost
from database import db
from keyboards import inline, reply
from config import MAX_FREE_BIDS

logger = get_logger(__name__)
router = Router()

# ── Browse Projects ────────────────────────────────────────────────────

@router.message(F.text == texts.BTN_FIND_PROJECTS)
async def find_projects(message: Message, state: FSMContext):
    await state.clear()
    await message.answer(
        "🔍 <b>جستجو و یافتن پروژه</b>\n\n"
        "می‌توانید تمام پروژه‌های باز را به صورت یکجا مشاهده کرده و یا با استفاده از کلمات کلیدی، پروژه خاصی را جستجو کنید:",
        reply_markup=inline.search_mode_keyboard(),
        parse_mode="HTML"
    )

@router.callback_query(F.data == "projects_list_all")
async def list_all_projects_start(callback: CallbackQuery):
    total = await db.count_open_projects()
    if total == 0:
        return await callback.message.edit_text(
            "😔 در حال حاضر پروژه بازی وجود ندارد.\nبعداً دوباره چک کنید!",
            reply_markup=inline.search_mode_keyboard()
        )
    projects = await db.get_open_projects(limit=8, offset=0)
    
    html_content = (
        "<h1>🔍 پروژه‌های فعال پلتفرم</h1>"
        "<hr/>"
        f"<p>تعداد <b>{total}</b> پروژه فعال و آماده پیشنهاددهی یافت شد.</p>"
        f"<p>صفحه ۱ از {(total - 1) // 8 + 1}</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_list_keyboard(projects, offset=0, total=total),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data == "projects_search_start")
async def start_keyword_search(callback: CallbackQuery, state: FSMContext):
    await state.set_state(ProjectSearch.waiting_for_query)
    await callback.message.answer(
        "🔎 <b>لطفاً کلمه کلیدی مورد نظر خود را برای جستجو وارد کنید:</b> \n"
        "_(مثال: طراحی لوگو، پایتون، ترجمه)_",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(ProjectSearch.waiting_for_query)
async def process_search_query(message: Message, state: FSMContext):
    query = message.text.strip()
    if len(query) < 2:
        return await message.answer("⚠️ کلمه کلیدی وارد شده خیلی کوتاه است. لطفاً حداقل ۲ کاراکتر وارد کنید.")
    
    try: await message.react([ReactionTypeEmoji(emoji="🔍")])
    except: pass
    
    total = await db.count_search_projects(query)
    if total == 0:
        is_admin = message.from_user.id in __import__('config').ADMIN_IDS
        await message.answer(
            f"❌ متأسفانه هیچ پروژه‌ای با کلمه کلیدی <b>«{query}»</b> یافت نشد.",
            reply_markup=reply.freelancer_main_menu(is_admin=is_admin),
            parse_mode="HTML"
        )
        await state.clear()
        return
    
    await state.update_data(search_query=query)
    projects = await db.search_projects(query, limit=8, offset=0)
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer("🔎 در حال جستجو...", reply_markup=reply.freelancer_main_menu(is_admin=is_admin))
    
    html_content = (
        "<h1>🔎 نتایج جستجوی پروژه‌ها</h1>"
        "<hr/>"
        f"<p>نتایج جستجو برای عبارت: <b>«{query}»</b></p>"
        f"<p>تعداد <b>{total}</b> پروژه منطبق یافت شد.</p>"
        f"<p>صفحه ۱ از {(total - 1) // 8 + 1}</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.project_search_list_keyboard(projects, offset=0, total=total)
    )

@router.callback_query(F.data.startswith("psearch_page_"))
async def paginate_search_results(callback: CallbackQuery, state: FSMContext):
    offset = int(callback.data.split("_")[-1])
    data = await state.get_data()
    query = data.get("search_query")
    if not query:
        return await callback.answer("خطایی رخ داد. مجدداً جستجو کنید.", show_alert=True)
    
    total = await db.count_search_projects(query)
    projects = await db.search_projects(query, limit=8, offset=offset)
    
    html_content = (
        "<h1>🔎 نتایج جستجوی پروژه‌ها</h1>"
        "<hr/>"
        f"<p>نتایج جستجو برای عبارت: <b>«{query}»</b></p>"
        f"<p>تعداد <b>{total}</b> پروژه منطبق یافت شد.</p>"
        f"<p>صفحه {offset // 8 + 1} از {(total - 1) // 8 + 1}</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_search_list_keyboard(projects, offset=offset, total=total),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data == "projects_find_back")
async def projects_find_back_handler(callback: CallbackQuery, state: FSMContext):
    await state.clear()
    await callback.message.edit_text(
        "🔍 <b>جستجو و یافتن پروژه</b>\n\n"
        "می‌توانید تمام پروژه‌های باز را به صورت یکجا مشاهده کرده و یا با استفاده از کلمات کلیدی، پروژه خاصی را جستجو کنید:",
        reply_markup=inline.search_mode_keyboard(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data == "projects_list_matching_skills")
async def list_matching_skills_projects(callback: CallbackQuery, state: FSMContext, db_user):
    user_skills = db_user['skills']
    if not user_skills:
        return await callback.answer(
            "⚠️ شما هنوز مهارت‌های خود را در بخش پروفایل تعریف نکرده‌اید.\n"
            "ابتدا به «👤 پروفایل من» بروید و مهارت ثبت کنید.",
            show_alert=True
        )
    
    parsed = db.parse_skills(user_skills)
    total = await db.count_projects_matching_skills(parsed)
    if total == 0:
        return await callback.answer(
            "😔 هیچ پروژه باز منطبق با مهارت‌های فعلی شما یافت نشد.",
            show_alert=True
        )
    
    projects = await db.get_projects_matching_skills(parsed, limit=8, offset=0)
    
    html_content = (
        "<h1>🎯 پروژه‌های منطبق با مهارت‌های شما</h1>"
        "<hr/>"
        f"<p>مهارت‌های شما: <code>{user_skills}</code></p>"
        f"<p>تعداد <b>{total}</b> پروژه منطبق یافت شد.</p>"
        f"<p>صفحه ۱ از {(total - 1) // 8 + 1}</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_skills_list_keyboard(projects, offset=0, total=total),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("pskills_page_"))
async def paginate_skills_matched_projects(callback: CallbackQuery, db_user):
    offset = int(callback.data.split("_")[-1])
    user_skills = db_user['skills']
    if not user_skills:
        return await callback.answer("خطایی رخ داد.", show_alert=True)
        
    parsed = db.parse_skills(user_skills)
    total = await db.count_projects_matching_skills(parsed)
    projects = await db.get_projects_matching_skills(parsed, limit=8, offset=offset)
    
    html_content = (
        "<h1>🎯 پروژه‌های منطبق با مهارت‌های شما</h1>"
        "<hr/>"
        f"<p>مهارت‌های شما: <code>{user_skills}</code></p>"
        f"<p>تعداد <b>{total}</b> پروژه منطبق یافت شد.</p>"
        f"<p>صفحه {offset // 8 + 1} از {(total - 1) // 8 + 1}</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_skills_list_keyboard(projects, offset=offset, total=total),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("projects_page_"))
async def paginate_projects(callback: CallbackQuery):
    offset = int(callback.data.split("_")[-1])
    total = await db.count_open_projects()
    projects = await db.get_open_projects(limit=8, offset=offset)
    
    html_content = (
        "<h1>🔍 پروژه‌های فعال پلتفرم</h1>"
        "<hr/>"
        f"<p>تعداد <b>{total}</b> پروژه فعال و آماده پیشنهاددهی یافت شد.</p>"
        f"<p>صفحه {offset // 8 + 1} از {(total - 1) // 8 + 1}</p>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_list_keyboard(projects, offset=offset, total=total),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("view_project_"))
async def view_project(callback: CallbackQuery):
    parts = callback.data.split("_")
    project_id = int(parts[2])
    
    # Extract source list and page offset to enable returning to the exact page/list
    source = parts[3] if len(parts) > 3 else None
    offset = int(parts[4]) if len(parts) > 4 else 0
    
    back_callback_data = None
    if source == "all":
        back_callback_data = f"projects_page_{offset}"
    elif source == "search":
        back_callback_data = f"psearch_page_{offset}"
    elif source == "skills":
        back_callback_data = f"pskills_page_{offset}"
        
    project = await db.get_project(project_id)
    if not project or project['status'] != 'open':
        return await callback.answer("این پروژه دیگر در دسترس نیست.", show_alert=True)
    bid_count = len(await db.get_bids_for_project(project_id))
    
    # Fetch client rating stats
    client_stats = await db.get_client_rating_stats(project['client_id'])
    if client_stats and client_stats['total_projects'] > 0:
        client_rating_str = f"⭐️ {client_stats['avg_rating']:.1f} ({client_stats['total_projects']} همکاری)"
    else:
        client_rating_str = "جدید (بدون امتیاز)"
        
    project_dict = dict(project)
    status = texts.PROJECT_STATUS_MAP.get(project_dict['status'], project_dict['status'])
    deadline = f"{project_dict['deadline_days']} روز" if project_dict['deadline_days'] else "بدون محدودیت"
    category = project_dict.get('category', 'نامشخص') or 'نامشخص'
    type_str = "تمام‌وقت/استخدامی" if project_dict.get('project_type') == 'full_time' else "پروژه‌ای مقطعی"
    file_icon = "📎 دارد" if project_dict.get('file_id') else "ندارد"
    
    title_prefix = "⭐ <b>پروژه ویژه</b><br/>" if project_dict['is_featured'] else ""
    
    html_content = (
        f"{title_prefix}"
        f"<h1>📌 {project_dict['title']}</h1>"
        "<hr/>"
        f"<blockquote>📋 {project_dict['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_dict['budget_min']:,} تا {project_dict['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>{bid_count} پیشنهاد</td></tr>"
        f"  <tr><th>👔 اعتبار کارفرما</th><td>{client_rating_str}</td></tr>"
        f"  <tr><th>📎 فایل ضمیمه</th><td>{file_icon}</td></tr>"
        "</table>"
    )
    
    rich_msg = InputRichMessage(html=html_content)
    
    await callback.message.edit_text(
        text=None,
        reply_markup=inline.project_action_keyboard(project_id, back_callback_data),
        rich_message=rich_msg
    )
    await callback.answer()

@router.callback_query(F.data.startswith("bid_start_"))
async def start_bidding(callback: CallbackQuery, state: FSMContext, db_user, bot: Bot):
    if not await check_channel_membership(bot, callback.from_user.id):
        channel_id = await db.get_setting('channel_id')
        channel_link = await get_channel_invite_link(bot, channel_id)
        return await callback.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 callback.answer(
            "⚠️ حساب کاربری شما احراز هویت نشده است!\nبرای ثبت پیشنهاد ابتدا اقدام به احراز هویت کنید.",
            show_alert=True
        )
        
    project_id = int(callback.data.split("_")[-1])
    user_id = callback.from_user.id

    # Check if already bid on this project
    existing_bids = await db.get_bids_for_project(project_id)
    already_bid = any(b['freelancer_id'] == user_id for b in existing_bids)
    if already_bid:
        return await callback.answer(texts.BID_ALREADY_SENT, show_alert=True)

    # Check connects balance
    required_connects = await db.get_bid_connects_cost(project_id, user_id)
    connects = db_user['connects'] if 'connects' in dict(db_user) else 5
    if connects < required_connects:
        return await callback.answer(
            f"⚠️ برای ثبت پیشنهاد در این پروژه به {required_connects} کانکت نیاز دارید.\n"
            f"موجودی فعلی شما: {connects} کانکت",
            show_alert=True
        )

    await state.update_data(project_id=project_id)
    await state.set_state(BidSubmission.waiting_for_amount)
    await callback.message.answer(texts.ASK_BID_AMOUNT, reply_markup=reply.cancel_menu(), parse_mode="HTML")
    await callback.answer()

@router.message(BidSubmission.waiting_for_amount)
async def process_bid_amount(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    await state.update_data(amount=int(message.text))
    await state.set_state(BidSubmission.waiting_for_days)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    await message.answer(texts.ASK_BID_DAYS, parse_mode="HTML")

@router.message(BidSubmission.waiting_for_days)
async def process_bid_days(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    await state.update_data(days=int(message.text))
    await state.set_state(BidSubmission.waiting_for_proposal)
    try: await message.react([ReactionTypeEmoji(emoji="⏳")])
    except: pass
    await message.answer(texts.ASK_BID_PROPOSAL, parse_mode="HTML")

@router.message(BidSubmission.waiting_for_proposal)
async def process_bid_proposal(message: Message, state: FSMContext, bot: Bot):
    from handlers.common import contains_forbidden_info
    proposal_text = message.text or ""
    
    if len(proposal_text) < 15:
        return await message.answer("❌ توضیحات پروپوزال خیلی کوتاه است. برای بررسی دقیق‌تر توسط کارفرما، حداقل ۱۵ کاراکتر بنویسید:")
    
    # Check forbidden info only for project-based (non full_time) projects
    data = await state.get_data()
    project_id = data.get('project_id')
    project = await db.get_project(project_id) if project_id else None
    project_type = project.get('project_type', 'project_based') if project else 'project_based'
    
    if project_type != 'full_time':
        from utils.ai_moderation import moderate_text
        is_clean, reason = moderate_text(proposal_text)
        if not is_clean:
            return await message.answer(
                f"⚠️ <b>خطا در ثبت پیشنهاد:</b>\n{reason}\n\n"
                "ارسال هرگونه اطلاعات تماس (شماره تلفن، آیدی تلگرام، ایمیل، آدرس وب‌سایت) یا لینک‌های خارجی در متن پیشنهاد <b>پروژه‌های مقطعی</b> مجاز نیست.\n"
                "💡 در پروژه‌های <b>تمام‌وقت / استخدامی</b> ارسال اطلاعات تماس در پروپوزال آزاد است.\n\n"
                "لطفاً متن پروپوزال خود را اصلاح کرده و بدون اطلاعات تماس مجدداً ارسال کنید:",
                parse_mode="HTML"
            )
        
    await state.update_data(proposal=proposal_text)
    await state.set_state(BidSubmission.waiting_for_file)
    try: await message.react([ReactionTypeEmoji(emoji="✍️")])
    except: pass
    await message.answer(texts.ASK_BID_FILE, reply_markup=reply.skip_or_cancel_menu(), parse_mode="HTML")

@router.message(BidSubmission.waiting_for_file)
async def process_bid_file(message: Message, state: FSMContext, bot: Bot):
    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 and message.text != texts.BTN_BACK:
            return await message.answer("لطفاً یک فایل ارسال کنید یا دکمه «رد کردن» را بزنید.")
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
            
    data = await state.get_data()
    project_id = data['project_id']

    bid_id = await db.add_bid(
        project_id=project_id,
        freelancer_id=message.from_user.id,
        amount=data['amount'],
        delivery_days=data['days'],
        proposal=data['proposal'],
        file_id=data.get('file_id')
    )
    await state.clear()

    if not bid_id:
        return await message.answer(texts.BID_ALREADY_SENT, parse_mode="HTML")

    is_admin_ids = __import__('config').ADMIN_IDS
    is_vip = await db.check_vip_status(message.from_user.id)
    kb = reply.freelancer_main_menu(is_admin=(message.from_user.id in is_admin_ids))
    await message.answer(texts.BID_SUBMITTED, reply_markup=kb, parse_mode="HTML")

    # Suggested post for paid bid promotion
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    boost_builder = InlineKeyboardBuilder()
    boost_builder.button(text="🚀 ارتقای پیشنهاد (بوست بید)", callback_data=f"freelancer_boost_bid_{bid_id}", style="success")
    boost_builder.button(text="📨 پیشنهادهای من", callback_data="my_bids_list", style="primary")
    boost_builder.adjust(1)
    
    await message.answer(
        "💡 <b>پیشنهاد ویژه برای دیده شدن بیشتر!</b>\n\n"
        "می‌توانید با صرف بید اضافی (بوست)، پیشنهاد خود را در صدر لیست کارفرما قرار دهید تا شانس انتخاب شما چندین برابر شود.",
        reply_markup=boost_builder.as_markup(),
        parse_mode="HTML",
        message_effect_id="5107584321108051014" # Thumbs Up!
    )

    # Notify client
    project = await db.get_project(project_id)
    if project:
        freelancer = await db.get_user(message.from_user.id)
        fl_name = freelancer['full_name'] if freelancer else "فریلنسر"
        notify_text = texts.new_bid_notification(
            project_title=project['title'],
            freelancer_name=fl_name,
            amount=data['amount'],
            days=data['days'],
            proposal=data.get('proposal')
        )
        try:
            from aiogram.utils.keyboard import InlineKeyboardBuilder
            builder = InlineKeyboardBuilder()
            builder.button(text="👀 مشاهده پیشنهاد و چت", callback_data=f"bid_detail_{bid_id}")
            builder.button(text="💼 مدیریت پروژه", callback_data=f"client_project_{project_id}_0")
            builder.adjust(1)
            
            await bot.send_message(
                project['client_id'],
                notify_text,
                reply_markup=builder.as_markup(),
                parse_mode="HTML"
            )
        except Exception:
            pass

# ── My Bids ────────────────────────────────────────────────────────────

async def show_freelancer_bids_page(callback_or_msg, user_id: int, offset: int = 0):
    limit = 8
    total = await db.count_freelancer_bids(user_id)
    if total == 0:
        text = "📭 شما هنوز هیچ پیشنهادی ارسال نکرده‌اید."
        if isinstance(callback_or_msg, CallbackQuery):
            await callback_or_msg.message.edit_text(text, parse_mode="HTML")
        else:
            await callback_or_msg.answer(text, parse_mode="HTML")
        return
        
    if offset >= total:
        offset = max(0, ((total - 1) // limit) * limit)
        
    bids = await db.get_freelancer_bids_paginated(user_id, limit=limit, offset=offset)
    kb = inline.my_bids_keyboard(bids, offset=offset, total=total, limit=limit)
    
    # Calculate stats dynamically
    all_bids = await db.get_freelancer_bids(user_id)
    pending_count = sum(1 for b in all_bids if b['status'] == 'pending')
    accepted_count = sum(1 for b in all_bids if b['status'] == 'accepted')
    rejected_count = sum(1 for b in all_bids if b['status'] == 'rejected')
    
    html_content = (
        "<h1>📨 پیشنهادهای شما</h1>"
        "<hr/>"
        "<h3>📊 خلاصه وضعیت پیشنهادها:</h3>"
        "<table bordered>"
        f"  <tr><th>⏳ در انتظار بررسی</th><td><b>{pending_count}</b></td></tr>"
        f"  <tr><th>✅ پذیرفته شده</th><td><b>{accepted_count}</b></td></tr>"
        f"  <tr><th>❌ رد شده</th><td><b>{rejected_count}</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_BIDS)
async def my_bids(message: Message):
    await show_freelancer_bids_page(message, message.from_user.id, offset=0)

@router.callback_query(F.data.startswith("bid_info_"))
async def bid_info(callback: CallbackQuery):
    parts = callback.data.split("_")
    bid_id = int(parts[2])
    offset = int(parts[3]) if len(parts) > 3 else 0
    
    bid = await db.get_bid(bid_id)
    if not bid:
        return await callback.answer("پیشنهاد یافت نشد.", show_alert=True)
    project_row = await db.get_project(bid['project_id'])
    project = dict(project_row) if project_row else None
    status_map = {'pending': '⏳ در انتظار تایید', 'accepted': '✅ پذیرفته شده', 'rejected': '❌ رد شده'}
    status = status_map.get(bid['status'], bid['status'])
    boost_text = ""
    try:
        if bid['boost_bids'] > 0:
            boost_text = f"<tr><th>🚀 وضعیت بوست</th><td>ارتقا یافته (بیدهای مصرفی: {bid['boost_bids']})</td></tr>"
    except:
        pass
        
    html_content = (
        f"<h1>📨 جزئیات پیشنهاد شما برای پروژه</h1>"
        f"<h3>📌 {project['title'] if project else 'نامشخص'}</h3>"
        "<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>{status}</td></tr>"
        f"  {boost_text}"
        "</table>"
    )
    
    if project:
        category = project.get('category', 'نامشخص') or 'نامشخص'
        budget_str = f"{project['budget_min']:,} تا {project['budget_max']:,} تومان"
        deadline_str = f"{project['deadline_days']} روز" if project['deadline_days'] else "بدون محدودیت"
        html_content += (
            "<br/>"
            "<h3>📋 مشخصات پروژه کارفرما:</h3>"
            "<table bordered>"
            f"  <tr><th>▪️ دسته‌بندی</th><td>{category}</td></tr>"
            f"  <tr><th>▪️ بودجه کل</th><td>{budget_str}</td></tr>"
            f"  <tr><th>▪️ مهلت زمان</th><td>{deadline_str}</td></tr>"
            "</table>"
        )
        
    html_content += (
        "<br/>"
        "<h3>📄 توضیحات پروپوزال شما:</h3>"
        f"<blockquote>{bid['proposal']}</blockquote>"
    )
    rich_msg = InputRichMessage(html=html_content)
    
    # Inline buttons for accepted bids (in_progress, delivered, completed)
    kb = None
    if bid['status'] == 'accepted' and project:
        kb = inline.freelancer_accepted_project_keyboard(bid['project_id'], project['status'])
        
    if kb:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder.from_markup(kb)
        builder.button(text="🔙 بازگشت", callback_data=f"my_bids_page_{offset}", style="danger")
        builder.adjust(1)
        kb = builder.as_markup()
    else:
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        if bid['status'] == 'pending':
            builder.button(text="✏️ ویرایش پیشنهاد", callback_data=f"freelancer_edit_bid_{bid_id}", style="primary")
            builder.button(text="🚀 ارتقای پیشنهاد (بوست)", callback_data=f"freelancer_boost_bid_{bid_id}", style="success")
            builder.button(text="❌ پس‌گرفتن پیشنهاد", callback_data=f"freelancer_withdraw_bid_{bid_id}_{offset}", style="danger")
        builder.button(text="🔙 بازگشت", callback_data=f"my_bids_page_{offset}", style="danger")
        builder.adjust(1)
        kb = builder.as_markup()
        
    await callback.message.edit_text(text=None, reply_markup=kb, rich_message=rich_msg)
    await callback.answer()

@router.callback_query(F.data == "my_bids_list")
async def my_bids_list(callback: CallbackQuery):
    await show_freelancer_bids_page(callback, callback.from_user.id, offset=0)
    await callback.answer()

@router.callback_query(F.data.startswith("my_bids_page_"))
async def my_bids_page_handler(callback: CallbackQuery):
    offset = int(callback.data.split("_")[-1])
    await show_freelancer_bids_page(callback, callback.from_user.id, offset=offset)
    await callback.answer()

@router.callback_query(F.data.startswith("freelancer_deliver_"))
async def start_delivery(callback: CallbackQuery, state: FSMContext):
    project_id = int(callback.data.split("_")[-1])
    await state.update_data(project_id=project_id)
    await state.set_state(ProjectDelivery.waiting_for_details)
    await callback.message.answer(
        "📤 <b>تحویل پروژه:</b>\n\nلطفاً توضیحات نهایی، لینک دانلود فایل‌ها یا گزارش انجام کار را ارسال کنید:\n_(حداقل ۱۰ کاراکتر)_",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(ProjectDelivery.waiting_for_details)
async def process_delivery_details(message: Message, state: FSMContext, bot: Bot):
    delivery_text = message.text or message.caption or ""
    if len(delivery_text) < 10:
        return await message.answer("⚠️ توضیحات تحویل خیلی کوتاه است. لطفا توضیحات کامل‌تری بنویسید (حداقل ۱۰ کاراکتر).")
    
    try: await message.react([ReactionTypeEmoji(emoji="📤")])
    except: pass
    
    file_id = None
    if message.document:
        file_id = message.document.file_id
    elif message.photo:
        file_id = message.photo[-1].file_id
    elif message.video:
        file_id = message.video.file_id
    elif message.audio:
        file_id = message.audio.file_id
        
    data = await state.get_data()
    project_id = data['project_id']
    await state.clear()
    
    # Update status in db
    await db.deliver_project(project_id, delivery_text, file_id)
    
    project = await db.get_project(project_id)
    freelancer = await db.get_user(message.from_user.id)
    fl_name = freelancer['full_name'] if freelancer else "فریلنسر"
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer(
        "✅ <b>کار با موفقیت تحویل داده شد و به کارفرما اطلاع‌رسانی گردید.</b>",
        reply_markup=reply.freelancer_main_menu(is_admin=is_admin),
        parse_mode="HTML",
        message_effect_id="5107584321108051014" # Thumbs Up!
    )
    
    # Notify client
    if project:
        notify_text = (
            f"🔔 <b>کارفرما گرامی، پروژه شما تحویل داده شد!</b>\n"
            f"\n"
            f"📌 پروژه: <b>{project['title']}</b>\n"
            f"💻 فریلنسر: <b>{fl_name}</b>\n"
            f"📄 <b>توضیحات تحویل:</b>\n<blockquote>{delivery_text}</blockquote>\n"
            f"\n"
            f"لطفاً کار تحویل داده شده را بررسی نمایید و از دکمه‌های زیر جهت تایید، درخواست اصلاحات یا ارتباط با فریلنسر استفاده کنید:"
        )
        try:
            kb = inline.project_management_keyboard(
                project_id=project_id,
                status='delivered',
                is_featured=project.get('is_featured', 0),
                has_delivery_file=bool(file_id)
            )
            if file_id:
                if message.document:
                    await bot.send_document(project['client_id'], file_id, caption=notify_text, reply_markup=kb, parse_mode="HTML")
                elif message.photo:
                    await bot.send_photo(project['client_id'], file_id, caption=notify_text, reply_markup=kb, parse_mode="HTML")
                elif message.video:
                    await bot.send_video(project['client_id'], file_id, caption=notify_text, reply_markup=kb, parse_mode="HTML")
                elif message.audio:
                    await bot.send_audio(project['client_id'], file_id, caption=notify_text, reply_markup=kb, parse_mode="HTML")
            else:
                await bot.send_message(project['client_id'], notify_text, reply_markup=kb, parse_mode="HTML")
        except Exception:
            pass

# ── VIP & Wallet for freelancers ────────────────────────────────────────

@router.message(F.text == texts.BTN_VIP)
async def show_vip(message: Message, user_id: int = None):
    user_id = user_id or 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)
        vip_text = (
            f"<blockquote expandable>🌟 <b>وضعیت اشتراک فعال:</b> {vip_names.get(vip_level, 'VIP')}\n\n"
            f"📅 <b>تاریخ انقضا:</b> <code>{shamsi_expiry}</code>\n"
            f"⏳ <b>زمان باقی‌مانده:</b> <code>{remaining_days}</code> روز و <code>{remaining_hours}</code> ساعت\n\n"
            f"💎 <b>مزایای فعال شما:</b>\n"
            f"🔸 سهمیه بید ماهانه: <b>{current_allowance} کانکت</b>\n"
            f"🔸 کمیسیون <b>{commissions.get(vip_level, '۰٪')}</b> در پروژه‌های انجام شده (به جای ۱۰٪)\n"
            f"🔸 نمایش <b>اولویت‌دار</b> پیشنهادات شما در صدر لیست کارفرما</blockquote>\n\n"
            f"💡 برای ارتقا یا تمدید اشتراک خود یکی از سطوح زیر را انتخاب کنید:"
        )
        await message.answer(vip_text, reply_markup=inline.vip_tier_keyboard(is_extension=True), parse_mode="HTML")
    else:
        vip_info_text = (
            f"💎 <b>پکیج‌های ویژه VIP ایکس‌لنسر</b>\n\n"
            f"با ارتقای حساب کاربری خود، از رقبایتان متمایز شوید:\n\n"
            f"<blockquote expandable>🥉 <b>سطح برنزی:</b> (شروع از {p1:,} تومان)\n"
            f"🔸 دریافت سهمیه <b>{s_bronze} کانکت</b> هدیه ماهانه\n"
            f"🔸 کمیسیون <b>{c1}٪</b> (به جای ۱۰٪)\n"
            f"🔸 نشان برنزی و نمایش بالاتر پیشنهادات\n\n"
            f"🥈 <b>سطح نقره‌ای:</b> (شروع از {p2:,} تومان)\n"
            f"🔸 دریافت سهمیه <b>{s_silver} کانکت</b> هدیه ماهانه\n"
            f"🔸 کمیسیون <b>{c2}٪</b> (به جای ۱۰٪)\n"
            f"🔸 نشان نقره‌ای و اولویت نمایش پیشنهادات\n\n"
            f"🥇 <b>سطح طلایی:</b> (شروع از {p3:,} تومان)\n"
            f"🔸 دریافت سهمیه <b>{s_gold} کانکت</b> هدیه ماهانه\n"
            f"🔸 کمیسیون <b>{c3}٪ (بدون پورسانت!)</b>\n"
            f"🔸 نشان طلایی و اولویت نمایش حداکثری پیشنهادات</blockquote>\n\n"
            f"💡 لطفاً سطح اشتراک مورد نظر خود را انتخاب کنید:"
        )
        await message.answer(vip_info_text, reply_markup=inline.vip_tier_keyboard(is_extension=False), parse_mode="HTML")

@router.message(F.text == texts.BTN_WALLET)
async def show_wallet(message: Message, db_user):
    user_id = message.from_user.id
    balance = db_user['balance'] or 0
    txs = await db.get_transactions(user_id, limit=5)
    tx_lines = []
    for tx in txs:
        sign = "+" if tx['amount'] > 0 else ""
        tx_lines.append(f"• {sign}{tx['amount']:,} — {tx['description']}")
    tx_text = "\n".join(tx_lines) if tx_lines else texts.NO_TRANSACTIONS
    from keyboards.inline import wallet_keyboard
    await message.answer(
        texts.WALLET_MENU.format(balance=balance, transactions=tx_text),
        reply_markup=wallet_keyboard('freelancer'),
        parse_mode="HTML"
    )

# ── Profile ─────────────────────────────────────────────────────────────

async def refresh_profile_ui(message_or_callback, user_id: int):
    import aiosqlite
    user = await db.get_user(user_id)
    if not user:
        return
        
    vip_level = await db.check_vip_level(user_id)
    vip_names = {0: "👤 کاربر عادی", 1: "🥉 برنزی", 2: "🥈 نقره‌ای", 3: "🥇 طلایی"}
    vip_status = vip_names.get(vip_level, "👤 کاربر عادی")
    
    # Fetch ratings & completed projects count via Scorecard
    scorecard = await db.get_freelancer_scorecard(user_id)
    completed_projects = scorecard['completed_count']
    avg_rating = scorecard['avg_rating']
    on_time_rate = scorecard['on_time_rate']
    response_rate = scorecard['response_rate']

    skills = user['skills'] or "_تعریف نشده (جهت ثبت روی ویرایش مهارت کلیک کنید)_"
    bio = user['bio'] or "_تعریف نشده (جهت ثبت روی ویرایش بیو کلیک کنید)_"
    
    # Calculate Profile Strength (percentage)
    strength = 0
    if user['skills']:
        strength += 30
    if user['bio']:
        strength += 30
    portfolios = await db.get_user_portfolios(user_id)
    if portfolios:
        strength += 20
    is_verified = user['is_verified']
    if is_verified == 1:
        strength += 20
        
    filled = strength // 10
    bar = "█" * filled + "░" * (10 - filled)
    progress_text = f"📈 <b>میزان تکمیل پروفایل شما:</b> {strength}% \n`[{bar}]` \n_(تکمیل پروفایل شانس استخدام شما را تا ۳ برابر افزایش می‌دهد!)_"
    
    v_badges = {0: "❌ احراز هویت نشده", 1: "✅ احراز هویت شده", 2: "⏳ در انتظار تایید مدیریت"}
    v_badge = v_badges.get(is_verified, "❌ احراز هویت نشده")
    if is_verified == 0 and user['verification_rejected_reason']:
        v_badge += f" (علت رد: {user['verification_rejected_reason']})"
    
    connects = user['connects'] if 'connects' in dict(user) else 5
    badges = await db.get_user_badges_emojis(user_id)
    badges_str = badges if badges else "ندارد"
    
    badge = await db.get_freelancer_badge(user_id)
    
    html_content = (
        f"<h1>👤 پروفایل حرفه‌ای فریلنسر</h1>"
        "<hr/>"
        "<table bordered>"
        f"  <tr><th>🎖 وضعیت اشتراک</th><td>{vip_status}</td></tr>"
        f"  <tr><th>🏆 نشان فریلنسر</th><td>{badge}</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>"
        f"  <tr><th>🪙 موجودی کانکت (بید)</th><td>{connects} عدد</td></tr>"
        f"  <tr><th>🏅 مدال‌های شما</th><td>{badges_str}</td></tr>"
        f"  <tr><th>🔐 وضعیت حساب</th><td>{v_badge}</td></tr>"
        "</table>"
        "<br/>"
        "<h3>💻 تخصص‌ها و مهارت‌ها:</h3>"
        f"<blockquote>{skills}</blockquote>"
        "<h3>📄 درباره من:</h3>"
        f"<blockquote>{bio}</blockquote>"
        "<br/>"
        f"<p>{progress_text}</p>"
    )
    
    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.freelancer_profile_keyboard(is_verified=is_verified), 
            rich_message=rich_msg
        )
    else:
        await bot.send_rich_message(
            chat_id=user_id,
            rich_message=rich_msg,
            reply_markup=inline.freelancer_profile_keyboard(is_verified=is_verified)
        )

@router.message(F.text == texts.BTN_PROFILE)
async def show_profile(message: Message, db_user):
    await refresh_profile_ui(message, message.from_user.id)

@router.callback_query(F.data == "profile_edit_skills")
async def profile_edit_skills(callback: CallbackQuery, state: FSMContext):
    await state.set_state(FreelancerProfile.waiting_for_skills)
    await callback.message.answer(
        "✏️ <b>لطفاً لیست مهارت‌های خود را وارد کنید:</b> \n_(مثال: طراحی وب، پایتون، فتوشاپ)_",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data == "profile_edit_bio")
async def profile_edit_bio(callback: CallbackQuery, state: FSMContext):
    await state.set_state(FreelancerProfile.waiting_for_bio)
    await callback.message.answer(
        "✏️ <b>لطفاً بیوگرافی و خلاصه‌ای از سوابق کاری خود را وارد کنید:</b>",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(FreelancerProfile.waiting_for_skills)
async def process_profile_skills(message: Message, state: FSMContext, db_user):
    if len(message.text) < 2:
        return await message.answer("مهارت وارد شده خیلی کوتاه است.")
        
    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.clear()
    await db.update_user_profile(message.from_user.id, skills=message.text)
    try: await message.react([ReactionTypeEmoji(emoji="💻")])
    except: pass
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer("✅ مهارت‌ها با موفقیت بروزرسانی شدند.", reply_markup=reply.freelancer_main_menu(is_admin=is_admin))
    await refresh_profile_ui(message, message.from_user.id)

@router.message(FreelancerProfile.waiting_for_bio)
async def process_profile_bio(message: Message, state: FSMContext, db_user):
    if len(message.text) < 10:
        return await message.answer("بیوگرافی باید حداقل ۱۰ کاراکتر باشد.")
        
    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.clear()
    await db.update_user_profile(message.from_user.id, bio=message.text)
    try: await message.react([ReactionTypeEmoji(emoji="📝")])
    except: pass
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer("✅ بیوگرافی با موفقیت بروزرسانی شد.", reply_markup=reply.freelancer_main_menu(is_admin=is_admin))
    await refresh_profile_ui(message, message.from_user.id)


# ── Portfolio Management ────────────────────────────────────────────────

async def edit_or_reply_text(message_or_callback, text, reply_markup=None, parse_mode="HTML"):
    if isinstance(message_or_callback, CallbackQuery):
        msg = message_or_callback.message
        if msg.photo or msg.document or msg.video or msg.audio:
            try: await msg.delete()
            except: pass
            await msg.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
        else:
            try:
                await msg.edit_text(text, reply_markup=reply_markup, parse_mode=parse_mode)
            except Exception:
                await msg.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
    else:
        await message_or_callback.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)

@router.callback_query(F.data == "profile_portfolio_menu")
async def profile_portfolio_menu_handler(callback: CallbackQuery):
    portfolios = await db.get_user_portfolios(callback.from_user.id)
    text = (
        "📂 <b>مدیریت نمونه‌کارها</b>\n\n"
        f"شما در حال حاضر <b>{len(portfolios)}</b> نمونه‌کار ثبت‌شده دارید.\n"
        "جهت مشاهده جزئیات روی هر کدام کلیک کنید یا با دکمه زیر نمونه‌کار جدید اضافه نمایید:"
    )
    await edit_or_reply_text(callback, text, reply_markup=inline.portfolios_keyboard(portfolios))
    await callback.answer()

@router.callback_query(F.data == "profile_view_back")
async def profile_view_back_handler(callback: CallbackQuery):
    try: await callback.message.delete()
    except: pass
    await refresh_profile_ui(callback, callback.from_user.id)
    await callback.answer()

@router.callback_query(F.data.startswith("portfolio_view_"))
async def view_portfolio_item(callback: CallbackQuery):
    pid = int(callback.data.split("_")[-1])
    portfolios = await db.get_user_portfolios(callback.from_user.id)
    item = next((p for p in portfolios if p['portfolio_id'] == pid), None)
    if not item:
        return await callback.answer("نمونه‌کار یافت نشد.", show_alert=True)
        
    url_str = item['project_url'] if item['project_url'] else "ثبت نشده"
    text = (
        f"📂 <b>نمونه‌کار:</b> {item['title']}\n\n"
        f"📋 <b>توضیحات:</b>\n{item['description']}\n\n"
        f"🔗 <b>لینک پروژه:</b> {url_str}"
    )
    kb = inline.portfolio_detail_keyboard(pid)
    
    file_id = item.get('file_id')
    if file_id:
        try: await callback.message.delete()
        except: pass
        await callback.message.answer_photo(photo=file_id, caption=text, reply_markup=kb, parse_mode="HTML")
    else:
        await edit_or_reply_text(callback, text, reply_markup=kb)
    await callback.answer()

@router.callback_query(F.data.startswith("portfolio_delete_"))
async def delete_portfolio_item_handler(callback: CallbackQuery):
    pid = int(callback.data.split("_")[-1])
    success = await db.delete_portfolio_item(pid, callback.from_user.id)
    if success:
        await callback.answer("✅ نمونه‌کار با موفقیت حذف شد.", show_alert=True)
    else:
        await callback.answer("❌ خطا در حذف نمونه‌کار.", show_alert=True)
    
    portfolios = await db.get_user_portfolios(callback.from_user.id)
    text = f"📂 <b>مدیریت نمونه‌کارها</b>\n\nشما در حال حاضر <b>{len(portfolios)}</b> نمونه‌کار ثبت‌شده دارید."
    await edit_or_reply_text(callback, text, reply_markup=inline.portfolios_keyboard(portfolios))

# ── Add Portfolio FSM ──

@router.callback_query(F.data == "portfolio_add_start")
async def add_portfolio_start(callback: CallbackQuery, state: FSMContext):
    await state.set_state(FreelancerPortfolio.waiting_for_title)
    await callback.message.answer(
        "📝 <b>عنوان نمونه‌کار</b> خود را وارد کنید:\n_(مثال: وب‌سایت شرکتی آریا)_",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(FreelancerPortfolio.waiting_for_title)
async def process_portfolio_title(message: Message, state: FSMContext):
    if len(message.text) < 3:
        return await message.answer("⚠️ عنوان نمونه‌کار خیلی کوتاه است. مجدداً وارد کنید:")
        
    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(FreelancerPortfolio.waiting_for_desc)
    try: await message.react([ReactionTypeEmoji(emoji="📝")])
    except: pass
    await message.answer(
        "📋 <b>توضیحات کامل</b> نمونه‌کار را بنویسید:\n_(نقش شما، فناوری‌ها، نتایج)_",
        parse_mode="HTML"
    )

@router.message(FreelancerPortfolio.waiting_for_desc)
async def process_portfolio_desc(message: Message, state: FSMContext):
    if len(message.text) < 10:
        return await message.answer("⚠️ توضیحات خیلی کوتاه است (حداقل ۱۰ کاراکتر). مجدداً وارد کنید:")
        
    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(FreelancerPortfolio.waiting_for_file)
    try: await message.react([ReactionTypeEmoji(emoji="📋")])
    except: pass
    
    from aiogram.utils.keyboard import ReplyKeyboardBuilder
    builder = ReplyKeyboardBuilder()
    builder.button(text="⏭ بدون تصویر (رد کردن)")
    builder.button(text=texts.BTN_BACK)
    builder.adjust(1)
    
    await message.answer(
        "🖼 <b>تصویر یا فایل نمونه‌کار</b> خود را ارسال کنید (یا دکمه رد کردن را بزنید):",
        reply_markup=builder.as_markup(resize_keyboard=True),
        parse_mode="HTML"
    )

@router.message(FreelancerPortfolio.waiting_for_file)
async def process_portfolio_file(message: Message, state: FSMContext):
    file_id = None
    if message.document:
        file_id = message.document.file_id
    elif message.photo:
        file_id = message.photo[-1].file_id
    elif message.video:
        file_id = message.video.file_id
        
    if message.text and "بدون تصویر" in message.text:
        file_id = None
        
    await state.update_data(file_id=file_id)
    await state.set_state(FreelancerPortfolio.waiting_for_url)
    try: await message.react([ReactionTypeEmoji(emoji="🖼")])
    except: pass
    
    from aiogram.utils.keyboard import ReplyKeyboardBuilder
    builder = ReplyKeyboardBuilder()
    builder.button(text="⏭ بدون لینک (رد کردن)")
    builder.button(text=texts.BTN_BACK)
    builder.adjust(1)
    
    await message.answer(
        "🔗 <b>لینک مرتبط با پروژه</b> یا وب‌سایت دمو را وارد کنید (اختیاری):",
        reply_markup=builder.as_markup(resize_keyboard=True),
        parse_mode="HTML"
    )

@router.message(FreelancerPortfolio.waiting_for_url)
async def process_portfolio_url(message: Message, state: FSMContext, db_user):
    url = ""
    if message.text and "بدون لینک" not in message.text and texts.BTN_BACK not in message.text:
        url = message.text.strip()
        
    data = await state.get_data()
    await state.clear()
    
    await db.add_portfolio_item(
        user_id=message.from_user.id,
        title=data['title'],
        description=data['description'],
        project_url=url,
        file_id=data.get('file_id')
    )
    try: await message.react([ReactionTypeEmoji(emoji="🔗")])
    except: pass
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer(
        "✅ <b>نمونه‌کار جدید با موفقیت به پروفایل شما اضافه شد!</b>",
        reply_markup=reply.freelancer_main_menu(is_admin=is_admin),
        parse_mode="HTML",
        message_effect_id="5046509860389126442" # Celebration effect
    )
    await refresh_profile_ui(message, message.from_user.id)


# ── Freelancer Rating Client ──────────────────────────────────────────

@router.callback_query(F.data.startswith("rateclient_"))
async def process_client_stars_rating(callback: CallbackQuery, state: FSMContext):
    parts = callback.data.split("_")
    stars = int(parts[1])
    project_id = int(parts[2])
    
    await state.update_data(project_id=project_id, rating=stars)
    await state.set_state(FreelancerRating.waiting_for_review)
    
    await callback.message.edit_text(
        f"⭐️ شما امتیاز <b>{stars} ستاره</b> را برای کارفرما انتخاب کردید.\n\n"
        f"📄 در صورت تمایل، نظر خود را درباره کارفرما بنویسید (اختیاری):",
        reply_markup=inline.skip_client_rating_review_keyboard(project_id),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data.startswith("skip_client_review_"))
async def skip_client_review(callback: CallbackQuery, state: FSMContext, db_user):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    await state.clear()
    
    await db.submit_client_rating(project_id, rating, "")
    
    is_admin = callback.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'freelancer'
    kb = reply.freelancer_main_menu(is_admin=is_admin) if role == 'freelancer' else reply.client_main_menu(is_admin=is_admin)
    
    await callback.message.edit_text(
        "✅ <b>امتیاز شما برای کارفرما با موفقیت ثبت شد.</b>\nممنون از همکاری شما!",
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(FreelancerRating.waiting_for_review)
async def process_client_review_text(message: Message, state: FSMContext, db_user):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    await state.clear()
    
    try: await message.react([ReactionTypeEmoji(emoji="⭐")])
    except: pass
    await db.submit_client_rating(project_id, rating, message.text)
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'freelancer'
    kb = reply.freelancer_main_menu(is_admin=is_admin) if role == 'freelancer' else reply.client_main_menu(is_admin=is_admin)
    
    await message.answer(
        "✅ <b>امتیاز و نظر شما برای کارفرما ثبت شد.</b>\nممنون از همکاری شما!",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014"
    )


# ── Edit & Withdraw Bid FSM ───────────────────────────────────────────

@router.callback_query(F.data.startswith("freelancer_withdraw_bid_"))
async def callback_withdraw_bid(callback: CallbackQuery):
    parts = callback.data.split("_")
    bid_id = int(parts[3])
    offset = int(parts[4]) if len(parts) > 4 else 0
    
    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'])
    import html
    project_title = html.escape(project['title']) if project else "نامشخص"
    
    confirm_text = (
        f"⚠️ <b>تایید پس‌گرفتن پیشنهاد</b>\n\n"
        f"آیا مطمئنید که می‌خواهید پیشنهاد خود را برای پروژه «<b>{project_title}</b>» پس بگیرید؟\n\n"
        f"<i>پس از لغو، متن پروپوزال شما حذف شده و بیدهای مصرف شده (در صورت عادی بودن اکانت) مسترد خواهند شد.</i>"
    )
    
    yes_data = f"freelancer_confirm_withdraw_{bid_id}_{offset}"
    no_data = f"bid_info_{bid_id}_{offset}"
    
    await callback.message.edit_text(
        confirm_text,
        reply_markup=inline.confirm_keyboard(yes_data=yes_data, no_data=no_data),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data.startswith("freelancer_confirm_withdraw_"))
async def callback_confirm_withdraw_bid(callback: CallbackQuery):
    parts = callback.data.split("_")
    bid_id = int(parts[3])
    offset = int(parts[4]) if len(parts) > 4 else 0
    
    success = await db.withdraw_bid(bid_id, callback.from_user.id)
    if not success:
        return await callback.answer("خطا در پس‌گرفتن پیشنهاد. شاید پیشنهاد دیگر در انتظار تایید نیست.", show_alert=True)
        
    await callback.answer("✅ پیشنهاد با موفقیت پس گرفته و لغو شد.", show_alert=True)
    await show_freelancer_bids_page(callback, callback.from_user.id, offset=offset)

@router.callback_query(F.data.startswith("freelancer_edit_bid_"))
async def callback_start_edit_bid(callback: CallbackQuery, state: FSMContext):
    bid_id = int(callback.data.split("_")[-1])
    bid = await db.get_bid(bid_id)
    if not bid or bid['status'] != 'pending':
        return await callback.answer("این پیشنهاد قابل ویرایش نیست.", show_alert=True)
        
    await state.update_data(edit_bid_id=bid_id)
    await state.set_state(BidEditing.waiting_for_amount)
    
    await callback.message.answer(
        f"✏ <b>ویرایش پیشنهاد</b>\n\n"
        f"مبلغ پیشنهادی قبلی: {bid['amount']:,} تومان\n"
        f"زمان تحویل قبلی: {bid['delivery_days']} روز\n\n"
        f"مبلغ پیشنهادی جدید (تومان) را وارد کنید:",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(BidEditing.waiting_for_amount)
async def process_edit_bid_amount(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    await state.update_data(amount=int(message.text))
    await state.set_state(BidEditing.waiting_for_days)
    try: await message.react([ReactionTypeEmoji(emoji="💰")])
    except: pass
    await message.answer("📅 زمان تحویل جدید را وارد کنید (تعداد روز):")

@router.message(BidEditing.waiting_for_days)
async def process_edit_bid_days(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer(texts.ERROR_NUMBER_EXPECTED, parse_mode="HTML")
    await state.update_data(days=int(message.text))
    await state.set_state(BidEditing.waiting_for_proposal)
    try: await message.react([ReactionTypeEmoji(emoji="⏳")])
    except: pass
    await message.answer("📄 معرفی و توضیحات جدید خود را بنویسید (حداقل ۱۰ کاراکتر):")

@router.message(BidEditing.waiting_for_proposal)
async def process_edit_bid_proposal(message: Message, state: FSMContext, db_user):
    if len(message.text) < 10:
        return await message.answer("توضیحات خیلی کوتاه است. حداقل ۱۰ کاراکتر وارد کنید.")
        
    data = await state.get_data()
    bid_id = data['edit_bid_id']
    await state.clear()
    
    await db.update_bid(
        bid_id=bid_id,
        amount=data['amount'],
        delivery_days=data['days'],
        proposal=message.text
    )
    
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    role = db_user['role'] or 'freelancer'
    kb = reply.freelancer_main_menu(is_admin=is_admin) if role == 'freelancer' else reply.client_main_menu(is_admin=is_admin)
    
    try: await message.react([ReactionTypeEmoji(emoji="👍")])
    except: pass
    await message.answer(
        "✅ <b>پیشنهاد شما با موفقیت ویرایش و بروزرسانی شد!</b>",
        reply_markup=kb,
        parse_mode="HTML",
        message_effect_id="5107584321108051014"
    )

# ── Connects (Bids) Packages Shop ───────────────────────────────────────

@router.callback_query(F.data == "wallet_buy_bids_menu")
async def buy_bids_menu(callback: CallbackQuery):
    user_id = callback.from_user.id
    user = await db.get_user(user_id)
    if not user:
        return await callback.answer("کاربر یافت نشد.", show_alert=True)
    
    connects = user['connects'] if 'connects' in dict(user) else 5
    balance = user['balance'] or 0
    
    price_10 = await db.get_setting_int("bid_pack_10_price", 10000)
    price_30 = await db.get_setting_int("bid_pack_30_price", 25000)
    price_100 = await db.get_setting_int("bid_pack_100_price", 70000)
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    builder.button(text=f"🪙 ۱۰ بید ({price_10:,} تومان)", callback_data="buy_bid_pack_10")
    builder.button(text=f"🪙 ۳۰ بید ({price_30:,} تومان)", callback_data="buy_bid_pack_30")
    builder.button(text=f"🪙 ۱۰۰ بید ({price_100:,} تومان)", callback_data="buy_bid_pack_100")
    builder.button(text="🔙 بازگشت به کیف پول", callback_data="wallet_menu_back_freelancer")
    builder.adjust(1)
    
    text = (
        "🛒 <b>فروشگاه بسته‌های بید (پیشنهاد)</b>\n\n"
        "با خرید بسته‌های بید، می‌توانید تعداد بیدهای (کانکت) موجودی خود را افزایش داده و پیشنهادات خود را در پروژه‌های بیشتری ثبت یا ارتقا دهید.\n\n"
        f"🪙 موجودی بیدهای شما: <b>{connects}</b> بید\n"
        f"💳 موجودی کیف پول: <b>{balance:,}</b> تومان\n\n"
        "لطفاً بسته مورد نظر خود را انتخاب کنید:"
    )
    await callback.message.edit_text(text, reply_markup=builder.as_markup(), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("buy_bid_pack_"))
async def process_buy_bid_pack(callback: CallbackQuery):
    pack_amount = int(callback.data.split("_")[-1])
    user_id = callback.from_user.id
    user = await db.get_user(user_id)
    if not user:
        return await callback.answer("کاربر یافت نشد.", show_alert=True)
        
    price_key = f"bid_pack_{pack_amount}_price"
    default_price = 10000 if pack_amount == 10 else (25000 if pack_amount == 30 else 70000)
    price = await db.get_setting_int(price_key, default_price)
    
    balance = user['balance'] or 0
    if balance < price:
        return await callback.answer(
            f"❌ موجودی کیف پول شما ({balance:,} تومان) برای خرید این بسته کافی نیست.\n"
            f"لطفاً ابتدا کیف پول خود را شارژ کنید.",
            show_alert=True
        )
        
    # Deduct balance and add connects
    await db.update_balance(user_id, -price, "buy_bids", f"خرید بسته {pack_amount} عددی بید (کانکت)")
    await db.update_connects(user_id, pack_amount, "buy_bids", f"دریافت {pack_amount} بید بابت خرید بسته")
    
    await callback.answer(f"🎉 خرید موفقیت‌آمیز بود! {pack_amount} بید به حساب شما افزوده شد.", show_alert=True)
    
    # Refresh wallet view
    user = await db.get_user(user_id)
    txs = await db.get_transactions(user_id, limit=5)
    tx_lines = []
    for tx in txs:
        sign = "+" if tx['amount'] > 0 else ""
        tx_lines.append(f"• {sign}{tx['amount']:,} — {tx['description']}")
    tx_text = "\n".join(tx_lines) if tx_lines else texts.NO_TRANSACTIONS
    from keyboards.inline import wallet_keyboard
    await callback.message.edit_text(
        texts.WALLET_MENU.format(balance=user['balance'] or 0, transactions=tx_text),
        reply_markup=wallet_keyboard('freelancer'),
        parse_mode="HTML"
    )

@router.callback_query(F.data == "wallet_menu_back_freelancer")
async def wallet_menu_back_freelancer_handler(callback: CallbackQuery):
    user_id = callback.from_user.id
    user = await db.get_user(user_id)
    balance = user['balance'] or 0
    txs = await db.get_transactions(user_id, limit=5)
    tx_lines = []
    for tx in txs:
        sign = "+" if tx['amount'] > 0 else ""
        tx_lines.append(f"• {sign}{tx['amount']:,} — {tx['description']}")
    tx_text = "\n".join(tx_lines) if tx_lines else texts.NO_TRANSACTIONS
    from keyboards.inline import wallet_keyboard
    await callback.message.edit_text(
        texts.WALLET_MENU.format(balance=balance, transactions=tx_text),
        reply_markup=wallet_keyboard('freelancer'),
        parse_mode="HTML"
    )
    await callback.answer()

# ── Proposal Boosting ───────────────────────────────────────────────────

@router.callback_query(F.data.startswith("freelancer_boost_bid_"))
async def start_boost_bid(callback: CallbackQuery, state: FSMContext):
    bid_id = int(callback.data.split("_")[-1])
    bid = await db.get_bid(bid_id)
    if not bid or bid['status'] != 'pending':
        return await callback.answer("این پیشنهاد قابل ارتقا نیست.", show_alert=True)
        
    user_id = callback.from_user.id
    user = await db.get_user(user_id)
    vip_level = await db.check_vip_level(user_id)
    connects = user['connects'] if 'connects' in dict(user) else 5
    
    if connects < 1:
        return await callback.answer("⚠️ موجودی کانکت شما کافی نیست! لطفاً ابتدا کانکت خریداری کنید.", show_alert=True)
        
    await state.update_data(boost_bid_id=bid_id)
    await state.set_state(BidBoost.waiting_for_bids)
    
    vip_notes = {
        0: "💡 هر کانکت اضافه، ۱ امتیاز بوست به پیشنهاد شما اضافه می‌کند.",
        1: "🥉 به عنوان کاربر برنزی، از <b>۱۰٪ تخفیف</b> در مصرف کانکت برای بوست بهره‌مند می‌شوید.",
        2: "🥈 به عنوان کاربر نقره‌ای، از <b>۲۰٪ تخفیف</b> در مصرف کانکت برای بوست بهره‌مند می‌شوید.",
        3: "🥇 به عنوان کاربر طلایی، از <b>۳۰٪ تخفیف</b> در مصرف کانکت برای بوست بهره‌مند می‌شوید."
    }
    vip_note = vip_notes.get(vip_level, "") + f"\n🪙 موجودی فعلی شما: <b>{connects}</b> کانکت"
    
    await callback.message.answer(
        f"🚀 <b>ارتقای پیشنهاد (بوست) برای پروژه</b>\n\n"
        f"با صرف بید بیشتر، پیشنهاد شما در لیست کارفرما بالاتر نمایش داده خواهد شد و با آیکون موشک متمایز می‌شود.\n\n"
        f"{vip_note}\n\n"
        f"تعداد بیدهای اضافی مورد نظر برای ارتقای پیشنهاد را به عدد وارد کنید:",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(BidBoost.waiting_for_bids)
async def process_boost_bids_amount(message: Message, state: FSMContext, db_user):
    if not message.text.isdigit():
        return await message.answer("⚠️ لطفا یک عدد صحیح و مثبت وارد کنید.")
        
    amount = int(message.text)
    if amount <= 0:
        return await message.answer("⚠️ مقدار وارد شده باید بیشتر از صفر باشد.")
        
    data = await state.get_data()
    bid_id = data.get("boost_bid_id")
    if not bid_id:
        await state.clear()
        return await message.answer("خطایی رخ داد. لطفاً مجدداً تلاش کنید.")
        
    user_id = message.from_user.id
    user = await db.get_user(user_id)
    vip_level = await db.check_vip_level(user_id)
    
    # Calculate discount
    if vip_level == 1:
        cost = max(1, int(amount * 0.90))
    elif vip_level == 2:
        cost = max(1, int(amount * 0.80))
    elif vip_level == 3:
        cost = max(1, int(amount * 0.70))
    else:
        cost = amount
        
    connects = user['connects'] if 'connects' in dict(user) else 5
    if connects < cost:
        return await message.answer(
            f"❌ موجودی کانکت‌های شما ({connects} کانکت) برای این میزان ارتقا کافی نیست (هزینه با احتساب تخفیف: {cost} کانکت).\n"
            f"لطفاً مقدار کمتری وارد کنید یا بسته کانکت خریداری کنید."
        )
            
    # Boost proposal
    success = await db.boost_bid(bid_id, user_id, amount)
    await state.clear()
    
    is_admin = user_id in __import__('config').ADMIN_IDS
    kb = reply.freelancer_main_menu(is_admin=is_admin)
    
    if success:
        try: await message.react([ReactionTypeEmoji(emoji="🔥")])
        except: pass
        await message.answer(
            f"🚀 <b>پیشنهاد شما با موفقیت به میزان {amount} بید ارتقا یافت!</b>",
            reply_markup=kb,
            parse_mode="HTML",
            message_effect_id="5104858069654301597"
        )
    else:
        await message.answer(
            "❌ خطا در ارتقای پیشنهاد. شاید پیشنهاد دیگر فعال نباشد یا موجودی شما کافی نباشد.",
            reply_markup=kb
        )

@router.callback_query(F.data.startswith("dispute_project_"))
async def freelancer_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.startswith("async_chat_client_"))
async def async_chat_client_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:
        return await callback.answer("پروژه یافت نشد.", show_alert=True)
        
    freelancer_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')
    freelancer_token = hashlib.sha256(f"Xlancer_Secret_{project_id}_{freelancer_id}".encode()).hexdigest()
    
    kb = inline.InlineKeyboardBuilder()
    kb.button(text="ورود به چت‌روم پروژه 💬", web_app=WebAppInfo(url=f"{bot_domain}/chat?project_id={project_id}&user_id={freelancer_id}&token={freelancer_token}"))
    
    await callback.message.answer(
        f"💬 <b>ارسال پیام به کارفرما در چت‌روم اختصاصی</b>\nلطفاً از طریق دکمه زیر وارد چت‌روم شوید:",
        reply_markup=kb.as_markup(),
        parse_mode="HTML"
    )
    await callback.answer()

# ── Skill Quizzes ───────────────────────────────────────────────────────

@router.callback_query(F.data == "profile_quiz_menu")
async def show_quiz_menu(callback: CallbackQuery):
    quizzes = await db.get_skill_quizzes()
    attempts = await db.get_user_attempts(callback.from_user.id)
    passed_quizzes = {a['quiz_id']: a['score'] for a in attempts if a['passed'] == 1}
    failed_quizzes = {a['quiz_id']: a['score'] for a in attempts if a['passed'] == 0}
    
    text = (
        "🎓 <b>بخش آزمون‌های مهارت پلتفرم ایکس‌لنسر</b>\n\n"
        "با شرکت در این آزمون‌های کوتاه ۵ سوالی، نشان مهارتی مربوطه (مانند 🐍، 🎨، 📝) را در پروفایل خود دریافت کنید. "
        "برای قبولی باید حداقل به <b>۴ سوال</b> پاسخ صحیح دهید.\n\n"
        "📌 <b>لیست آزمون‌های موجود:</b>\n"
    )
    
    from aiogram.utils.keyboard import InlineKeyboardBuilder
    builder = InlineKeyboardBuilder()
    
    for q in quizzes:
        status_str = "⚪️ شرکت نکرده"
        if q['quiz_id'] in passed_quizzes:
            status_str = f"✅ قبول شده (امتیاز: {passed_quizzes[q['quiz_id']]}/۵)"
        elif q['quiz_id'] in failed_quizzes:
            status_str = f"❌ مردود شده (امتیاز: {failed_quizzes[q['quiz_id']]}/۵) - امکان شرکت مجدد"
            
        text += f"• <b>آزمون {q['title']}:</b> {status_str}\n"
        
        if q['quiz_id'] not in passed_quizzes:
            builder.button(text=f"✍️ شروع آزمون {q['title']}", callback_data=f"start_quiz_{q['quiz_id']}")
            
    builder.button(text="🔙 بازگشت به پروفایل", callback_data="profile_view_back", 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("start_quiz_"))
async def start_quiz_handler(callback: CallbackQuery, state: FSMContext, bot: Bot):
    quiz_id = int(callback.data.split("_")[-1])
    quizzes = await db.get_skill_quizzes()
    quiz = next((q for q in quizzes if q['quiz_id'] == quiz_id), None)
    if not quiz:
        return await callback.answer("آزمون یافت نشد.", show_alert=True)
        
    import json
    questions = json.loads(quiz['questions_json'])
    user_id = callback.from_user.id
    
    # Clean up any existing active polls
    await db.clear_user_active_polls(user_id)
    await state.clear()
    
    await callback.message.answer(
        f"🎓 <b>آزمون مهارتی «{quiz['title']}» آغاز شد.</b>\n\n"
        "لطفاً به ۵ سوالی که در قالب نظرسنجی (Quiz Poll) برای شما ارسال می‌شود پاسخ دهید:",
        parse_mode="HTML"
    )
    
    # Send first question
    poll_msg = await bot.send_poll(
        chat_id=user_id,
        question=f"سوال ۱ از ۵: {questions[0]['q']}",
        options=questions[0]['options'],
        type="quiz",
        correct_option_id=questions[0]['answer'],
        is_anonymous=False,
        open_period=10
    )
    
    # Save active poll mapping in DB
    await db.save_active_poll(
        poll_id=poll_msg.poll.id,
        user_id=user_id,
        quiz_id=quiz_id,
        questions_json=quiz['questions_json'],
        current_index=0,
        score=0,
        quiz_title=quiz['title']
    )
    await callback.answer()

@router.poll_answer()
async def handle_quiz_poll_answer(poll_answer: PollAnswer, bot: Bot):
    poll_id = poll_answer.poll_id
    user_id = poll_answer.user.id
    
    # Fetch active poll from DB
    active_poll = await db.get_active_poll(poll_id)
    if not active_poll:
        return
        
    import json
    quiz_id = active_poll['quiz_id']
    questions = json.loads(active_poll['questions_json'])
    current_index = active_poll['current_index']
    score = active_poll['score']
    quiz_title = active_poll['quiz_title']
    
    # Verify option
    chosen_option = poll_answer.option_ids[0]
    correct_option = questions[current_index]['answer']
    
    if chosen_option == correct_option:
        score += 1
        
    # Delete old poll mapping
    await db.delete_active_poll(poll_id)
    
    # Send next or complete
    next_index = current_index + 1
    if next_index < len(questions):
        next_q = questions[next_index]
        poll_msg = await bot.send_poll(
            chat_id=user_id,
            question=f"سوال {next_index + 1} از ۵: {next_q['q']}",
            options=next_q['options'],
            type="quiz",
            correct_option_id=next_q['answer'],
            is_anonymous=False,
            open_period=10
        )
        await db.save_active_poll(
            poll_id=poll_msg.poll.id,
            user_id=user_id,
            quiz_id=quiz_id,
            questions_json=active_poll['questions_json'],
            current_index=next_index,
            score=score,
            quiz_title=quiz_title
        )
    else:
        # Quiz complete
        passed = 1 if score >= 4 else 0
        await db.save_quiz_attempt(user_id, quiz_id, score, passed)
        
        from keyboards import inline
        from aiogram.utils.keyboard import InlineKeyboardBuilder
        builder = InlineKeyboardBuilder()
        builder.button(text="🎓 بازگشت به آزمون‌ها", callback_data="profile_quiz_menu", style="success")
        builder.button(text="🔙 بازگشت به منو", callback_data="role_freelancer", style="danger")
        builder.adjust(1)
        
        if passed:
            msg = (
                f"🎉 <b>تبریک فراوان! شما در آزمون مهارتی «{quiz_title}» قبول شدید!</b>\n\n"
                f"📊 امتیاز نهایی شما: <b>{score} از ۵</b>\n"
                f"🏅 نشان مهارتی متناظر با موفقیت فعال شد و در پروفایل و پیشنهادات شما نمایش داده می‌شود."
            )
        else:
            msg = (
                f"❌ <b>متأسفانه موفق به کسب حدنصاب قبولی در آزمون «{quiz_title}» نشدید.</b>\n\n"
                f"📊 امتیاز نهایی شما: <b>{score} از ۵</b>\n"
                f"💡 جهت قبولی باید حداقل به ۴ سوال پاسخ صحیح دهید. می‌توانید هر زمان تمایل داشتید مجدداً تلاش کنید."
            )
            
        await bot.send_message(user_id, msg, reply_markup=builder.as_markup(), parse_mode="HTML")

# ── Freelancer Rating Client ──────────────────────────────────────────

@router.callback_query(F.data.startswith("rate_client_start_"))
async def start_client_rating(callback: CallbackQuery, state: FSMContext):
    project_id = int(callback.data.split("_")[-1])
    await state.update_data(project_id=project_id)
    await state.set_state(FreelancerRating.waiting_for_stars)
    await callback.message.edit_reply_markup(reply_markup=inline.stars_rating_client_keyboard(project_id))
    await callback.answer()

@router.callback_query(F.data.startswith("rate_client_"))
async def process_client_stars_rating(callback: CallbackQuery, state: FSMContext):
    parts = callback.data.split("_")
    stars = int(parts[2])
    project_id = int(parts[3])

    await state.update_data(rating=stars)
    await state.set_state(FreelancerRating.waiting_for_review)

    await callback.message.edit_text(
        f"⭐ شما امتیاز <b>{stars} ستاره</b> را به کارفرما دادید.\n\n"
        f"📄 لطفا نظر یا توضیحات خود را درباره همکاری با این کارفرما بنویسید (اختیاری):",
        reply_markup=inline.skip_rating_client_keyboard(project_id),
        parse_mode="HTML"
    )
    await callback.answer()

@router.callback_query(F.data.startswith("skip_review_client_"))
async def skip_client_review(callback: CallbackQuery, state: FSMContext, bot: Bot):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    await state.clear()

    await db.submit_rating_to_client(project_id, rating, "")
    
    await callback.message.edit_text(
        "✅ <b>امتیاز شما به کارفرما با موفقیت و بدون نظر متنی ثبت شد.</b>\nممنون از همکاری شما!",
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(FreelancerRating.waiting_for_review)
async def process_client_review_text(message: Message, state: FSMContext, bot: Bot):
    data = await state.get_data()
    project_id = data.get('project_id')
    rating = data.get('rating', 5)
    review_text = message.text

    await state.clear()

    await db.submit_rating_to_client(project_id, rating, review_text)

    await message.answer(
        "✅ <b>نظر و امتیاز شما برای کارفرما با موفقیت ثبت شد.</b>\nممنون از همراهی شما!",
        parse_mode="HTML"
    )


@router.callback_query(F.data == "projects_search_budget")
async def start_budget_search(callback: CallbackQuery, state: FSMContext):
    await state.set_state(ProjectSearch.waiting_for_budget)
    await callback.message.answer(
        "💵 <b>لطفاً حداقل بودجه مورد نظر خود را وارد کنید (به تومان):</b> \n"
        "_(مثلاً 1000000)_",
        reply_markup=reply.cancel_menu(),
        parse_mode="HTML"
    )
    await callback.answer()

@router.message(ProjectSearch.waiting_for_budget)
async def process_budget_query(message: Message, state: FSMContext):
    if not message.text.isdigit():
        return await message.answer("❌ لطفاً فقط عدد وارد کنید.")
    
    amount = int(message.text)
    try: await message.react([ReactionTypeEmoji(emoji="🔍")])
    except: pass
    
    query_str = f"budget_min:{amount}"
    total = await db.count_open_projects(query=query_str)
    if total == 0:
        return await message.answer(
            f"😔 پروژه‌ای با بودجه بالای {amount:,} تومان یافت نشد.",
            reply_markup=inline.search_mode_keyboard()
        )
        
    projects = await db.get_open_projects(limit=8, offset=0, query=query_str)
    await message.answer(
        f"🔍 <b>{total} پروژه با بودجه بالای {amount:,} تومان یافت شد:</b>",
        reply_markup=inline.project_search_list_keyboard(projects, offset=0, total=total),
        parse_mode="HTML"
    )
    await state.clear()


from states.forms import ServiceCreation

@router.message(F.text == "🛍 مدیریت خدمات من")
async def my_services(message: Message):
    services = await db.get_freelancer_services(message.from_user.id)
    await message.answer(
        "🛍 <b>خدمات آماده شما (Gigs)</b>\n\nدر این بخش می‌توانید پکیج‌های خدماتی خود را بسازید تا کارفرمایان مستقیماً آن‌ها را بخرند.",
        reply_markup=inline.service_list_keyboard(services, 0, len(services), is_freelancer=True),
        parse_mode="HTML"
    )

@router.callback_query(F.data == "create_service_start")
async def create_service_start(callback: CallbackQuery, state: FSMContext):
    await state.set_state(ServiceCreation.waiting_for_title)
    await callback.message.answer("📝 عنوان خدمت شما چیست؟ (مثال: طراحی لوگو در ۲ روز)", reply_markup=reply.cancel_menu())
    await callback.answer()

@router.message(ServiceCreation.waiting_for_title)
async def process_svc_title(message: Message, state: FSMContext):
    if len(message.text) < 5:
        return await message.answer("⚠️ عنوان خدمت خیلی کوتاه است (حداقل ۵ کاراکتر). مجدداً وارد کنید:")
        
    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(ServiceCreation.waiting_for_desc)
    await message.answer("📋 توضیحات کامل خدمت خود را بنویسید (چه چیزی تحویل می‌دهید؟):")

@router.message(ServiceCreation.waiting_for_desc)
async def process_svc_desc(message: Message, state: FSMContext):
    if len(message.text) < 15:
        return await message.answer("⚠️ توضیحات خدمت خیلی کوتاه است (حداقل ۱۵ کاراکتر). مجدداً وارد کنید:")
        
    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(desc=message.text)
    await state.set_state(ServiceCreation.waiting_for_price)
    await message.answer("💵 قیمت نهایی این خدمت چقدر است؟ (به تومان - فقط عدد):")

@router.message(ServiceCreation.waiting_for_price)
async def process_svc_price(message: Message, state: FSMContext):
    if not message.text.isdigit(): return await message.answer("فقط عدد وارد کنید.")
    await state.update_data(price=int(message.text))
    await state.set_state(ServiceCreation.waiting_for_days)
    await message.answer("📅 تحویل این خدمت چند روز زمان می‌برد؟ (فقط عدد):")

@router.message(ServiceCreation.waiting_for_days)
async def process_svc_days(message: Message, state: FSMContext):
    if not message.text.isdigit(): return await message.answer("فقط عدد وارد کنید.")
    await state.update_data(days=int(message.text))
    await state.set_state(ServiceCreation.waiting_for_file)
    await message.answer("📎 در صورت تمایل یک عکس یا فایل نمونه کار ارسال کنید (اختیاری):", reply_markup=reply.skip_or_cancel_menu())

@router.message(ServiceCreation.waiting_for_file)
async def process_svc_file(message: Message, state: FSMContext, bot: Bot):
    file_id = None
    if message.text and message.text.startswith('http'): file_id = message.text
    elif message.document: file_id = message.document.file_id
    elif message.photo: file_id = message.photo[-1].file_id
    
    data = await state.get_data()
    service_id = await db.create_service(message.from_user.id, data['title'], data['desc'], data['price'], data['days'], file_id)
    await state.clear()
    
    from handlers.common import send_main_menu
    user = await db.get_user(message.from_user.id)
    is_admin = message.from_user.id in __import__('config').ADMIN_IDS
    await message.answer("✅ خدمت شما با موفقیت ثبت شد و پس از بررسی و تایید مدیریت در فروشگاه قرار خواهد گرفت.", reply_markup=reply.freelancer_main_menu(is_admin=is_admin))
    await send_main_menu(message, user)

    # Route to admin_group_services
    recipients = await db.get_admin_recipients('services')
    admin_msg = (
        f"🛍 <b>درخواست تایید خدمت جدید (Gigs)</b>\n\n"
        f"🆔 شناسه خدمت: <code>#{service_id}</code>\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>{data['title']}</b>\n"
        f"💵 قیمت: <b>{data['price']:,} تومان</b>\n"
        f"📅 تحویل: <b>{data['days']} روز</b>\n\n"
        f"📋 توضیحات:\n{data['desc']}"
    )
    admin_kb = inline.admin_service_approval_keyboard(service_id)
    
    for r_id in recipients:
        try:
            if file_id:
                if file_id.startswith('http'):
                    await bot.send_message(r_id, admin_msg + f"\n\n🔗 لینک فایل: {file_id}", reply_markup=admin_kb, parse_mode="HTML")
                else:
                    try:
                        await bot.send_photo(r_id, file_id, caption=admin_msg[:1024], reply_markup=admin_kb, parse_mode="HTML")
                    except:
                        await bot.send_message(r_id, admin_msg, reply_markup=admin_kb, parse_mode="HTML")
            else:
                await bot.send_message(r_id, admin_msg, reply_markup=admin_kb, parse_mode="HTML")
        except:
            pass

@router.callback_query(F.data.startswith("edit_service_"))
async def edit_service_handler(callback: CallbackQuery):
    service_id = int(callback.data.split("_")[2])
    svc = await db.get_service(service_id)
    text = f"🛍 <b>{svc['title']}</b>\n\n{svc['description']}\n\n💵 قیمت: {svc['price']:,} تومان\n📅 زمان تحویل: {svc['delivery_days']} روز"
    await callback.message.answer(text, reply_markup=inline.service_action_keyboard(service_id, True), parse_mode="HTML")
    await callback.answer()

@router.callback_query(F.data.startswith("delete_service_"))
async def delete_service_handler(callback: CallbackQuery):
    service_id = int(callback.data.split("_")[2])
    await db.delete_service(service_id, callback.fromuser.id) if hasattr(callback, 'fromuser') else await db.delete_service(service_id, callback.from_user.id)
    await callback.message.edit_text("🗑 خدمت شما با موفقیت حذف شد.")
    await callback.answer()
