# database/queries.py
from datetime import datetime, timedelta, date
from typing import Optional, List
from sqlalchemy import or_
from sqlalchemy.orm import selectinload
from sqlalchemy import select, update, delete
from database.db import async_session
from database.models import (
    Plan,
    PlanCategory,
    User,
    Order,
    PaymentStatus,
    LinkPool,
    PaymentConfig,
    LinkType,           # ← برای نوع لینک
)



async def get_links_with_capacity():
    async with async_session() as session:
        result = await session.execute(
            select(LinkPool).where(LinkPool.used_users < LinkPool.capacity)
        )
        return result.scalars().all()

async def assign_specific_link_to_order(order_id: int, new_link_id: int) -> str | None:
    async with async_session() as session:
        order = await session.get(Order, order_id)
        if not order:
            return None

        # آزاد کردن ظرفیت لینک قبلی (اگه وجود داشته باشه)
        if order.assigned_link_id:
            await session.execute(
                update(LinkPool)
                .where(LinkPool.id == order.assigned_link_id)
                .values(used_users=LinkPool.used_users - 1)
            )

        # گرفتن لینک جدید
        link = await session.get(LinkPool, new_link_id)
        if not link or link.used_users >= link.capacity:
            return None

        # افزایش ظرفیت لینک جدید
        await session.execute(
            update(LinkPool)
            .where(LinkPool.id == new_link_id)
            .values(used_users=LinkPool.used_users + 1)
        )

        # آپدیت سفارش با لینک جدید
        new_url = link.url
        await session.execute(
            update(Order)
            .where(Order.id == order_id)
            .values(
                assigned_link=new_url,
                assigned_link_id=new_link_id
            )
        )

        await session.commit()
        return new_url





# ──────────────── Users ────────────────
async def get_user(user_id: int) -> Optional[User]:
    async with async_session() as session:
        res = await session.execute(select(User).where(User.user_id == user_id))
        return res.scalar_one_or_none()

async def add_user(user_id: int, username: Optional[str]) -> None:
    async with async_session() as session:
        session.add(User(user_id=user_id, username=username))
        await session.commit()


# ─────────────── Categories ───────────────
async def add_category(type: str, title: str, description: str = "") -> int:
    async with async_session() as session:
        cat = PlanCategory(type=type, title=title, description=description)
        session.add(cat)
        await session.commit()
        return cat.id

async def get_all_categories() -> list[PlanCategory]:
    async with async_session() as session:
        res = await session.execute(select(PlanCategory))
        return res.scalars().all()

async def get_category_by_id(category_id: int) -> Optional[PlanCategory]:
    async with async_session() as session:
        res = await session.execute(select(PlanCategory).where(PlanCategory.id == category_id))
        return res.scalar_one_or_none()

async def update_category_field(category_id: int, field_name: str, value) -> None:
    async with async_session() as session:
        stmt = update(PlanCategory).where(PlanCategory.id == category_id).values({field_name: value})
        await session.execute(stmt)
        await session.commit()

async def delete_category(category_id: int) -> None:
    async with async_session() as session:
        await session.execute(delete(PlanCategory).where(PlanCategory.id == category_id))
        await session.commit()


# ─────────────── Plans ───────────────
async def add_plan(
    name: str,
    type: str,
    category_id: int,
    price: float,
    description: str = "",
    user_limit: int = 1,
    volume_limit: Optional[float] = None,
) -> int:
    async with async_session() as session:
        plan = Plan(
            name=name,
            type=type,
            category_id=category_id,
            price=price,
            description=description,
            user_limit=user_limit,
            volume_limit=volume_limit,
        )
        session.add(plan)
        await session.commit()
        return plan.id

async def get_plans_by_category(category_id: int) -> list[Plan]:
    async with async_session() as session:
        res = await session.execute(select(Plan).where(Plan.category_id == category_id))
        return res.scalars().all()

from sqlalchemy import select
from sqlalchemy.orm import selectinload
from database.db import async_session
from database.models import Plan

async def get_plan_by_id(plan_id: int):
    async with async_session() as session:
        stmt = (
            select(Plan)
            .options(selectinload(Plan.category))
            .where(Plan.id == plan_id)
        )
        result = await session.execute(stmt)
        return result.scalar_one_or_none()


async def update_plan_field(plan_id: int, field_name: str, value) -> None:
    async with async_session() as session:
        await session.execute(
            update(Plan).where(Plan.id == plan_id).values({field_name: value})
        )
        await session.commit()

async def delete_plan(plan_id: int) -> None:
    async with async_session() as session:
        await session.execute(delete(Plan).where(Plan.id == plan_id))
        await session.commit()


# ─────────────── Orders ───────────────
from datetime import datetime, timedelta
from typing import Optional
import jdatetime

from sqlalchemy import select
from sqlalchemy.orm import selectinload

from database.db import async_session
from database.models import Order, Plan


async def generate_order_code(session) -> str:
    """
    تولید order_code به صورت Mushak001, Mushak002, ...
    """
    result = await session.execute(select(Order).order_by(Order.id.desc()).limit(1))
    last_order = result.scalar_one_or_none()

    if last_order and last_order.order_code:
        try:
            num = int(last_order.order_code.replace("Mushak", "")) + 1
        except ValueError:
            num = last_order.id + 1
    else:
        num = 1

    if num < 1000:
        return f"Mushak{str(num).zfill(3)}"
    return f"Mushak{num}"


# داخل database/queries.py — جایگزین کامل تابع add_order کن
async def add_order(
    user_id: int,
    plan_id: int,
    payment_method: str = "card_to_card",
    start_date: Optional[datetime] = None,
    expiration_date: Optional[datetime] = None,
    renew_order_id: Optional[int] = None,  # 👈 پارامتر جدید
) -> int:
    """
    ایجاد سفارش جدید.
    - اگر start_date پاس نشده باشد:
      * برای دسته‌ی 'نامحدود' → تاریخ شروع بر اساس بازه‌های شمسی 1,5,10,15,20,25 تعیین می‌شود
      * برای بقیه (حجمی) → شروع = اکنون
    - در صورتی که expiration_date پاس نشده باشد → start_date + 30 روز
    """
    async with async_session() as session:
        now = datetime.utcnow()

        # پلن را همراه با دسته‌بندی بگیر (برای جلوگیری از lazy load)
        stmt = select(Plan).options(selectinload(Plan.category)).where(Plan.id == plan_id)
        result = await session.execute(stmt)
        plan = result.scalar_one_or_none()

        if not plan:
            raise ValueError(f"Plan {plan_id} not found")
                    # اگر تمدید پلن نامحدود باشد
        if (
            renew_order_id is not None
            and plan.category
            and plan.category.type == "نامحدود"
        ):
            stmt = select(Order).where(Order.id == renew_order_id)
            result = await session.execute(stmt)
            old_order = result.scalar_one_or_none()

            if old_order:
                # اگر اشتراک قبلی هنوز منقضی نشده باشد
                if old_order.expiration_date > now:
                    start_date = old_order.expiration_date

        # اگر start_date داده نشده
        # # اگر start_date داده نشده
        # if start_date is None:
        #     is_unlimited = (plan.category and plan.category.type == "نامحدود")
        #
        #     if is_unlimited:
        #         # تاریخ شمسی (jdatetime)
        #         jal_now = jdatetime.datetime.fromgregorian(datetime=now)
        #         cycle_days = [1, 5, 10, 15, 20, 25]
        #         today_jal_day = jal_now.day
        #
        #         prev_days = [d for d in cycle_days if d <= today_jal_day]
        #         if prev_days:
        #             chosen_day = max(prev_days)
        #             jal_start_dt = jdatetime.datetime(
        #                 jal_now.year, jal_now.month, chosen_day, 0, 0, 0
        #             )
        #         else:
        #             if jal_now.month > 1:
        #                 prev_month = jal_now.month - 1
        #                 prev_year = jal_now.year
        #             else:
        #                 prev_month = 12
        #                 prev_year = jal_now.year - 1
        #
        #             jal_start_dt = jdatetime.datetime(
        #                 prev_year, prev_month, 25, 0, 0, 0
        #             )
        #
        #         start_date = jal_start_dt.togregorian()
        #     else:
        #         start_date = now
        # اگر start_date قبلاً تعیین نشده باشد
        if start_date is None:
            start_date = now
            
        # اگر expiration_date داده نشده
        if expiration_date is None:

            if plan.category and plan.category.type == "نامحدود":

                jal_start = jdatetime.datetime.fromgregorian(datetime=start_date)

                year = jal_start.year
                month = jal_start.month + 1
                day = jal_start.day

                if month > 12:
                    month = 1
                    year += 1

                # اگر روز در ماه بعد وجود نداشت
                while True:
                    try:
                        jal_exp = jdatetime.datetime(
                            year,
                            month,
                            day,
                            jal_start.hour,
                            jal_start.minute,
                            jal_start.second,
                        )
                        break
                    except ValueError:
                        day -= 1

                expiration_date = jal_exp.togregorian()

            else:

                expiration_date = start_date + timedelta(days=30)

        # 🔹 تولید order_code
        order_code = await generate_order_code(session)

        # ایجاد سفارش (renew_order_id هم درج می‌شود)
        order = Order(
            user_id=user_id,
            plan_id=plan_id,
            payment_method=payment_method,
            start_date=start_date,
            expiration_date=expiration_date,
            order_code=order_code,
            renew_order_id=renew_order_id  # 👈 اینجا مقدار می‌رود (ممکن است None باشد)
        )
        session.add(order)
        await session.commit()
        await session.refresh(order)
        return order.id







async def set_order_status(order_id: int, status: PaymentStatus, admin_note: Optional[str] = None) -> None:
    async with async_session() as session:
        await session.execute(
            update(Order)
            .where(Order.id == order_id)
            .values(payment_status=status, admin_note=admin_note)
        )
        await session.commit()

async def attach_receipt(order_id: int, receipt_type: str, value: str) -> None:
    """
    ذخیره رسید برای سفارش.
    receipt_type: "file" یا "text"
    value: متن رسید یا شناسه فایل
    """
    async with async_session() as session:
        await session.execute(
            update(Order)
            .where(Order.id == order_id)
            .values(receipt_type=receipt_type, receipt_value=value)
        )
        await session.commit()


async def get_pending_orders() -> list[Order]:
    async with async_session() as session:
        res = await session.execute(
            select(Order)
            .where(Order.payment_status == PaymentStatus.pending,Order.receipt_type.is_not(None))
        )
        return res.scalars().all()



async def get_order_by_id(order_id: int) -> Optional[Order]:
    async with async_session() as session:
        res = await session.execute(select(Order).where(Order.id == order_id))
        return res.scalar_one_or_none()



from datetime import datetime
from sqlalchemy import select
from database.models import Order, PaymentStatus

async def get_active_orders(session) -> list[Order]:
    now = datetime.utcnow()
    q = select(Order).where(
        Order.payment_status == PaymentStatus.paid,  # یا approved بر اساس enum شما
        Order.expiration_date > now
    )
    result = await session.execute(q)
    return result.scalars().all()


# ---------- پاک‌سازی سفارشات معوق قدیمی ----------
async def delete_old_pending_orders():
    two_days_ago = datetime.utcnow() - timedelta(days=2)
    async with async_session() as session:
        old_orders = await session.scalars(
            select(Order)
            .where(Order.payment_status == PaymentStatus.pending,
                   Order.created_at <= two_days_ago)
        )
        for o in old_orders:
            await session.delete(o)
        await session.commit()

async def delete_order_by_id(order_id: int) -> None:
    async with async_session() as session:
        await session.execute(delete(Order).where(Order.id == order_id))
        await session.commit()


# ──────────────── Link Pool ────────────────
async def add_link_to_pool(
    url: str,
    capacity: int,
    link_type: LinkType = LinkType.volume,
    admin_label: Optional[str] = None,
    plan_id: Optional[int] = None,
    admin_note: Optional[str] = None,
) -> int:
    async with async_session() as session:
        link = LinkPool(
            url=url,
            capacity=capacity,
            used_users=0,
            status="active",
            type=link_type,
            admin_label=admin_label,
            plan_id=plan_id,
            admin_note=admin_note,
        )
        session.add(link)
        await session.commit()
        return link.id


async def get_available_link(
    plan_id: int,
    group_start_day: int,
    link_type: LinkType = LinkType.volume
) -> Optional[LinkPool]:
    async with async_session() as session:
        res = await session.execute(
            select(LinkPool)
            .where(
                LinkPool.plan_id == plan_id,
                LinkPool.capacity > LinkPool.used_users,
                LinkPool.status == "active",
                LinkPool.type == link_type,
            )
            .order_by(LinkPool.used_users.asc())
        )
        return res.scalars().first()


async def increment_link_usage(link_id: int) -> None:
    async with async_session() as session:
        await session.execute(
            update(LinkPool)
            .where(LinkPool.id == link_id)
            .values(used_users=LinkPool.used_users + 1)
        )
        await session.commit()


async def get_all_links() -> list[LinkPool]:
    async with async_session() as session:
        res = await session.execute(select(LinkPool))
        return res.scalars().all()

async def delete_link_by_id(link_id: int) -> None:
    async with async_session() as session:
        await session.execute(delete(LinkPool).where(LinkPool.id == link_id))
        await session.commit()

async def update_link_field(link_id: int, field_name: str, value) -> None:
    async with async_session() as session:
        await session.execute(
            update(LinkPool).where(LinkPool.id == link_id).values({field_name: value})
        )
        await session.commit()



async def get_links_by_plan_id(plan_id: int) -> list[LinkPool]:
    """
    دریافت همه لینک‌های مربوط به یک پلن خاص.
    """
    async with async_session() as session:
        result = await session.execute(
            select(LinkPool).where(LinkPool.plan_id == plan_id)
        )
        return result.scalars().all()



# ──────────────── کاربران معوق ────────────────
async def get_user_pending_orders(user_id: int) -> List[Order]:
    async with async_session() as session:
        res = await session.execute(
            select(Order)
            .where(Order.user_id == user_id, Order.payment_status == PaymentStatus.pending)
        )
        return res.scalars().all()

async def get_orders_without_link() -> list[Order]:
    """
    سفارشاتی که پرداخت‌شده (approved) ولی assigned_link آنها هنوز None است.
    """
    async with async_session() as session:
        res = await session.execute(
            select(Order)
            .where(
                Order.payment_status == PaymentStatus.approved,
                Order.assigned_link.is_(None)
            )
        )
        return res.scalars().all()


# در انتهای database/queries.py
async def get_links_by_category(category_id: int) -> list[LinkPool]:
    async with async_session() as session:
        cat = await session.get(PlanCategory, category_id)
        if not cat:
            return []

        # دریافت لینک‌های متصل به پلن‌های این دسته
        stmt = (
            select(LinkPool)
            .join(Plan, LinkPool.plan_id == Plan.id)
            .where(Plan.category_id == category_id)
            .order_by(LinkPool.id)
        )
        res = await session.execute(stmt)
        links = list(res.scalars().all())

        # اگر دسته‌بندی «نامحدود» باشد، لینک‌های عمومی/نامحدود بدون plan_id را هم اضافه می‌کند
        if getattr(cat, "type", None) == "نامحدود":
            existing_ids = {l.id for l in links}
            stmt_unlimited = (
                select(LinkPool)
                .where(
                    or_(
                        LinkPool.type == LinkType.unlimited,
                        LinkPool.type == "unlimited",
                        LinkPool.plan_id.is_(None)
                    )
                )
                .order_by(LinkPool.id)
            )
            res_unlimited = await session.execute(stmt_unlimited)
            unlimited_links = res_unlimited.scalars().all()
            for ul in unlimited_links:
                if ul.id not in existing_ids:
                    links.append(ul)

        return links


from sqlalchemy import select, distinct

async def get_subscribers_by_link_id(link_id: int) -> list[int]:
    async with async_session() as session:
        # لینک را بگیر
        link = await session.get(LinkPool, link_id)
        if not link:
            return []

        url = link.url

        # فقط user_id یکتا
        res = await session.execute(
            select(distinct(Order.user_id)).where(
                Order.payment_status == PaymentStatus.approved,
                Order.assigned_link == url
            )
        )

        return [row[0] for row in res.all()]





async def search_links_by_keyword(keyword: str) -> list[LinkPool]:
    # ایجاد الگو برای جستجو
    pattern = f"%{keyword}%"

    # کوئری برای جستجو در url و admin_label و admin_note
    query = select(LinkPool).where(
        or_(
            LinkPool.url.ilike(pattern),  # جستجو در URL
            LinkPool.admin_label.ilike(pattern),  # جستجو در admin_label
            LinkPool.admin_note.ilike(pattern)  # جستجو در admin_note
        )
    )

    # اجرای کوئری و برگشت نتایج
    async with async_session() as session:
        result = await session.execute(query)
        return result.scalars().all()



async def get_active_order(user_id: int) -> Optional[Order]:
    async with async_session() as session:
        res = await session.execute(
            select(Order).where(
                Order.user_id == user_id,
                Order.payment_status == PaymentStatus.approved,
                Order.assigned_link.is_not(None)
            )
        )
        return res.scalar_one_or_none()



async def update_link_for_all_orders(link_id: int, new_url: str) -> None:
    """
    این تابع:
    - لینک با شناسه داده‌شده را پیدا می‌کند.
    - URL جدید را در جدول لینک‌ها (LinkPool) ذخیره می‌کند.
    - تمام سفارش‌هایی که assigned_link آن‌ها برابر با URL قبلی بوده را آپدیت می‌کند.
    """
    async with async_session() as session:
        # 🔹 گرفتن لینک فعلی
        link = await session.get(LinkPool, link_id)
        if not link:
            print(f"[update_link] لینک با id={link_id} پیدا نشد.")
            return
        old_url = link.url
        print(f"[update_link] URL قبلی: {old_url} → جدید: {new_url}")

        # 🔹 آپدیت URL در جدول LinkPool
        await session.execute(
            update(LinkPool)
            .where(LinkPool.id == link_id)
            .values(url=new_url)
        )

        # 🔹 آپدیت سفارش‌ها بر اساس assigned_link قدیمی
        result = await session.execute(
            update(Order)
            .where(Order.assigned_link == old_url)
            .values(assigned_link=new_url)
        )

        print(f"[update_link] تعداد سفارش‌هایی که آپدیت شدند: {result.rowcount}")

        await session.commit()


from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Order, PaymentStatus

from database.models import Order, PaymentStatus

async def get_active_orders(
    user_id: int,
    session: AsyncSession
) -> list[Order]:
    """
    برمی‌گرداند همهٔ سفارش‌های پرداخت‌شده (approved) و دارای لینک اختصاص یافته.
    """
    stmt = (
        select(Order)
        .where(
            Order.user_id == user_id,
            Order.payment_status == PaymentStatus.approved,  # ← از approved استفاده کنید
            Order.assigned_link.is_not(None)                # فقط سفارش‌های لینک‌خورده
        )
        .order_by(Order.id.desc())
    )
    res = await session.execute(stmt)
    return res.scalars().all()



# در database/queries.py

# database/queries.py

from sqlalchemy import update
from database.db import async_session
from database.models import Order

# database/queries.py

from sqlalchemy import update
from database.db import async_session
from database.models import Order

async def set_order_price_discount(
    order_id: int,
    final_price: int,
    discount: float
) -> None:
    """
    قیمت نهایی را در final_price و درصد تخفیف را در discount_percent می‌نویسد.
    """
    async with async_session() as session:
        await session.execute(
            update(Order)
            .where(Order.id == order_id)
            .values(
                final_price=final_price,
                discount_percent=discount   # ← اینجا به جای discount_applied بنویسید
            )
        )
        await session.commit()



from sqlalchemy import update, select
from database.models import User

# … بقیه‌ی ایمپورت‌ها …
from sqlalchemy import select, update, literal
from database.db import async_session
from database.models import User


from sqlalchemy import update

# در database/queries.py (واردات لازم در بالای فایل را داشته باشید)
from sqlalchemy import select, update
from database.db import async_session
from database.models import User

async def add_gift_credit(user_id: int, amount: int) -> None:
    """
    افزایش یا کاهش اعتبار هدیه کاربر.
    اگر کاربر وجود نداشته باشد:
      - اگر amount > 0، رکورد کاربر ساخته می‌شود و اعتبار برابر amount قرار می‌گیرد.
      - اگر amount <= 0، هیچ کاری انجام نمی‌شود (چون منفی گذاشتن برای کاربرِ وجود نداشته منطقی نیست).
    """
    async with async_session() as session:
        # بررسی وجود کاربر
        result = await session.execute(select(User).where(User.user_id == user_id))
        user = result.scalar_one_or_none()

        if user is None:
            # اگر مقدار مثبت است، رکورد جدید بساز
            if amount > 0:
                new_user = User(user_id=user_id, username=None, gift_credit=amount)
                session.add(new_user)
                await session.commit()
            else:
                # مقدار منفی برای کاربر ناموجود => هیچ کاری انجام نمی‌دهیم
                return
            return

        # اگر کاربر وجود داشت، مقدار را جمع کن (از ستون واقعی جدول استفاده می‌کنیم)
        await session.execute(
            update(User)
            .where(User.user_id == user_id)
            .values(gift_credit=User.__table__.c.gift_credit + amount)
        )
        await session.commit()



async def set_user_gift_credit(user_id: int, amount: int) -> None:
    """
    ست کردن مستقیم اعتبار هدیه
    """
    async with async_session() as session:
        await session.execute(
            update(User)
            .where(User.user_id == user_id)
            .values(gift_credit=amount)
        )
        await session.commit()

from sqlalchemy import select
from database.db import async_session
from database.models import User

# --- گرفتن اعتبار هدیه کاربر ---
async def get_user_gift_credit(user_id: int) -> int:
    async with async_session() as session:
        result = await session.execute(
            select(User.gift_credit).where(User.user_id == user_id)
        )
        credit = result.scalar_one_or_none()
        return credit or 0



# گزارش فروش ماهانه
from datetime import datetime, timedelta
from sqlalchemy import select, func
from database.db import async_session
from database.models import Order, Plan, PaymentStatus

async def get_monthly_sales_report() -> list[tuple[str, int, int]]:
    """
    برمی‌گرداند لیستی از تاپل‌های (plan_name, count_orders, total_revenue)
    برای سفارش‌هایی که در ۳۰ روز گذشته تأیید شده‌اند.
    """
    one_month_ago = datetime.utcnow() - timedelta(days=30)
    async with async_session() as session:
        stmt = (
            select(
                Plan.name,
                func.count(Order.id),
                func.coalesce(func.sum(Order.final_price), 0)
            )
            .join(Plan, Plan.id == Order.plan_id)
            .where(
                Order.payment_status == PaymentStatus.approved,
                Order.created_at >= one_month_ago
            )
            .group_by(Plan.name)
        )
        res = await session.execute(stmt)
        return res.all()  # List[tuple[plan_name, count, revenue]]


# ──────────────── دریافت تمام سفارش‌های فعال ────────────────
from datetime import datetime
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Order, PaymentStatus

async def get_all_active_orders(session: AsyncSession) -> list[Order]:
    """
    دریافت تمام سفارش‌های فعال (پرداخت‌شده و تاریخ انقضا > حالا)
    """
    now = datetime.utcnow()
    stmt = (
        select(Order)
        .options(
            selectinload(Order.plan),
            selectinload(Order.user)
        )
        .where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date > now
        )
        .order_by(Order.id.desc())
    )
    res = await session.execute(stmt)
    return res.scalars().unique().all()



from sqlalchemy import select
from sqlalchemy.orm import selectinload
from database.models import Order, User, PaymentStatus
from datetime import datetime

async def get_user_active_order(
    session: AsyncSession,
    user_id: int | None = None,
    username: str | None = None
) -> Order | None:
    now = datetime.utcnow()
    stmt = select(Order).options(
        selectinload(Order.user),
        selectinload(Order.plan)
    ).where(
        Order.payment_status == PaymentStatus.approved,
        Order.expiration_date > now
    )
    if user_id is not None:
        stmt = stmt.where(Order.user_id == user_id)
    elif username:
        stmt = stmt.join(Order.user).where(User.username == username)
    else:
        return None

    res = await session.execute(stmt.limit(1))
    return res.scalars().first()



# ──────────────── جستجوی کاربران ────────────────
from sqlalchemy import select, or_
from database.db import async_session
from database.models import User

async def search_users(keyword: str) -> list[User]:
    """
    جستجو بر اساس:
      - username به‌صورت partial (غیر حساس به حروف بزرگ/کوچک)
      - در صورت عدد بودن keyword، جستجو بر اساس user_id دقیق
    """
    async with async_session() as session:
        # الگوی جستجو در username
        pattern = f"%{keyword}%"

        conditions = [User.username.ilike(pattern)]
        if keyword.isdigit():
            conditions.append(User.user_id == int(keyword))

        stmt = select(User).where(or_(*conditions))
        res = await session.execute(stmt)
        return res.scalars().all()







# ──────────────── تغییر تاریخ انقضا ────────────────
async def update_order_expiration(
    order_id: int,
    new_date: date,
    session
) -> None:
    """
    تاریخ انقضای سفارش با شناسه order_id را به new_date تغییر می‌دهد.
    پارامترها:
      - order_id: شناسه سفارش
      - new_date: شیء datetime.date جدید
      - session: AsyncSession جاری
    """
    await session.execute(
        update(Order)
        .where(Order.id == order_id)
        .values(expiration_date=new_date)
    )
    # Commit باید توسط handler انجام شود



from database.db import async_session
from database.models import LinkPool

async def get_link_url_by_id(link_id: int) -> str | None:
    """
    URL لینک با شناسه link_id رو برمی‌گردونه.
    """
    async with async_session() as session:
        link = await session.get(LinkPool, link_id)
        return link.url if link else None


from sqlalchemy import select, update, func
from database.db import async_session
from database.models import LinkPool

async def get_link_by_url(url: str) -> LinkPool | None:
    async with async_session() as session:
        result = await session.execute(
            select(LinkPool).where(LinkPool.url == url)
        )
        return result.scalar_one_or_none()

async def decrement_link_usage(link_id: int) -> None:
    async with async_session() as session:
        # used_users = GREATEST(used_users - 1, 0)
        await session.execute(
            update(LinkPool)
            .where(LinkPool.id == link_id)
            .values(
                used_users=func.greatest(LinkPool.used_users - 1, 0)
            )
        )
        await session.commit()


# ویرایش اعتبار هدیه 
async def set_user_gift_credit(user_id: int, amount: int) -> None:
    async with async_session() as session:
        await session.execute(
            update(User)
            .where(User.user_id == user_id)
            .values(gift_credit=amount)
        )
        await session.commit()



# ظرفیت تکمیل 
from sqlalchemy import update
from .models import PlanCategory, Plan
from database.db import async_session as session

async def toggle_category_capacity(category_id: int, new_state: bool):
    async with session() as s:
        await s.execute(
            update(PlanCategory)
            .where(PlanCategory.id == category_id)
            .values(is_full=new_state)
        )
        await s.commit()

async def toggle_plan_capacity(plan_id: int, new_state: bool):
    async with session() as s:
        await s.execute(
            update(Plan)
            .where(Plan.id == plan_id)
            .values(is_full=new_state)
        )
        await s.commit()


# database/queries.py

from sqlalchemy import select
from .models import PlanCategory, Plan
from database.db import async_session

# بررسی پر بودن یک دسته (آیا هیچ پلنی ظرفیت دارد یا نه)
async def is_category_full(category_id: int) -> bool:
    async with async_session() as session:
        result = await session.execute(
            select(Plan).where(
                Plan.category_id == category_id,
                Plan.is_full == False
            )
        )
        plans = result.scalars().all()
        print(f"🔎 [is_category_full] category {category_id} → {len(plans)} plans available")
        return len(plans) == 0

# بررسی پر بودن یک پلن
async def is_plan_full(plan_id: int) -> bool:
    async with async_session() as session:
        result = await session.execute(
            select(Plan.is_full).where(Plan.id == plan_id)
        )
        is_full = result.scalar_one_or_none()
        print(f"🔎 [is_plan_full] plan {plan_id} → {is_full}")
        return bool(is_full)



# ──────────────── Orders by User ────────────────
async def get_active_orders_by_user(user_id: int) -> list[Order]:
    """
    دریافت لیست سفارش‌های فعال یک کاربر خاص
    """
    now = datetime.utcnow()
    async with async_session() as session:
        res = await session.execute(
            select(Order)
            .where(
                Order.user_id == user_id,
                Order.payment_status == PaymentStatus.approved,
                Order.expiration_date > now
            )
        )
        return res.scalars().all()


# ──────────────── Expire Order ────────────────
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Order, PaymentStatus
from database.db import async_session

# async def expire_order(order_id: int):
#     """
#     یک سفارش را منقضی می‌کند (payment_status → expired).
#     """
#     async with async_session() as session:  # type: AsyncSession
#         await session.execute(
#             update(Order)
#             .where(Order.id == order_id)
#             .values(payment_status=PaymentStatus.expired)
#         )
#         await session.commit()

from sqlalchemy import update, select
from database.models import Order

async def update_order_with_renew(new_order_id: int, old_order_id: int):
    async with async_session() as session:
        # سفارش قبلی را expire و لینک بده به جدید
        await session.execute(
            update(Order)
            .where(Order.id == old_order_id)
            .values(is_expired=True, renewed_to=new_order_id)
        )
        await session.commit()

        print(f"✅ سفارش قبلی تمدید شد → old_id={old_order_id}, new_id={new_order_id}")



from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from .models import Order

async def generate_order_code(session: AsyncSession) -> str:
    # آخرین سفارش رو پیدا کن
    result = await session.execute(select(Order).order_by(Order.id.desc()).limit(1))
    last_order = result.scalar_one_or_none()

    if last_order and last_order.order_code:
        # استخراج عدد از انتهای order_code
        num = int(last_order.order_code.replace("Mushak", "")) + 1
    else:
        num = 1

    # ساختن فرمت Mushak001, Mushak002, ...
    if num < 1000:
        return f"Mushak{str(num).zfill(3)}"
    return f"Mushak{num}"

# ──────────────── Discount Codes ───────────────
from datetime import datetime
from sqlalchemy import select, update, delete, func
from database.db import async_session
from database.models import DiscountCode, DiscountUsage, Order


# افزودن کد تخفیف
async def add_discount_code(
    code: str,
    percent: int,
    usage_limit: int | None = None,
    expires_at: datetime | None = None
) -> int:
    async with async_session() as session:
        new_code = DiscountCode(
            code=code.upper(),
            percent=percent,
            usage_limit=usage_limit,
            expires_at=expires_at,
            used_count=0
        )
        session.add(new_code)
        await session.commit()
        return new_code.id


# گرفتن کد تخفیف بر اساس متن کد
async def get_discount_code(code: str) -> DiscountCode | None:
    async with async_session() as session:
        res = await session.execute(
            select(DiscountCode).where(DiscountCode.code == code.upper())
        )
        return res.scalar_one_or_none()


# گرفتن کد تخفیف بر اساس ID
async def get_discount_code_by_id(discount_id: int) -> DiscountCode | None:
    async with async_session() as session:
        res = await session.execute(
            select(DiscountCode).where(DiscountCode.id == discount_id)
        )
        return res.scalar_one_or_none()


# افزایش تعداد استفاده (ساده)
async def use_discount_code(code: str) -> None:
    async with async_session() as session:
        await session.execute(
            update(DiscountCode)
            .where(DiscountCode.code == code.upper())
            .values(used_count=DiscountCode.used_count + 1)
        )
        await session.commit()


# لیست همه‌ی کدهای تخفیف
async def list_discount_codes() -> list[DiscountCode]:
    async with async_session() as session:
        res = await session.execute(select(DiscountCode))
        return res.scalars().all()


# حذف کد تخفیف
async def delete_discount_code(code_id: int) -> None:
    async with async_session() as session:
        await session.execute(delete(DiscountCode).where(DiscountCode.id == code_id))
        await session.commit()


# ثبت استفاده
async def log_discount_usage(user_id: int, discount_id: int, order_id: int):
    async with async_session() as session:
        usage = DiscountUsage(
            user_id=user_id,
            discount_id=discount_id,
            order_id=order_id
        )
        session.add(usage)
        await session.commit()


# شمارش تعداد دفعاتی که یک کاربر از یک کد خاص استفاده کرده
async def count_user_discount_usage(user_id: int, discount_id: int) -> int:
    async with async_session() as session:
        res = await session.execute(
            select(func.count()).select_from(DiscountUsage).where(
                DiscountUsage.user_id == user_id,
                DiscountUsage.discount_id == discount_id
            )
        )
        val = res.scalar_one_or_none()
        return int(val or 0)


# لیست استفاده‌کننده‌ها و تعداد دفعات استفاده از یک کد
async def list_discount_usages(discount_id: int):
    async with async_session() as session:
        stmt = (
            select(DiscountUsage.user_id, func.count().label("count"))
            .where(DiscountUsage.discount_id == discount_id)
            .group_by(DiscountUsage.user_id)
            .order_by(func.count().desc())
        )
        res = await session.execute(stmt)
        return res.all()


# اجرای اتمیک تخفیف روی سفارش
async def apply_discount_atomic(
    order_id: int,
    code: str,
    final_price: int,
    discount_percent: float,
    user_id: int
) -> tuple[bool, str | None]:
    async with async_session() as session:
        async with session.begin():
            # بارگذاری تخفیف
            res = await session.execute(
                select(DiscountCode).where(DiscountCode.code == code.upper())
            )
            discount = res.scalar_one_or_none()
            if not discount:
                return False, "invalid"

            # اعتبارسنجی
            if discount.expires_at and discount.expires_at < datetime.utcnow():
                return False, "invalid"
            if discount.usage_limit and discount.used_count >= discount.usage_limit:
                return False, "invalid"

            # آپدیت سفارش (فقط اگر قبلاً کدی اعمال نشده باشد)
            upd = update(Order).where(
                (Order.id == order_id) &
                (Order.discount_code == None)
            ).values(
                final_price=final_price,
                discount_percent=discount_percent,
                discount_code=code.upper()
            )
            result = await session.execute(upd)
            if result.rowcount == 0:
                return False, "already_applied"

            # افزایش used_count
            # await session.execute(
                # update(DiscountCode)
                # .where(DiscountCode.id == discount.id)
                # .values(used_count=DiscountCode.used_count + 1)
            # )

            # ثبت استفاده
            # usage = DiscountUsage(user_id=user_id, discount_id=discount.id, # # order_id=order_id)
            # session.add(usage)

        return True, None

# مجموع مبلغ سفارشات پرداخت‌شده و تأییدشده با یک کد تخفیف
async def get_discount_total_sales(discount_id: int) -> int:
    async with async_session() as session:
        res = await session.execute(
            select(func.coalesce(func.sum(Order.final_price), 0))
            .where(
                Order.discount_code == select(DiscountCode.code).where(DiscountCode.id == discount_id).scalar_subquery(),
                Order.payment_status == PaymentStatus.approved  # 👈 اضافه شد: فقط سفارش‌های تأییدشده
            )
        )
        return res.scalar_one()
        
        
        
        
# queries.py
from sqlalchemy import update

async def update_discount_code(discount_id: int, **kwargs) -> bool:
    """بروزرسانی کد تخفیف با فیلدهای دلخواه"""
    async with async_session() as session:
        stmt = (
            update(DiscountCode)
            .where(DiscountCode.id == discount_id)
            .values(**kwargs)
        )
        res = await session.execute(stmt)
        await session.commit()
        return res.rowcount > 0

# تعداد کاربران یکتایی که از یک کد تخفیف استفاده کرده‌اند
async def count_discount_users(discount_id: int) -> int:
    async with async_session() as session:
        res = await session.execute(
            select(func.count(func.distinct(DiscountUsage.user_id)))
            .where(DiscountUsage.discount_id == discount_id)
        )
        return res.scalar_one() or 0

# لیست صفحه‌بندی شده استفاده‌کننده‌ها
async def list_discount_usages_paginated(
    discount_id: int,
    limit: int,
    offset: int
):
    async with async_session() as session:
        stmt = (
            select(
                DiscountUsage.user_id,
                func.count().label("count")
            )
            .where(DiscountUsage.discount_id == discount_id)
            .group_by(DiscountUsage.user_id)
            .order_by(func.count().desc())  # مرتب‌سازی بر اساس تعداد استفاده
            .limit(limit)
            .offset(offset)
        )
        res = await session.execute(stmt)
        return res.all()




# اطلاع رسانی تمدید 
from database.models import Setting

async def get_setting(session, key: str) -> str | None:
    result = await session.get(Setting, key)
    return result.value if result else None


async def set_setting(session, key: str, value: str):
    setting = await session.get(Setting, key)
    if setting:
        setting.value = value
    else:
        session.add(Setting(key=key, value=value))
    await session.commit()


# اجرای دستور برای ایجاد وضعیت کرون (در صورتی که جدول خالی باشد)
async def initialize_cron_status():
    async with async_session() as session:
        cron_status = await session.execute(select(CronStatus).filter(CronStatus.id == 1))
        cron_status = cron_status.scalar_one_or_none()

        if not cron_status:
            cron_status = CronStatus(id=1, is_active=False)
            session.add(cron_status)
            await session.commit()



from sqlalchemy import insert, select, delete
from database.models import Setting, NotificationLog
from sqlalchemy.ext.asyncio import AsyncSession

# -------------- Settings helpers --------------
async def get_setting(key: str) -> Optional[str]:
    async with async_session() as session:
        res = await session.execute(select(Setting).where(Setting.key == key))
        s = res.scalar_one_or_none()
        return s.value if s else None


async def set_setting(key: str, value: Optional[str]) -> None:
    async with async_session() as session:
        # try update, else insert
        res = await session.execute(select(Setting).where(Setting.key == key))
        s = res.scalar_one_or_none()
        if s:
            s.value = value
            session.add(s)
            await session.commit()
        else:
            new = Setting(key=key, value=value)
            session.add(new)
            await session.commit()


async def get_cron_enabled() -> bool:
    v = await get_setting("cron_enabled")
    if v is None:
        # پیش‌فرض روشن باشه (در صورت نیاز تغییر بدید)
        await set_setting("cron_enabled", "1")
        return True
    return v in ("1", "true", "True", "on")


async def set_cron_enabled(enabled: bool) -> None:
    await set_setting("cron_enabled", "1" if enabled else "0")


# -------------- Notification log helpers --------------
async def has_notification_been_sent(order_id: int, notification_type: str) -> bool:
    async with async_session() as session:
        res = await session.execute(
            select(NotificationLog).where(
                NotificationLog.order_id == order_id,
                NotificationLog.notification_type == notification_type
            )
        )
        return res.scalar_one_or_none() is not None


async def record_notification_sent(
    order_id: int,
    user_id: int,
    notification_type: str
) -> None:

    async with async_session() as session:

        nl = NotificationLog(
            order_id=order_id,
            user_id=user_id,
            notification_type=notification_type,
            notification_date=date.today()
        )

        session.add(nl)

        await session.commit()


# -------------- Orders due for notification --------------
from sqlalchemy import select, exists
from database.models import Order, PaymentStatus
from database.db import async_session
from datetime import datetime, timedelta

async def get_orders_expiring_within(delta_seconds: int, limit: int = 500, offset: int = 0) -> list[Order]:
    now = datetime.utcnow()
    cutoff = now + timedelta(seconds=delta_seconds)
    async with async_session() as session:
        # alias برای subquery
        subq = select(Order.id).where(
            Order.renew_order_id == Order.__table__.c.id,  # این خطِ نمونه است؛ در ادامه اصلاح می‌کنیم
        )

        # اما چون SQLAlchemy نیاز به subquery بر اساس همان table دارد، بهتر از exists با یک alias استفاده کنیم:
        from sqlalchemy.orm import aliased
        NewOrder = aliased(Order)

        stmt = (
            select(Order)
            .where(
                Order.payment_status == PaymentStatus.approved,
                Order.expiration_date > now,
                Order.expiration_date <= cutoff,
                # شرط NOT EXISTS سفارش جدید که این سفارش را renew کرده
                ~exists().where(
                    (NewOrder.renew_order_id == Order.id) &
                    (NewOrder.payment_status == PaymentStatus.approved)
                )
            )
            .limit(limit)
            .offset(offset)
        )
        res = await session.execute(stmt)
        return res.scalars().all()

# استخراج کاربران برای لیست کاربران فعال 
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from datetime import datetime

async def get_active_orders_page(
    session: AsyncSession,
    limit: int,
    offset: int
) -> list[Order]:
    now = datetime.utcnow()

    stmt = (
        select(Order)
        .options(
            selectinload(Order.user),
            selectinload(Order.plan)
        )
        .where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date > now
        )
        .order_by(Order.expiration_date.asc())  # 👈 مرتب بر اساس انقضا
        .limit(limit)
        .offset(offset)
    )

    res = await session.execute(stmt)
    return res.scalars().unique().all()


async def count_active_orders(session: AsyncSession) -> int:
    now = datetime.utcnow()
    res = await session.execute(
        select(func.count())
        .select_from(Order)
        .where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date > now
        )
    )
    return res.scalar_one()


# نمایش نام لینک قبلی کاربر پس از تمدید سفارش به ادمین 
# database/queries.py
from database.db import async_session
from database.models import LinkPool
import sqlalchemy as sa

async def get_link_by_id(link_id: int) -> LinkPool | None:
    async with async_session() as session:
        return await session.get(LinkPool, link_id)

async def get_link_for_order(order) -> LinkPool | None:
    """
    Centralized lookup for an order's link:
    1) اگر assigned_link_id موجود است از آن استفاده کن (اصلی)
    2) fallback: اگر assigned_link_url موجود بود، سعی کن از جدول link_pool آن را پیدا کنی
    """
    if getattr(order, "assigned_link_id", None):
        return await get_link_by_id(order.assigned_link_id)

    # fallback (برای داده‌های قدیمی) — فقط در صورت لزوم
    link_url = getattr(order, "assigned_link_url", None) or getattr(order, "assigned_link", None)
    if link_url:
        async with async_session() as session:
            q = await session.execute(sa.select(LinkPool).where(LinkPool.url == link_url))
            return q.scalar_one_or_none()
    return None


# اطلاع رسانی به انواع کاربران
from datetime import datetime
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import User, Order, PaymentStatus

async def get_users_by_type(
    session: AsyncSession,
    target: str  # "all" | "active" | "non"
) -> set[int]:
    now = datetime.utcnow()

    # همه کاربران ربات (start کرده‌اند)
    res = await session.execute(
        select(User.user_id)
    )
    all_users = set(res.scalars().all())

    # کاربران با اشتراک فعال
    res = await session.execute(
        select(Order.user_id)
        .where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date > now
        )
        .distinct()
    )
    active_users = set(res.scalars().all())

    if target == "active":
        return active_users

    if target == "non":
        return all_users - active_users

    # پیش‌فرض: all
    return all_users


# ذخیره کاربرانی که ربات را بلاک کردند 
from database.models import BlockedUser
from sqlalchemy import select, delete

async def mark_user_blocked(user_id: int):
    async with async_session() as session:
        exists = await session.get(BlockedUser, user_id)
        if not exists:
            session.add(BlockedUser(user_id=user_id))
            await session.commit()

# حذف کاربران از لیست بلاک
async def unblock_user(user_id: int):
    async with async_session() as session:
        await session.execute(
            delete(BlockedUser).where(BlockedUser.user_id == user_id)
        )
        await session.commit()

# گرفتن لیست کاربران بلاک
async def get_blocked_user_ids(session) -> set[int]:
    res = await session.execute(select(BlockedUser.user_id))
    return set(res.scalars().all())

# مازولار کردن اطلاعات پرداخت در پنل ادمین 
from database.models import PaymentConfig

async def get_payment_config() -> PaymentConfig | None:
    async with async_session() as session:
        return await session.get(PaymentConfig, 1)



async def set_payment_config(
    card_number: str | None = None,
    card_holder: str | None = None
):
    async with async_session() as session:
        config = await session.get(PaymentConfig, 1)

        if not config:
            config = PaymentConfig(
                id=1,
                card_number=card_number or "",
                card_holder=card_holder or ""
            )
            session.add(config)
        else:
            if card_number is not None:
                config.card_number = card_number
            if card_holder is not None:
                config.card_holder = card_holder

        await session.commit()


# فایل: database/queries.py یا هر جایی که توابع دیتابیس هست
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Plan

async def get_plans_by_ids(plan_ids: set[int], session: AsyncSession) -> list[Plan]:
    """تمام پلان‌ها را بر اساس لیست plan_id می‌گیرد."""
    if not plan_ids:
        return []

    result = await session.execute(
        select(Plan).where(Plan.id.in_(plan_ids))
    )
    return result.scalars().all()



# --- pagination & search helpers for pending orders ---
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from database.db import async_session
from database.models import Order, User, PaymentStatus

async def _apply_pending_filters(stmt, filters: dict | None):
    """
    filters ممکن است شامل:
      - user_id: int
      - order_code: str
      - username: str (partial match)
    """
    if not filters:
        return stmt

    if filters.get("user_id") is not None:
        stmt = stmt.where(Order.user_id == int(filters["user_id"]))

    if filters.get("order_code"):
        stmt = stmt.where(Order.order_code == filters["order_code"])

    if filters.get("username"):
        # join to User
        # use ilike for partial, case-insensitive search
        stmt = stmt.join(Order.user).where(User.username.ilike(f"%{filters['username']}%"))

    return stmt

async def get_pending_orders_page(limit: int, offset: int, filters: dict | None = None) -> list[Order]:
    """
    صفحه‌ای از سفارش‌های pending را برمی‌گرداند.
    هر رکورد نیز user و plan را eager-load می‌کنیم برای کاهش کوئری‌ها در نمایش.
    """
    async with async_session() as session:
        stmt = (
            select(Order)
            .options(selectinload(Order.user), selectinload(Order.plan))
            .where(Order.payment_status == PaymentStatus.pending,Order.receipt_type.is_not(None))
            .order_by(Order.id.desc())
        )

        stmt = await _apply_pending_filters(stmt, filters)
        stmt = stmt.limit(limit).offset(offset)

        res = await session.execute(stmt)
        return res.scalars().unique().all()


async def count_pending_orders(filters: dict | None = None) -> int:
    """
    تعداد کل سفارش‌های pending با همان فیلترها را برمی‌گرداند.
    """
    async with async_session() as session:
        stmt = select(func.count()).select_from(Order).where(Order.payment_status == PaymentStatus.pending,Order.receipt_type.is_not(None))
        # برای شمارش با username نیاز به join داریم
        if filters and filters.get("username"):
            stmt = stmt.join(User, Order.user_id == User.user_id).where(User.username.ilike(f"%{filters['username']}%"))

        if filters and filters.get("user_id"):
            stmt = stmt.where(Order.user_id == int(filters["user_id"]))

        if filters and filters.get("order_code"):
            stmt = stmt.where(Order.order_code == filters["order_code"])

        res = await session.execute(stmt)
        val = res.scalar_one_or_none()
        return int(val or 0)
        
        
        
# ──────────────── حذف اشتراک منقضی شده و لینک مربوط به آن ────────────────
from sqlalchemy import delete, select
from datetime import datetime
from database.db import async_session
from database.models import Order, LinkPool, PaymentStatus

async def delete_expired_order_and_link(order_id: int) -> bool:
    """
    حذف کامل یک سفارش منقضی‌شده و لینک اختصاص‌یافته به آن از جدول LinkPool
    """
    async with async_session() as session:
        # ۱. دریافت سفارش
        order = await session.get(Order, order_id)
        if not order:
            return False

        # ۲. حذف لینک از LinkPool (بر اساس ID یا URL)
        if getattr(order, "assigned_link_id", None):
            await session.execute(
                delete(LinkPool).where(LinkPool.id == order.assigned_link_id)
            )
        elif getattr(order, "assigned_link", None):
            await session.execute(
                delete(LinkPool).where(LinkPool.url == order.assigned_link)
            )

        # ۳. حذف خود سفارش از دیتابیس
        await session.delete(order)
        await session.commit()
        return True


async def purge_all_expired_orders_and_links() -> int:
    """
    پاک‌سازی دسته‌جمعی تمام سفارش‌های منقضی‌شده و لینک‌های متصل به آن‌ها.
    (فراخوانی توسط تسک‌های زمان‌بندی‌شده / Cron Job)
    """
    now = datetime.utcnow()
    async with async_session() as session:
        stmt = select(Order).where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date <= now
        )
        res = await session.execute(stmt)
        expired_orders = res.scalars().all()

        deleted_count = 0
        for order in expired_orders:
            # حذف لینک
            if getattr(order, "assigned_link_id", None):
                await session.execute(
                    delete(LinkPool).where(LinkPool.id == order.assigned_link_id)
                )
            elif getattr(order, "assigned_link", None):
                await session.execute(
                    delete(LinkPool).where(LinkPool.url == order.assigned_link)
                )

            # حذف سفارش
            await session.delete(order)
            deleted_count += 1

        await session.commit()
        return deleted_count


# ──────────────── جایگزین تابع expire_order قبلی ────────────────
async def expire_order(order_id: int):
    """
    به جای تغییر وضعیت به expired، سفارش و لینک آن را کاملاً حذف می‌کند.
    """
    await delete_expired_order_and_link(order_id)



# دریافت جایزه
from database.models import UserGameLimit

async def get_user_game_last_played(user_id: int, game_type: str) -> datetime | None:
    """دریافت آخرین زمان بازی کاربر برای یک بازی خاص"""
    async with async_session() as session:
        stmt = select(UserGameLimit.last_played_at).where(
            UserGameLimit.user_id == user_id,
            UserGameLimit.game_type == game_type
        )
        res = await session.execute(stmt)
        return res.scalar_one_or_none()

async def set_user_game_last_played(user_id: int, game_type: str, played_at: datetime) -> None:
    """ثبت یا به‌روزرسانی زمان بازی کاربر"""
    async with async_session() as session:
        stmt = select(UserGameLimit).where(
            UserGameLimit.user_id == user_id,
            UserGameLimit.game_type == game_type
        )
        res = await session.execute(stmt)
        record = res.scalar_one_or_none()

        if record:
            record.last_played_at = played_at
        else:
            session.add(UserGameLimit(user_id=user_id, game_type=game_type, last_played_at=played_at))

        await session.commit()
        
        
        
        
        
        
        
        


# تغییر لینک کاربران
import re
from datetime import datetime
from sqlalchemy import select, update
from database.db import async_session
from database.models import Order, LinkPool, PaymentStatus

async def replace_active_links_pattern(old_str: str, new_str: str) -> list[dict]:
    """
    جایگزینی هوشمند و بدون حساسیت به حروف (Case-Insensitive) در تمام لینک‌های فعال و مخزن لینک‌ها
    """
    old_str = old_str.strip()
    new_str = new_str.strip()
    if not old_str or old_str == new_str:
        return []

    now = datetime.utcnow()
    updated_notifications = []

    # کامپایل الگوی Regex بدون حساسیت به بزرگی/کوچکی حروف
    pattern = re.compile(re.escape(old_str), re.IGNORECASE)

    async with async_session() as session:
        # ۱. استخراج سفارش‌های فعال
        stmt = select(Order).where(
            Order.payment_status == PaymentStatus.approved,
            Order.expiration_date > now,
            Order.assigned_link.is_not(None)
        )
        res = await session.execute(stmt)
        active_orders = res.scalars().all()

        for order in active_orders:
            old_link = order.assigned_link or ""

            # اگر عبارت قدیمی در لینک وجود داشت
            if pattern.search(old_link):
                new_link = pattern.sub(new_str, old_link)

                # اطمینان ۱۰۰٪ از اینکه لینک واقعاً تغییر کرده است
                if new_link != old_link:
                    # آپدیت مستقیم جدول سفارشات
                    await session.execute(
                        update(Order)
                        .where(Order.id == order.id)
                        .values(assigned_link=new_link)
                    )

                    # آپدیت در مخزن لینک‌ها (LinkPool)
                    if order.assigned_link_id:
                        await session.execute(
                            update(LinkPool)
                            .where(LinkPool.id == order.assigned_link_id)
                            .values(url=new_link)
                        )

                    updated_notifications.append({
                        "user_id": order.user_id,
                        "order_code": order.order_code,
                        "old_link": old_link,
                        "new_link": new_link
                    })

        # ۲. به‌روزرسانی کلیه لینک‌های موجود در مخزن (LinkPool)
        pool_res = await session.execute(select(LinkPool))
        all_pool_links = pool_res.scalars().all()
        for plk in all_pool_links:
            if plk.url and pattern.search(plk.url):
                updated_pool_url = pattern.sub(new_str, plk.url)
                if updated_pool_url != plk.url:
                    await session.execute(
                        update(LinkPool)
                        .where(LinkPool.id == plk.id)
                        .values(url=updated_pool_url)
                    )

        await session.commit()

    return updated_notifications