"""کوئری‌های صفحه تسک — پورت‌شده از core/repositories/task_repo.py دسکتاپ.

منطق دسته‌بندی تسک‌ها (نام‌ها و رنگ‌ها طبق config.py ثابت مونده، فقط منطق زیرشون تغییر کرده):
- تماس امروز (قرمز): تاریخ تماس بعدیِ مشتری = امروز (به وقت ایران)
- VIP بدون خرید (سبز): مشتری با تگ VIP که بیش از ۲ هفته از آخرین تماسش گذشته (یا هنوز تماسی نداشته)
- مشتری خوابیده (زرد): قبلاً باهاش تماس گرفته شده ولی بیش از ۲ ماه از آخرین تماسش گذشته
  (مشتری‌ای که هیچ‌وقت تماس نداشته «پرزنت نشده» است، نه خوابیده)
- پرزنت نشده (آبی): هیچ‌وقت تماس نگرفته و مجموع وزن خریدش صفر است
- تماس هفته آینده (بنفش): تاریخ تماس بعدیِ مشتری در بازه‌ی هفته‌ی تقویمی آینده است
- پیگیری این هفته (صورتی): مشتری‌هایی که در هفته‌ی جاری (از شنبه، به وقت ایران) براشون تماس ثبت شده
"""
from datetime import timedelta

from sqlalchemy import func

from app.extensions import db
from app.models.customer import Customer, Purchase
from app.models.call import Call
from app.models.tag import Tag, CustomerTag
from app.services import customer_service
from app.services.jalali import iran_today, next_week_range, this_week_utc_range, utc_now
from config import SLEEPING_AFTER_DAYS

VIP_TAG_NAME = "VIP"


def _cutoff_datetime(days: int):
    return utc_now() - timedelta(days=days)  # call_date به UTC ذخیره می‌شه


def _stats_query():
    stats = (
        db.session.query(
            Purchase.customer_id.label("customer_id"),
            func.sum(Purchase.amount).label("total_amount"),
            func.sum(Purchase.weight).label("total_weight"),
            func.max(Purchase.purchase_date).label("last_purchase_date"),
        )
        .group_by(Purchase.customer_id)
        .subquery()
    )
    calls = (
        db.session.query(Call.customer_id.label("customer_id"), func.max(Call.call_date).label("last_call_date"))
        .group_by(Call.customer_id)
        .subquery()
    )
    query = (
        db.session.query(
            Customer,
            func.coalesce(stats.c.total_amount, 0).label("total_amount"),
            func.coalesce(stats.c.total_weight, 0).label("total_weight"),
            stats.c.last_purchase_date,
            calls.c.last_call_date,
        )
        .outerjoin(stats, stats.c.customer_id == Customer.id)
        .outerjoin(calls, calls.c.customer_id == Customer.id)
        .filter(Customer.is_deleted.is_(False))
    )
    return query, stats, calls


def _finalize(rows):
    results = []
    for customer, total_amount, total_weight, last_purchase_date, last_call_date in rows:
        customer.computed_total_amount = total_amount or 0
        customer.computed_total_weight = total_weight or 0
        customer.computed_last_purchase_date = last_purchase_date
        customer.computed_last_call_date = last_call_date
        results.append(customer)
    return results


def _call_today(query, _stats, _calls):
    return query.filter(Customer.next_call_date == iran_today()).order_by(Customer.first_name, Customer.last_name)


def _call_next_week(query, _stats, _calls):
    start, end = next_week_range()
    return query.filter(Customer.next_call_date.between(start, end)).order_by(Customer.next_call_date)


def _vip_inactive(query, _stats, calls, days=14):
    cutoff = _cutoff_datetime(days)
    vip_ids = (
        db.session.query(CustomerTag.customer_id)
        .join(Tag, Tag.id == CustomerTag.tag_id)
        .filter(Tag.name == VIP_TAG_NAME)
    )
    return query.filter(
        Customer.id.in_(vip_ids),
        db.or_(calls.c.last_call_date.is_(None), calls.c.last_call_date < cutoff),
    ).order_by(Customer.first_name, Customer.last_name)


def _sleeping(query, _stats, calls):
    cutoff = _cutoff_datetime(SLEEPING_AFTER_DAYS)
    return query.filter(
        calls.c.last_call_date.isnot(None), calls.c.last_call_date < cutoff
    ).order_by(Customer.first_name, Customer.last_name)


def _not_presented(query, stats, calls):
    return query.filter(
        calls.c.last_call_date.is_(None),
        db.or_(stats.c.total_weight.is_(None), stats.c.total_weight == 0),
    ).order_by(Customer.joined_at.desc())


def _follow_up_week(query, _stats, calls):
    start, end = this_week_utc_range()  # call_date به UTC ذخیره می‌شه
    called_this_week = (
        db.session.query(Call.customer_id)
        .filter(Call.call_date >= start, Call.call_date < end)
    )
    return query.filter(Customer.id.in_(called_this_week)).order_by(calls.c.last_call_date.desc())


CATEGORY_QUERIES = {
    "call_today": _call_today,
    "vip_inactive": _vip_inactive,
    "sleeping": _sleeping,
    "not_presented": _not_presented,
    "call_next_week": _call_next_week,
    "follow_up_week": _follow_up_week,
}


def category_query(key):
    builder = CATEGORY_QUERIES.get(key)
    if builder is None:
        return None
    query, stats, calls = _stats_query()
    # شناسه به‌عنوان مرتب‌سازی آخر تا «نمایش همه» دقیقاً ادامه‌ی همون ۵۰ تای اول باشه
    return builder(query, stats, calls).order_by(Customer.id.desc())


def get_category_customers(key, search="", city=None, limit=None):
    """(ردیف‌ها، تعداد کل) — فیلتر شهر و جستجو داخل دیتابیس انجام می‌شه."""
    query = category_query(key)
    if query is None:
        return [], 0
    if city:
        query = query.filter(Customer.city == city)
    if search:
        query = query.filter(customer_service.search_condition(search))
    total = query.order_by(None).count()
    if limit:
        query = query.limit(limit)
    return _finalize(query.all()), total


def get_category_cities(key):
    query = category_query(key)
    if query is None:
        return []
    rows = (
        query.order_by(None)
        .with_entities(Customer.city)
        .filter(Customer.city.isnot(None), Customer.city != "")
        .distinct()
        .all()
    )
    return sorted(r[0] for r in rows)


def get_category_counts():
    return {key: category_query(key).order_by(None).count() for key in CATEGORY_QUERIES}
