"""تحليلات خط فيندور: أوردرات + مصروف إعلانات + ربح + تشخيص.

المنتجات هنا أسماء حقيقية من عمود "الأصناف" (مش فئات عامة)،
والمصروف بيتوزّع على المنتجات حسب مطابقة اسم المجموعة الإعلانية.
"""

import collections
import os
import re
from datetime import datetime

import ads_sync
import orders_import
import attribution

DATA_DIR = os.path.dirname(os.path.abspath(__file__))

MONTHS_AR = {
    1: 'يناير', 2: 'فبراير', 3: 'مارس', 4: 'أبريل', 5: 'مايو', 6: 'يونيو',
    7: 'يوليو', 8: 'أغسطس', 9: 'سبتمبر', 10: 'أكتوبر', 11: 'نوفمبر', 12: 'ديسمبر',
}

BENCH = {'deliv_rate': 35.0}
STOP_TOKENS = {'شباشب', 'شبشب', 'جلد', 'فيندور', 'رجالي', 'رجالى', 'حريمي',
               'حريمى', 'كود', 'مقاس', 'لون', 'كوتشي'}
ALIASES = {'سكتشر': 'skechers', 'سكيتشرز': 'skechers', 'سكتشرز': 'skechers',
           'تكس': 'tex', 'تكس12': 'tex12', 'تكس13': 'tex13'}


# --------------------------------------------------------------------------
# استخراج أسماء المنتجات
# --------------------------------------------------------------------------

def norm_name(value):
    s = value or ''
    s = s.replace('\u0640', '')
    s = re.sub(r'\([^)]*\)', ' ', s)
    # كمية بعلامة ضرب أو بمسافة قبلها فقط (مش جوّه اسم زي Tex12)
    s = re.sub(r'\s*[×✕]\s*\d+\s*$', ' ', s)
    s = re.sub(r'\s+[xX]\s*\d+\s*$', ' ', s)
    s = re.sub(r'[\u064B-\u0652]', '', s)
    s = s.replace('ک', 'ك').replace('ى', 'ي').replace('ة', 'ه')
    s = re.sub(r'\s+', ' ', s).strip(' -،,')
    return s


def split_items(text):
    parts = re.split(r'\s*\+\s*|\s*,\s*', text or '')
    out = []
    for p in parts:
        name = norm_name(p)
        if name:
            out.append(name)
    return out


def ag_tokens(name):
    n = norm_name(name).lower()
    toks = re.findall(r'[a-z0-9]{2,}|[\u0600-\u06FF]{2,}', n)
    result = []
    for t in toks:
        t = ALIASES.get(t, t)
        if t in STOP_TOKENS or t in result:
            continue
        result.append(t)
    return result


# --------------------------------------------------------------------------
# بيانات الأوردرات والمصروف
# --------------------------------------------------------------------------

def _cfg():
    cfg = ads_sync.load_config()
    cfg.setdefault('campaign_map', {})
    cfg.setdefault('line_map', {})
    return cfg


def _date_key(value):
    try:
        return datetime.strptime(value, '%d-%m-%Y')
    except (TypeError, ValueError):
        return datetime.min


def _match_month(date_str, month):
    return not month or month == 'الكل' or (date_str or '').endswith(f'-{month}')


def _match_day(date_str, day):
    return not day or day == 'الكل' or date_str == day


def _in_range(date_str, month='الكل', day='الكل', start=None, end=None):
    if start and end:
        try:
            d = datetime.strptime(date_str or '', '%d-%m-%Y').date()
            s = datetime.strptime(start, '%Y-%m-%d').date()
            e = datetime.strptime(end, '%Y-%m-%d').date()
            return s <= d <= e
        except ValueError:
            return False
    return _match_month(date_str, month) and _match_day(date_str, day)


def load_orders():
    return orders_import.load_orders('vendor')


def is_paid(r):
    return r.get('نزلت المحفظة') == 'نعم' or r.get('حالة الشحن') == 'تم تسليمها للعميل'


def months():
    seen = set()
    for r in load_orders():
        d = r.get('تاريخ الإنشاء', '')
        if len(d) == 10:
            seen.add(d[3:])
    result = []
    for key in sorted(seen, key=lambda k: datetime.strptime('01-' + k, '%d-%m-%Y'), reverse=True):
        m, y = key.split('-')
        result.append({'value': key, 'label': f"{MONTHS_AR[int(m)]} {y}"})
    return result


def days(month):
    seen = set()
    for r in load_orders():
        d = r.get('تاريخ الإنشاء', '')
        if _match_month(d, month) and len(d) == 10:
            seen.add(d)
    return sorted(seen, key=_date_key, reverse=True)


def vendor_spend_rows():
    """صفوف مصروف فيندور: [(platform, date, campaign_id, campaign, group, amount)]."""
    cfg = _cfg()
    out = []
    for platform in ('meta', 'google'):
        for date, items in (ads_sync.load_rows().get(platform) or {}).items():
            for r in items:
                if ads_sync.line_for(platform, r, cfg) != 'vendor':
                    continue
                group = r.get('adgroup') or r.get('name') or '(بدون مجموعة)'
                out.append((
                    platform,
                    date,
                    str(r.get('id') or ''),
                    r.get('name') or '',
                    group,
                    float(r.get('spend') or 0),
                ))
    return out


def ag_key(campaign_id, adgroup):
    return f"{campaign_id}|{adgroup}"


def _auto_candidates(products, adgroup):
    toks = ag_tokens(adgroup)
    if not toks:
        return []
    found = []
    for name, p in products.items():
        low = name.lower()
        if any(tok in low for tok in toks):
            found.append(p)
    return found


# --------------------------------------------------------------------------
# الحسابات
# --------------------------------------------------------------------------

def _build_products(rows):
    products = {}
    for r in rows:
        items = split_items(r.get('المحتوى', ''))
        if not items:
            items = ['غير محدد']
        count = collections.Counter(items)
        total_units = sum(count.values()) or 1
        try:
            comm = float(r.get('العمولة') or 0)
        except ValueError:
            comm = 0.0
        paid = is_paid(r)
        status = r.get('حالة الشحن', '')

        for name, units in count.items():
            p = products.setdefault(name, {
                'name': name, 'units': 0, 'orders': 0, 'delivered': 0,
                'returned': 0, 'cancelled': 0, 'commission': 0.0,
            })
            p['units'] += units
            p['orders'] += 1
            if paid:
                p['delivered'] += 1
                p['commission'] += comm * units / total_units
            elif status == 'مرتجع في المخزن':
                p['returned'] += 1
            elif status == 'ملغي':
                p['cancelled'] += 1
    return products


def _spend_allocation(products, spend_rows, cfg, month='الكل', day='الكل', start=None, end=None):
    """توزيع مصروف كل مجموعة إعلانية على المنتجات (يدوي أولاً ثم مطابقة الأسماء)."""
    mapping = cfg.get('vendor_map') or {}

    totals = {}
    for platform, date, cid, campaign, ag, amount in spend_rows:
        if not _in_range(date, month, day, start, end):
            continue
        key = ag_key(cid, ag)
        item = totals.setdefault(key, {'ag': ag, 'campaign': campaign, 'amount': 0.0})
        item['amount'] += amount

    shares = {}
    for key, item in totals.items():
        manual = mapping.get(key) or mapping.get(item['ag'])
        if manual:
            shares[key] = {manual: 1.0}
            continue
        candidates = _auto_candidates(products, item['ag'])
        if not candidates:
            continue
        weight = sum(max(p['units'], 1) for p in candidates)
        shares[key] = {p['name']: max(p['units'], 1) / weight for p in candidates}

    product_spend = collections.defaultdict(lambda: collections.defaultdict(float))
    platform_spend = collections.defaultdict(
        lambda: collections.defaultdict(lambda: collections.defaultdict(float)))
    unallocated = collections.defaultdict(float)
    for platform, date, cid, campaign, ag, amount in spend_rows:
        if not _in_range(date, month, day, start, end):
            continue
        key = ag_key(cid, ag)
        assigned = shares.get(key)
        if not assigned:
            unallocated[date] += amount
            continue
        for product_name, ratio in assigned.items():
            product_spend[product_name][date] += amount * ratio
            platform_spend[product_name][platform][date] += amount * ratio

    return product_spend, unallocated, platform_spend


def _filter_rows(month, day, product, start=None, end=None):
    rows = []
    for r in load_orders():
        d = r.get('تاريخ الإنشاء', '')
        if not _in_range(d, month, day, start, end):
            continue
        if product and product != 'الكل':
            if product not in split_items(r.get('المحتوى', '')):
                continue
        rows.append(r)
    return rows


def _stats(rows, spend_total):
    total = len(rows)
    delivered = shipping = returned = cancelled = pending = 0
    wallet = ship_comm = 0.0
    for r in rows:
        try:
            comm = float(r.get('العمولة') or 0)
        except ValueError:
            comm = 0.0
        st = r.get('حالة الشحن', '')
        if is_paid(r):
            delivered += 1
            wallet += comm
        elif st in ('خرج للشحن', 'جاهز للشحن'):
            shipping += 1
            ship_comm += comm
        elif st == 'مرتجع في المخزن':
            returned += 1
        elif st == 'ملغي':
            cancelled += 1
        else:
            pending += 1

    return {
        'total_orders': total,
        'delivered': delivered,
        'delivered_rate': round(delivered / total * 100, 1) if total else 0,
        'shipping': shipping,
        'shipping_comm': round(ship_comm, 0),
        'returned': returned,
        'cancelled': cancelled,
        'pending': pending,
        'wallet_comm': round(wallet, 0),
        'spend': round(spend_total, 2),
        'net_profit': round(wallet - spend_total, 0),
        'cpa': round(spend_total / total, 1) if total else 0,
    }


def _source_breakdown(rows, month='الكل', start=None, end=None):
    """لكل مصدر: إيراد محصّل + عمولة أوردرات لسه في الشحن."""
    matches = attribution.match('vendor', month, start, end)['matches']
    out = {k: {'rev': 0.0, 'ship': 0.0, 'ship_count': 0}
           for k in ('meta', 'google', 'direct', 'other', 'unknown')}
    for r in rows:
        rec = matches.get(r.get('رقم الطلب')) or {}
        source = rec.get('source') or 'unknown'
        if source in ('fb', 'ig', 'th', 'facebook'):
            bucket = 'meta'
        elif source == 'google':
            bucket = 'google'
        elif source == 'direct':
            bucket = 'direct'
        elif source == 'an':
            bucket = 'other'
        else:
            bucket = 'unknown'
        try:
            comm = float(r.get('العمولة') or 0)
        except ValueError:
            comm = 0.0
        if is_paid(r):
            out[bucket]['rev'] += comm
        elif r.get('حالة الشحن') in ('خرج للشحن', 'جاهز للشحن'):
            out[bucket]['ship'] += comm
            out[bucket]['ship_count'] += 1
    return out


def platform_compare(rows, platform_spend, month='الكل', start=None, end=None):
    """مقارنة ميتا وجوجل لمنتج واحد: إيراد/مصروف/صافي + أمل الشحن + حكم."""
    breakdown = _source_breakdown(rows, month, start, end)
    meta_spend = round(sum((platform_spend.get('meta') or {}).values()), 2)
    google_spend = round(sum((platform_spend.get('google') or {}).values()), 2)

    def pack(spend, data):
        revenue = data['rev']
        ship_comm = data['ship']
        net = round(revenue - spend, 0)
        potential = round(net + ship_comm * 0.7, 0)
        if spend == 0 and revenue == 0 and ship_comm == 0:
            verdict = 'بدون بيانات'
        elif spend == 0:
            verdict = 'إيراد بدون إعلانات'
        elif net > 0:
            verdict = 'رابح'
        elif potential > 0 and ship_comm > 0:
            verdict = 'خاسر حاليًا — في أمل'
        elif revenue == 0:
            verdict = 'خاسر — بدون إيراد مُسند'
        else:
            verdict = 'خاسر'
        return {
            'revenue': round(revenue, 0),
            'spend': round(spend, 2),
            'net': net,
            'potential': potential,
            'shipping_comm': round(ship_comm, 0),
            'shipping_count': data['ship_count'],
            'roas': round(revenue / spend, 2) if spend else 0,
            'verdict': verdict,
        }

    meta = pack(meta_spend, breakdown['meta'])
    google = pack(google_spend, breakdown['google'])

    if meta['spend'] == 0 and google['spend'] == 0:
        better = None
    elif meta['net'] <= 0 and google['net'] <= 0:
        better = 'both_loss'
    elif meta['net'] > google['net']:
        better = 'meta'
    elif google['net'] > meta['net']:
        better = 'google'
    else:
        better = 'tie'

    return {
        'meta': meta,
        'google': google,
        'organic': round(breakdown['direct']['rev'], 0),
        'other': round(breakdown['other']['rev'] + breakdown['unknown']['rev'], 0),
        'shipping_total': round(meta['shipping_comm'] + google['shipping_comm'], 0),
        'better': better,
        'diff': round(meta['net'] - google['net'], 0),
    }


def _easy_live(product, month='الكل', start=None, end=None):
    """عدد وحالات أوردرات المنتج لايف من إيزي أوردر (مش من تصدير فيندور)."""
    try:
        import easy_orders
        rows = easy_orders.load()
    except Exception:
        return None
    target = norm_name(product).lower()
    if len(target) < 8:
        return None
    count = 0
    by_status = collections.Counter()
    total_value = 0.0
    for r in rows:
        if not _in_range(r.get('date', ''), month, 'الكل', start, end):
            continue
        items = [norm_name(p).lower() for p in (r.get('items') or '').split(' | ')]
        if not any(it and (it == target or target in it) for it in items):
            continue
        count += 1
        by_status[r.get('status') or 'unknown'] += 1
        try:
            total_value += float(r.get('total') or 0)
        except ValueError:
            pass
    return {'orders': count, 'by_status': dict(by_status), 'value': round(total_value, 0)}


def dashboard(month='الكل', day='الكل', product='الكل', start=None, end=None):
    cfg = _cfg()
    all_rows = [r for r in load_orders()
                if _in_range(r.get('تاريخ الإنشاء', ''), month, day, start, end)]
    products_all = _build_products(all_rows)
    for mapped in (cfg.get('vendor_map') or {}).values():
        products_all.setdefault(mapped, {
            'name': mapped, 'units': 0, 'orders': 0, 'delivered': 0,
            'returned': 0, 'cancelled': 0, 'commission': 0.0,
        })
    spend_rows = vendor_spend_rows()
    product_spend_map, unallocated, platform_spend_map = _spend_allocation(
        products_all, spend_rows, cfg, month, day, start, end)

    rows = _filter_rows(month, day, product, start, end)
    if product and product != 'الكل':
        spend_total = round(sum(product_spend_map.get(product, {}).values()), 2)
    else:
        spend_total = round(sum(sum(d.values()) for d in product_spend_map.values())
                            + sum(unallocated.values()), 2)

    stats = _stats(rows, spend_total)

    live = _easy_live(product, month, start, end) if product and product != 'الكل' else None
    if live and live.get('orders'):
        stats['vendor_orders'] = stats['total_orders']
        stats['total_orders'] = live['orders']
        stats['orders_source'] = 'easy'
        stats['live_value'] = live['value']
        stats['live_statuses'] = live['by_status']
        stats['delivered_rate_vendor'] = stats.get('delivered_rate')
        if live['orders']:
            stats['cpa'] = round(spend_total / live['orders'], 1)
            stats['delivered_rate'] = round(stats['delivered'] / live['orders'] * 100, 1)

    # جدول المنتجات
    visible = rows if product in (None, '', 'الكل') else rows
    table_source = _build_products(visible)
    if product and product != 'الكل':
        table_source = {product: products_all[product]} if product in products_all else {}

    table = []
    for name, p in table_source.items():
        spend = round(sum(product_spend_map.get(name, {}).values()), 2) if product in (None, '', 'الكل') else spend_total
        delivered = p['delivered']
        table.append({
            'name': name,
            'orders': p['orders'],
            'delivered': delivered,
            'deliv_rate': round(delivered / p['orders'] * 100, 1) if p['orders'] else 0,
            'wallet': round(p['commission'], 0),
            'spend': spend,
            'net': round(p['commission'] - spend, 0),
            'cpa': round(spend / p['orders'], 1) if p['orders'] else 0,
            'units': p['units'],
        })
    table.sort(key=lambda x: x['orders'], reverse=True)
    if live and live.get('orders') and product and product != 'الكل' and table:
        table[0]['orders'] = live['orders']
        table[0]['deliv_rate'] = round(table[0]['delivered'] / live['orders'] * 100, 1)
        table[0]['cpa'] = round(spend_total / live['orders'], 1) if live['orders'] else 0

    # رسم يومي
    daily = collections.defaultdict(lambda: {'orders': 0, 'delivered': 0, 'wallet': 0.0, 'spend': 0.0})
    for r in rows:
        d = r.get('تاريخ الإنشاء', '')
        daily[d]['orders'] += 1
        if is_paid(r):
            daily[d]['delivered'] += 1
            try:
                daily[d]['wallet'] += float(r.get('العمولة') or 0)
            except ValueError:
                pass
    if product and product != 'الكل':
        for date, amount in product_spend_map.get(product, {}).items():
            daily[date]['spend'] += amount
    else:
        for name, dates in product_spend_map.items():
            for date, amount in dates.items():
                daily[date]['spend'] += amount
        for date, amount in unallocated.items():
            daily[date]['spend'] += amount

    dates = sorted([d for d in daily if len(d) == 10], key=_date_key)
    chart = {
        'dates': dates,
        'orders': [daily[d]['orders'] for d in dates],
        'delivered': [daily[d]['delivered'] for d in dates],
        'wallet': [round(daily[d]['wallet'], 0) for d in dates],
        'spend': [round(daily[d]['spend'], 0) for d in dates],
    }

    return {
        'stats': stats,
        'products': table[:40],
        'product_count': len(table),
        'chart': chart,
        'diagnosis': diagnosis(stats, table),
        'filters': {'month': month, 'day': day, 'product': product},
        'unallocated_spend': round(sum(unallocated.values()), 2),
        'platforms': platform_compare(
            rows, platform_spend_map.get(product, {}) if product and product != 'الكل' else {},
            month, start, end) if product and product != 'الكل' else None,
        'live': live,
    }


def diagnosis(stats, products):
    issues = []

    def add(area, severity, detail, action):
        issues.append({'area': area, 'severity': round(max(0.0, min(1.0, severity)), 2),
                       'detail': detail, 'action': action})

    if stats['total_orders'] and stats['delivered_rate'] < BENCH['deliv_rate']:
        add('التسليم', (BENCH['deliv_rate'] - stats['delivered_rate']) / BENCH['deliv_rate'],
            f"التسليم {stats['delivered_rate']}% (المعتاد ≥ {BENCH['deliv_rate']}%)",
            'أكد الأوردر والمقاس قبل الشحن، وراجع بيانات العميل.')

    if stats['delivered'] and stats['spend'] and (stats['spend'] / stats['delivered']) > (stats['wallet_comm'] / stats['delivered']):
        cost = stats['spend'] / stats['delivered']
        comm = stats['wallet_comm'] / stats['delivered']
        add('اقتصاديات الوحدة', (cost - comm) / max(comm, 1),
            f"تكلفة الأوردر المسلّم {cost:.0f} ج مقابل عمولة {comm:.0f} ج",
            'قلل الميزانية أو ركّز على منتجات بعمولة أعلى.')

    net = stats['net_profit']
    if net < 0:
        status = 'خاسر'
    elif stats['delivered'] == 0:
        status = 'لسه بيبدأ'
    else:
        status = 'رابح'

    worst = None
    for p in products:
        if p['orders'] >= 10 and p['deliv_rate'] < BENCH['deliv_rate']:
            if worst is None or p['deliv_rate'] < worst['deliv_rate']:
                worst = p
    if worst:
        add('منتج ضعيف', (BENCH['deliv_rate'] - worst['deliv_rate']) / BENCH['deliv_rate'],
            f"{worst['name'][:40]}: تسليم {worst['deliv_rate']}% من {worst['orders']} أوردر",
            'راجع وصف المنتج والمقاسات مع العميل قبل الشحن.')

    issues.sort(key=lambda x: x['severity'], reverse=True)
    primary = issues[0] if issues else None
    advice = primary['action'] if primary else (
        'الأرقام متوازنة — كمّل وراقب التسليم.' if status == 'رابح' else 'استنى دفعة تسليم أكبر قبل الحكم.')

    return {'status': status, 'primary': primary, 'issues': issues[:3], 'advice': advice}


def adgroups(month='الكل', start=None, end=None):
    """مجموعات فيندور الإعلانية + المصروف + المنتج الحالي (يدوي أو تلقائي)."""
    cfg = _cfg()
    rows = [r for r in load_orders() if _in_range(r.get('تاريخ الإنشاء', ''), month, 'الكل', start, end)]
    products = _build_products(rows)
    mapping = cfg.get('vendor_map') or {}

    totals = {}
    for platform, date, cid, campaign, ag, amount in vendor_spend_rows():
        if not _in_range(date, month, 'الكل', start, end):
            continue
        key = ag_key(cid, ag)
        item = totals.setdefault(key, {'key': key, 'campaign': campaign, 'name': ag, 'spend': 0.0})
        item['spend'] += amount

    out = []
    for key, item in totals.items():
        manual = mapping.get(key) or mapping.get(item['name'])
        if manual:
            product, mapped = manual, True
        else:
            candidates = _auto_candidates(products, item['name'])
            product = max(candidates, key=lambda p: p['units'])['name'] if candidates else ''
            mapped = False
        item['spend'] = round(item['spend'], 2)
        item['product'] = product
        item['mapped'] = mapped
        out.append(item)
    out.sort(key=lambda x: x['spend'], reverse=True)
    return out


def set_mapping(key, product):
    cfg = ads_sync.load_config()
    mapping = cfg.setdefault('vendor_map', {})
    if product:
        mapping[key] = product
    else:
        mapping.pop(key, None)
    ads_sync.save_config(cfg)


def product_options(month='الكل'):
    rows = [r for r in load_orders() if _match_month(r.get('تاريخ الإنشاء', ''), month)]
    products = _build_products(rows)
    for mapped in (_cfg().get('vendor_map') or {}).values():
        products.setdefault(mapped, {'name': mapped, 'orders': 0, 'units': 0})
    ordered = sorted(products.values(), key=lambda p: p.get('orders', 0), reverse=True)
    return [{'name': p['name'], 'orders': p.get('orders', 0)} for p in ordered[:80]]


def orders_page(month='الكل', day='الكل', product='الكل', status='الكل', q='', page=1, limit=25,
                start=None, end=None):
    rows = _filter_rows(month, day, product, start, end)
    if q:
        needle = q.lower()
        rows = [r for r in rows if needle in f"{r.get('رقم الطلب','')} {r.get('اسم العميل','')} {r.get('رقم العميل','')} {r.get('المحافظة','')}".lower()]
    if status != 'الكل':
        def matches(r):
            st = r.get('حالة الشحن', '')
            if status == 'مسلّم':
                return is_paid(r)
            if status == 'شحن':
                return st in ('خرج للشحن', 'جاهز للشحن')
            if status == 'مرتجع':
                return st == 'مرتجع في المخزن'
            if status == 'ملغي':
                return st == 'ملغي'
            return True
        rows = [r for r in rows if matches(r)]

    rows.sort(key=lambda r: (_date_key(r.get('تاريخ الإنشاء')), r.get('رقم الطلب', '')), reverse=True)
    total = len(rows)
    start = (page - 1) * limit
    items = [{
        'id': r.get('رقم الطلب'),
        'date': r.get('تاريخ الإنشاء'),
        'name': r.get('اسم العميل'),
        'phone': r.get('رقم العميل'),
        'gov': r.get('المحافظة'),
        'status': r.get('حالة الشحن'),
        'items': r.get('المحتوى'),
        'total': r.get('الإجمالي'),
        'comm': r.get('العمولة'),
        'wallet': r.get('نزلت المحفظة'),
    } for r in rows[start:start + limit]]
    return {'total': total, 'page': page, 'orders': items}
