import http.server
import socketserver
import os
import json
import csv
import urllib.parse
import urllib.request
import ssl
import re
import sys
import calendar
import collections
import threading
import time
from datetime import date, datetime

import ads_sync
import orders_import
import vendor_stats
import easy_orders
import attribution

if hasattr(sys.stdout, 'reconfigure'):
    try:
        sys.stdout.reconfigure(encoding='utf-8', errors='replace')
    except Exception:
        pass

PORT = int(os.environ.get("DASHBOARD_PORT", "5000"))
HOST = os.environ.get("DASHBOARD_HOST", "")
PORT_CANDIDATES = [PORT] + [p for p in range(5001, 5011) if p != PORT]
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
WEB_DIR = os.path.join(DATA_DIR, "web")
ORDERS_CSV = os.path.join(DATA_DIR, "orders.csv")
CONFIG_FILE = os.path.join(DATA_DIR, "config.json")

DEFAULT_CONFIG = {
    "easyorders_api_key": "12f9fb83-8bfc-420f-a136-6981747af8de",
    "google_sheet_url": "",
    "sheet_auto": False,
    "sheet_platform": "google",
    "spend_sources_enabled": ["manual"],
    "campaign_map": {},
    "vendor_map": {},
    "line_map": {},
    "meta": {"access_token": "", "ad_account_id": "", "api_version": "v21.0"},
    "meta_accounts": [],
    "meta_auto": True,
    "meta_auto_minutes": 15,
    "easy_webhook_secret": "",
    "easy_auto_gap": True,
    "web_user": "admin",
    "web_pass": "",
    "google_ads": {
        "developer_token": "", "client_id": "", "client_secret": "",
        "refresh_token": "", "customer_id": "", "login_customer_id": "",
        "api_version": "v21",
    },
}

# معايير المقارنة (بنشوف مين أكبر "تسريب" في القمع)
BENCH = {
    "ctr": 1.5,                 # % نقرات من الظهور
    "cvr": 2.0,                 # % شراء من زيارة لاند
    "atc_rate": 3.0,            # % إضافة للسلة من زيارة
    "checkout_completion": 40.0,  # % إكمال الشراء من بدء الشيك أوت
    "deliv_rate": 35.0,         # % تسليم من إجمالي الأوردرات
}

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE


def load_config():
    cfg = ads_sync._read_json(CONFIG_FILE, {})
    merged = json.loads(json.dumps(DEFAULT_CONFIG))
    for k, v in cfg.items():
        if isinstance(v, dict) and isinstance(merged.get(k), dict):
            merged[k].update(v)
        else:
            merged[k] = v
    return merged


def save_config(cfg):
    ads_sync.save_config(cfg)


def load_ad_spend():
    return ads_sync.load_aggregate()


def classify_product(c):
    if not c:
        return 'أخرى'
    if 'ميجا' in c:
        return 'كوتشي ميجا'
    if 'M شوز' in c:
        return 'كوتشي إم'
    if 'كوتشي نيو بوما' in c:
        return 'نيو بوما'
    if 'Puma' in c or 'بوما' in c:
        return 'بوما Puma'
    return 'أخرى'


def load_orders(source='kzamiza'):
    return orders_import.load_orders(source)


def product_matches(prod_filter, product):
    if not prod_filter or prod_filter == 'الكل':
        return True
    if prod_filter == 'بوما كلي':
        return product in ('بوما Puma', 'نيو بوما')
    return product == prod_filter


def month_matches(date_str, month_filter):
    if not month_filter or month_filter == 'الكل':
        return True
    return (date_str or '').endswith(f"-{month_filter}")


def parse_date_param(value):
    for fmt in ('%Y-%m-%d', '%d-%m-%Y'):
        try:
            return datetime.strptime(value, fmt).date()
        except (TypeError, ValueError):
            continue
    return None


def resolve_range(params):
    """يرجّع (بداية، نهاية) من start/end أو month."""
    start = parse_date_param(params.get('start', [None])[0])
    end = parse_date_param(params.get('end', [None])[0])
    if start and end:
        return start, end
    month = params.get('month', ['الكل'])[0]
    if month and month != 'الكل':
        try:
            m, y = month.split('-')
            m, y = int(m), int(y)
            last = calendar.monthrange(y, m)[1]
            return date(y, m, 1), date(y, m, last)
        except (ValueError, calendar.IllegalMonthError):
            return None
    return None


def date_ok(value, rng):
    if not rng:
        return True
    try:
        d = datetime.strptime(value or '', '%d-%m-%Y').date()
    except ValueError:
        return False
    return rng[0] <= d <= rng[1]


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


def spend_total(agg, rng, prod_filter='الكل'):
    total = 0.0
    for d, products in agg.items():
        if not date_ok(d, rng):
            continue
        for p, amount in products.items():
            if product_matches(prod_filter, p):
                total += amount
    return total


def spend_by_product(agg, rng):
    result = {p: 0.0 for p in ads_sync.PRODUCTS}
    for d, products in agg.items():
        if not date_ok(d, rng):
            continue
        for p, amount in products.items():
            result[p] = result.get(p, 0.0) + amount
    return result


def sync_from_google_sheet(sheet_url):
    m = re.search(r'/spreadsheets/d/([a-zA-Z0-9-_]+)', sheet_url)
    if not m:
        raise ValueError("رابط شيت جوجل غير صالح. تأكد من نسخ رابط الشيت كاملاً.")
    sheet_id = m.group(1)
    csv_url = f"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=csv"

    req = urllib.request.Request(csv_url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(req, context=ctx, timeout=12) as resp:
        content = resp.read().decode('utf-8')

    reader = csv.DictReader(content.splitlines())
    manual = ads_sync.load_manual()
    updated_count = 0

    for row in reader:
        date = (row.get('Date') or row.get('تاريخ') or '').strip()
        prod = (row.get('Product') or row.get('المنتج') or '').strip()
        cost_str = (row.get('Cost') or row.get('المصروف') or '0').replace(',', '').strip()

        if not prod and ('CampaignName' in row or 'الحملة' in row):
            c_name = row.get('CampaignName') or row.get('الحملة') or ''
            prod = classify_product(c_name)

        try:
            cost = float(cost_str)
        except ValueError:
            cost = 0.0

        if date and prod and cost > 0:
            date = ads_sync.norm_date(date)
            product = ads_sync.normalize_product(prod)
            manual.setdefault(date, {})
            manual[date][product] = round(float(manual[date].get(product, 0)) + cost, 2)
            updated_count += 1

    ads_sync._write_json(ads_sync.MANUAL_FILE, manual)
    cfg = load_config()
    if 'manual' not in ads_sync.enabled_sources(cfg):
        cfg.setdefault('spend_sources_enabled', []).append('manual')
        save_config(cfg)
    ads_sync.rebuild(cfg)
    return updated_count


def _webhook_state(ok=True, order_id=None, rejected=False):
    path = orders_import.STATE_FILE
    state = {}
    try:
        if os.path.exists(path):
            with open(path, "r", encoding="utf-8") as f:
                state = json.load(f)
        info = state.get("easy_webhook") or {}
        now = datetime.now().strftime("%Y-%m-%d %H:%M")
        if rejected:
            info["last_reject_at"] = now
            info["rejected_count"] = int(info.get("rejected_count") or 0) + 1
        else:
            info["last_at"] = now
            info["last_order"] = order_id
            if ok:
                info["count"] = int(info.get("count") or 0) + 1
            else:
                info["last_error"] = "تعذر حفظ الأوردر"
        state["easy_webhook"] = info
        with open(path, "w", encoding="utf-8") as f:
            json.dump(state, f, ensure_ascii=False, indent=2)
    except Exception:
        pass


def _fetch_order_uuid(order_id):
    cfg = load_config()
    key = cfg.get("easyorders_api_key") or ""
    if not key:
        return None
    url = f"https://api.easy-orders.net/api/v1/external-apps/orders/{order_id}"
    req = urllib.request.Request(url, headers={"Api-Key": key})
    try:
        with urllib.request.urlopen(req, context=ctx, timeout=20) as resp:
            return json.loads(resp.read().decode("utf-8", "replace"))
    except Exception:
        return None


def process_webhook_order(order_id):
    """يعالج الويبهوك في الخلفية: يجيب الأوردر ويحفظه."""
    order = _fetch_order_uuid(order_id) or easy_orders.fetch_by_short(order_id)
    if not order:
        _webhook_state(False, None)
        return
    record = easy_orders.record_from_api_order(order)
    if record.get("id") and record.get("date"):
        easy_orders.save([record])
        _webhook_state(True, record.get("id"))
    else:
        _webhook_state(False, None)


_VENDOR_NAMES_CACHE = {"ts": 0.0, "names": set()}


def _vendor_product_names():
    now = time.time()
    if _VENDOR_NAMES_CACHE["names"] and now - _VENDOR_NAMES_CACHE["ts"] < 300:
        return _VENDOR_NAMES_CACHE["names"]
    names = set()
    try:
        for p in vendor_stats.product_options('الكل'):
            n = vendor_stats.norm_name(p.get('name') or '').lower()
            if len(n) >= 8:
                names.add(n)
    except Exception:
        pass
    _VENDOR_NAMES_CACHE["names"] = names
    _VENDOR_NAMES_CACHE["ts"] = now
    return names


def _is_vendor_item(item, vendor_names):
    n = vendor_stats.norm_name(item or '').lower()
    if not n:
        return False
    for v in vendor_names:
        if v in n or (len(n) >= 8 and n in v):
            return True
    return False


class DashboardHandler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=WEB_DIR, **kwargs)

    # ------------------------------------------------------------------
    # GET
    # ------------------------------------------------------------------
    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        path = parsed.path
        params = urllib.parse.parse_qs(parsed.query)

        try:
            if path == "/api/stats":
                self.send_json(self.get_stats(params))
            elif path == "/api/chart":
                self.send_json(self.get_chart_data(params))
            elif path == "/api/products":
                self.send_json(self.get_products_summary(params))
            elif path == "/api/products/platforms":
                self.send_json(self.get_products_platforms(params))
            elif path == "/api/platform-profit":
                self.send_json(self.get_platform_profit(params))
            elif path == "/api/orders":
                self.send_json(self.get_orders_list(params))
            elif path == "/api/ad-spend":
                self.send_json(load_ad_spend())
            elif path == "/api/ads/status":
                self.send_json(self.get_ads_status(params))
            elif path == "/api/orders/summary":
                self.send_json(self.get_orders_summary(params))
            elif path == "/api/easy/webhook":
                self.send_json(self.easy_webhook_status())
            elif path == "/api/easy/orders":
                self.send_json(easy_orders.orders_page(
                    params.get('start', [None])[0],
                    params.get('end', [None])[0],
                    params.get('source', ['الكل'])[0],
                    params.get('q', [''])[0],
                    int(params.get('page', [1])[0]),
                    int(params.get('limit', [25])[0])))
            elif path == "/api/vendor/summary":
                self.send_json(self.get_vendor_summary(params))
            elif path == "/api/vendor/months":
                self.send_json(vendor_stats.months())
            elif path == "/api/vendor/days":
                self.send_json(vendor_stats.days(params.get('month', ['الكل'])[0]))
            elif path == "/api/vendor/products":
                self.send_json(vendor_stats.product_options(params.get('month', ['الكل'])[0]))
            elif path == "/api/vendor/adgroups":
                self.send_json(vendor_stats.adgroups(
                    params.get('month', ['الكل'])[0],
                    params.get('start', [None])[0],
                    params.get('end', [None])[0]))
            elif path == "/api/vendor/dashboard":
                self.send_json(vendor_stats.dashboard(
                    params.get('month', ['الكل'])[0],
                    params.get('day', ['الكل'])[0],
                    params.get('product', ['الكل'])[0],
                    params.get('start', [None])[0],
                    params.get('end', [None])[0]))
            elif path == "/api/vendor/orders":
                self.send_json(vendor_stats.orders_page(
                    params.get('month', ['الكل'])[0],
                    params.get('day', ['الكل'])[0],
                    params.get('product', ['الكل'])[0],
                    params.get('status', ['الكل'])[0],
                    params.get('q', [''])[0],
                    int(params.get('page', [1])[0]),
                    int(params.get('limit', [25])[0]),
                    params.get('start', [None])[0],
                    params.get('end', [None])[0]))
            elif path == "/api/funnel":
                self.send_json(self.get_funnel(params))
            elif path == "/api/diagnose":
                self.send_json(self.get_diagnose(params))
            elif path == "/api/ads/breakdown":
                self.send_json(self.get_breakdown(params))
            elif path == "/api/config":
                self.send_json(self.sanitized_config())
            else:
                super().do_GET()
        except Exception as e:
            self.send_json({"error": str(e)}, status=500)

    # ------------------------------------------------------------------
    # POST
    # ------------------------------------------------------------------
    def do_POST(self):
        parsed = urllib.parse.urlparse(self.path)
        path = parsed.path
        content_len = int(self.headers.get('Content-Length', 0))
        post_body = self.rfile.read(content_len).decode('utf-8')
        try:
            payload = json.loads(post_body) if post_body else {}
        except Exception:
            payload = {}

        try:
            if path == "/api/ad-spend":
                self.handle_manual_spend(payload)
            elif path == "/api/config":
                self.handle_save_config(payload)
            elif path == "/api/ads/save":
                self.handle_ads_save(payload)
            elif path == "/api/ads/test":
                self.handle_ads_test(payload)
            elif path == "/api/ads/sync":
                self.handle_ads_sync(payload)
            elif path == "/api/ads/import":
                self.handle_ads_import(payload)
            elif path == "/api/ads/map":
                self.handle_ads_map(payload)
            elif path == "/api/ads/line":
                self.handle_ads_line(payload)
            elif path == "/api/ads/meta-account":
                self.handle_meta_account(payload)
            elif path == "/api/ads/meta-auto":
                self.handle_meta_auto(payload)
            elif path == "/api/vendor/map":
                self.handle_vendor_map(payload)
            elif path == "/api/easy/webhook-secret":
                self.handle_easy_secret(payload)
            elif path == "/api/easy/sync":
                self.handle_easy_sync(payload)
            elif path == "/easy/webhook":
                self.handle_easy_webhook(payload)
            elif path == "/api/ads/sources":
                self.handle_ads_sources(payload)
            elif path == "/api/ads/google-sheet":
                self.handle_google_sheet(payload)
            elif path == "/api/orders/import":
                self.handle_orders_import(payload)
            else:
                self.send_json({"success": False, "error": "Endpoint not found"}, status=404)
        except ads_sync.AdsError as e:
            self.send_json({"success": False, "error": str(e)}, status=400)
        except Exception as e:
            self.send_json({"success": False, "error": f"خطأ غير متوقع: {e}"}, status=500)

    # ------------------------------------------------------------------
    # معالجات POST
    # ------------------------------------------------------------------
    def handle_manual_spend(self, payload):
        cfg = load_config()
        date = (payload.get("date") or "").strip()
        product = ads_sync.normalize_product(payload.get("product"))
        try:
            amount = float(payload.get("amount") or 0)
        except (TypeError, ValueError):
            amount = 0
        if not date or amount <= 0:
            self.send_json({"success": False, "error": "أدخل تاريخ ومبلغ صحيحين"}, status=400)
            return

        date = ads_sync.norm_date(date)

        manual = ads_sync.load_manual()
        manual.setdefault(date, {})[product] = round(amount, 2)
        ads_sync._write_json(ads_sync.MANUAL_FILE, manual)

        sources = ads_sync.enabled_sources(cfg)
        if 'manual' not in sources:
            sources.append('manual')
            cfg['spend_sources_enabled'] = sources
            save_config(cfg)

        ads_sync.rebuild(cfg)
        self.send_json({"success": True, "message": "تم حفظ المصروف بنجاح"})

    def handle_save_config(self, payload):
        cfg = load_config()
        for key in ("easyorders_api_key", "google_sheet_url"):
            if key in payload:
                cfg[key] = payload[key]
        save_config(cfg)
        self.send_json({"success": True, "message": "تم حفظ الإعدادات", "config": self.sanitized_config()})

    def handle_ads_save(self, payload):
        cfg = load_config()
        clear = payload.get("clear_platform")
        if clear == "meta":
            cfg["meta"] = {"access_token": "", "ad_account_id": "", "api_version": "v21.0"}
            cfg["spend_sources_enabled"] = [s for s in ads_sync.enabled_sources(cfg) if s != "meta"]
        elif clear == "google":
            cfg["google_ads"] = {
                "developer_token": "", "client_id": "", "client_secret": "",
                "refresh_token": "", "customer_id": "", "login_customer_id": "",
                "api_version": "v21",
            }
            cfg["spend_sources_enabled"] = [s for s in ads_sync.enabled_sources(cfg) if s != "google"]
        else:
            ads_sync.merge_config(cfg, {
                "meta": payload.get("meta") or {},
                "google_ads": payload.get("google_ads") or {},
            })
        if payload.get("google_sheet_url") is not None:
            cfg["google_sheet_url"] = payload["google_sheet_url"]
        if not cfg.get("spend_sources_enabled"):
            cfg["spend_sources_enabled"] = ["manual"]
        save_config(cfg)
        ads_sync.rebuild(cfg)
        self.send_json({"success": True, "message": "تم حفظ بيانات الربط", "status": self.build_status('الكل')})

    def handle_ads_test(self, payload):
        platform = payload.get("platform")
        account = payload.get("account")
        try:
            info = ads_sync.test(platform, load_config(), account)
        except ads_sync.AdsError as e:
            self.send_json({"success": False, "error": str(e)}, status=400)
            return
        self.send_json({"success": True, "message": "الاتصال سليم ✅", "account": info})

    def handle_ads_sync(self, payload):
        platform = payload.get("platform")
        month = payload.get("month") or 'الكل'
        account = payload.get("account") or None
        cfg = load_config()
        result = ads_sync.sync(platform, cfg, month, account)
        self.send_json({
            "success": True,
            "message": f"تم سحب {result['rows']} صف بإجمالي {result['total']:,.0f} ج.م",
            "result": result,
            "status": self.build_status(month),
        })

    def handle_ads_import(self, payload):
        platform = payload.get("platform")
        content = payload.get("content") or ""
        filename = payload.get("filename") or ""
        if not content:
            self.send_json({"success": False, "error": "الملف فاضي"}, status=400)
            return
        if len(content) > 12_000_000:
            self.send_json({"success": False, "error": "الملف كبير جداً (أقصى 12 ميجا)"}, status=400)
            return
        cfg = load_config()
        result = ads_sync.import_report(platform, content, cfg, filename)
        self.send_json({
            "success": True,
            "message": f"تم استيراد {result['rows']} صف بإجمالي {result['total']:,.0f} ج.م",
            "result": result,
            "status": self.build_status('الكل'),
        })

    def handle_orders_import(self, payload):
        filename = (payload.get("filename") or "").strip()
        content = payload.get("content") or ""
        source = payload.get("source") or "kzamiza"
        if source not in ("kzamiza", "vendor", "easy"):
            self.send_json({"success": False, "error": "مصدر أوردرات غير معروف"}, status=400)
            return
        if not content:
            self.send_json({"success": False, "error": "الملف فاضي"}, status=400)
            return
        if len(content) > 40_000_000:
            self.send_json({"success": False, "error": "الملف كبير جداً (أقصى 30 ميجا)"}, status=400)
            return
        try:
            if source == "easy":
                result = easy_orders.import_file(content, filename)
                self.record_easy_import(result, filename)
                self.send_json({
                    "success": True,
                    "message": f"تم استيراد {result['added']} أوردر من إيزي أوردر — الإجمالي {result['rows']}",
                    "result": result,
                })
                return
            result = orders_import.import_orders(filename, content, source)
        except orders_import.OrdersImportError as e:
            self.send_json({"success": False, "error": str(e)}, status=400)
            return
        except easy_orders.EasyImportError as e:
            self.send_json({"success": False, "error": str(e)}, status=400)
            return
        label = orders_import.LINE_NAMES.get(source, source)
        self.send_json({
            "success": True,
            "message": f"تم استيراد {result['added']} أوردر جديد وتحديث {result['updated']} — {label}: {result['rows']} أوردر",
            "result": result,
        })

    def get_orders_summary(self, params):
        source = params.get('source', ['kzamiza'])[0]
        if source == 'easy':
            return easy_orders.summary()
        return orders_import.summary(source)

    def get_vendor_summary(self, params):
        month = params.get('month', ['11-2026'])[0]
        data = vendor_stats.dashboard(month, 'الكل', 'الكل')
        stats = data['stats']
        stats['configured'] = bool(orders_import.load_orders('vendor'))
        stats['month'] = month
        stats['products'] = [
            {'name': p['name'], 'orders': p['orders'], 'wallet': p['wallet']}
            for p in data['products'][:6]
        ]
        return stats

    def get_funnel(self, params):
        month = params.get('month', ['11-2026'])[0]
        return ads_sync.funnel(load_config(), month,
                               params.get('start', [None])[0],
                               params.get('end', [None])[0])

    def get_breakdown(self, params):
        month = params.get('month', ['11-2026'])[0]
        level = params.get('level', ['campaign'])[0]
        if level not in ('campaign', 'adgroup', 'ad'):
            level = 'campaign'
        return ads_sync.breakdown(load_config(), month, level,
                                  params.get('start', [None])[0],
                                  params.get('end', [None])[0])

    def get_diagnose(self, params):
        products = self.get_products_summary(params)
        month = params.get('month', ['11-2026'])[0]
        funnel_map = {p['name']: p for p in ads_sync.funnel(
            load_config(), month,
            params.get('start', [None])[0],
            params.get('end', [None])[0])['products']}

        results = []
        for p in products:
            f = funnel_map.get(p['name']) or {}
            if p['orders'] == 0 and not f.get('lp_views'):
                continue
            results.append(self.diagnose_product(p, f))

        order = {'خاسر': 0, 'لسه بيبدأ': 1, 'بدون أوردرات': 2, 'رابح': 3}
        results.sort(key=lambda r: (order.get(r['status'], 9), r['net']))
        return results

    def diagnose_product(self, p, f):
        orders = p['orders']
        delivered = p['delivered']
        wallet = p['wallet']
        spend = p['ad_spend']
        net = p['net_actual']
        deliv_rate = p['deliv_rate']
        lp = f.get('lp_views') or 0
        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 lp:
            if f['cvr'] < BENCH['cvr']:
                add('اللاند بيدج', (BENCH['cvr'] - f['cvr']) / BENCH['cvr'],
                    f"التحويل {f['cvr']:.1f}% (المعتاد ≥ {BENCH['cvr']}%)",
                    'راجع أول شاشة: السعر، وضوح العرض، صور المنتج، وسرعة التحميل.')
            if f['atc_rate'] < BENCH['atc_rate']:
                add('العرض / المنتج', (BENCH['atc_rate'] - f['atc_rate']) / BENCH['atc_rate'],
                    f"إضافة للسلة {f['atc_rate']:.1f}% فقط",
                    'جرّب عرض أو سعر مختلف، أو أضف صور/فيديو حقيقي للمنتج.')
            if f['ctr'] < BENCH['ctr']:
                add('الكرياتيف / الجمهور', (BENCH['ctr'] - f['ctr']) / BENCH['ctr'],
                    f"CTR {f['ctr']:.1f}% — الإعلان مش بيشد الناس",
                    'غيّر الفيديو/الصورة الأولى، أو وسّع الجمهور.')
            if f['checkouts'] and f['purchases'] / f['checkouts'] * 100 < BENCH['checkout_completion']:
                completion = f['purchases'] / f['checkouts'] * 100
                add('الشيك أوت', (BENCH['checkout_completion'] - completion) / BENCH['checkout_completion'],
                    f"{completion:.0f}% فقط من اللي بدأوا الدفع كمّلوا",
                    'بسّط خطوات الطلب، وفّر الدفع عند الاستلام، وقلل حقول الاستمارة.')

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

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

        if net < 0:
            status = 'خاسر'
        elif not orders:
            status = 'بدون أوردرات'
        elif delivered == 0:
            status = 'لسه بيبدأ'
        else:
            status = 'رابح'

        issues.sort(key=lambda i: i['severity'], reverse=True)
        primary = issues[0] if issues else None

        if primary:
            advice = primary['action']
        elif status == 'رابح':
            advice = 'الأرقام صحية — كبّر الميزانية بالتدريج مع مراقبة التسليم.'
        elif status == 'لسه بيبدأ':
            advice = 'استنى أول دفعة تسليم قبل أي حكم.'
        else:
            advice = 'راجع الأرقام يدوياً — مفيش بيانات كافية للتشخيص.'

        return {
            'name': p['name'],
            'status': status,
            'net': net,
            'orders': orders,
            'delivered': delivered,
            'deliv_rate': deliv_rate,
            'spend': spend,
            'wallet': wallet,
            'cvr': f.get('cvr', 0),
            'cpa_pixel': f.get('cpa_pixel', 0),
            'lp_views': lp,
            'primary': primary,
            'issues': issues[:3],
            'advice': advice,
        }

    def handle_ads_map(self, payload):
        key = payload.get("key")
        product = payload.get("product")
        if not key:
            self.send_json({"success": False, "error": "حملة غير محددة"}, status=400)
            return
        if product and product not in ads_sync.PRODUCTS:
            self.send_json({"success": False, "error": "منتج غير معروف"}, status=400)
            return
        cfg = load_config()
        mapping = cfg.setdefault("campaign_map", {})
        if product:
            mapping[key] = product
        else:
            mapping.pop(key, None)
        save_config(cfg)
        ads_sync.rebuild(cfg)
        campaigns = ads_sync.load_campaigns()
        if key in campaigns:
            campaigns[key]["product"] = ads_sync.campaign_product(key, campaigns[key]["name"], cfg)
            ads_sync._write_json(ads_sync.CAMPAIGNS_FILE, campaigns)
        self.send_json({"success": True, "message": "تم تحديث ربط الحملة", "status": self.build_status('الكل')})

    def handle_ads_line(self, payload):
        key = payload.get("key")
        line = payload.get("line") or "kzamiza"
        if line not in ("kzamiza", "vendor", "service"):
            self.send_json({"success": False, "error": "خط شغل غير معروف"}, status=400)
            return
        cfg = load_config()
        cfg.setdefault("line_map", {})[key] = line
        save_config(cfg)
        ads_sync.rebuild(cfg)
        self.send_json({
            "success": True,
            "message": "تم تحديث خط شغل الحملة",
            "status": self.build_status('الكل'),
        })

    def handle_meta_auto(self, payload):
        cfg = load_config()
        if "enabled" in payload:
            cfg["meta_auto"] = bool(payload["enabled"])
        if "minutes" in payload:
            try:
                cfg["meta_auto_minutes"] = max(5, int(payload["minutes"]))
            except (TypeError, ValueError):
                pass
        save_config(cfg)
        self.send_json({
            "success": True,
            "message": "تم تحديث المزامنة التلقائية",
            "status": self.build_status('الكل'),
        })

    def handle_meta_account(self, payload):
        action = payload.get("action")
        cfg = load_config()
        accounts = cfg.get("meta_accounts") or []

        if action == "add":
            account_id = (payload.get("ad_account_id") or "").strip()
            token = (payload.get("access_token") or "").strip()
            if not account_id or not token:
                self.send_json({"success": False, "error": "أدخل Ad Account ID والتوكن"}, status=400)
                return
            if not account_id.startswith("act_"):
                account_id = "act_" + account_id
            accounts = [a for a in accounts if a.get("ad_account_id") != account_id]
            accounts.append({
                "ad_account_id": account_id,
                "access_token": token,
                "api_version": (payload.get("api_version") or "v21.0").strip(),
                "label": (payload.get("label") or "").strip(),
            })
        elif action == "remove":
            account_id = (payload.get("ad_account_id") or "").strip()
            accounts = [a for a in accounts if a.get("ad_account_id") != account_id]
            state = ads_sync.load_state()
            (state.get("meta_accounts") or {}).pop(account_id, None)
            ads_sync.save_state(state)
        else:
            self.send_json({"success": False, "error": "إجراء غير معروف"}, status=400)
            return

        cfg["meta_accounts"] = accounts
        save_config(cfg)
        self.send_json({
            "success": True,
            "message": "تم تحديث حسابات ميتا",
            "status": self.build_status('الكل'),
        })

    def record_easy_import(self, result, filename):
        try:
            path = orders_import.STATE_FILE
            state = {}
            if os.path.exists(path):
                with open(path, "r", encoding="utf-8") as f:
                    state = json.load(f)
            state["easy"] = {
                "last_import": {
                    "at": datetime.now().strftime("%Y-%m-%d %H:%M"),
                    "file": filename,
                    "rows": result.get("rows"),
                    "added": result.get("added", 0),
                    "updated": 0,
                }
            }
            with open(path, "w", encoding="utf-8") as f:
                json.dump(state, f, ensure_ascii=False, indent=2)
        except Exception:
            pass

    def handle_easy_sync(self, payload):
        scan = payload.get('scan') or 60
        try:
            scan = max(5, min(200, int(scan)))
            result = easy_orders.sync_missing(scan)
            self.send_json({'success': True, 'result': result})
        except Exception as exc:  # noqa: BLE001
            self.send_json({'success': False, 'error': str(exc)}, status=500)

    def handle_easy_secret(self, payload):
        cfg = load_config()
        cfg["easy_webhook_secret"] = (payload.get("secret") or "").strip()
        save_config(cfg)
        self.send_json({"success": True, "message": "تم حفظ الـ secret"})

    def easy_webhook_status(self):
        cfg = load_config()
        path = orders_import.STATE_FILE
        state = {}
        if os.path.exists(path):
            try:
                with open(path, "r", encoding="utf-8") as f:
                    state = json.load(f)
            except Exception:
                state = {}
        info = state.get("easy_webhook") or {}
        return {
            "secret_set": bool((cfg.get("easy_webhook_secret") or "").strip()),
            "last_at": info.get("last_at"),
            "last_order": info.get("last_order"),
            "count": info.get("count") or 0,
            "error": info.get("last_error"),
            "last_reject_at": info.get("last_reject_at"),
            "rejected_count": info.get("rejected_count") or 0,
        }

    def handle_easy_webhook(self, payload):
        cfg = load_config()
        secret = (cfg.get("easy_webhook_secret") or "").strip()
        incoming = (self.headers.get("secret") or "").strip()
        if secret and incoming != secret:
            self.record_webhook_state(False, None, rejected=True)
            self.send_json({"ok": False, "error": "secret غير صحيح"}, status=401)
            return

        event = payload.get("event_type")
        order_id = payload.get("order_id") if event else payload.get("id")
        if not order_id:
            self.send_json({"ok": False, "error": "مفيش order id"}, status=400)
            return

        # نرد فوراً وبعدين نعالج في الخلفية (عشان إيزي أوردر ما يعتبرهاش فشل)
        threading.Thread(target=process_webhook_order, args=(str(order_id),), daemon=True).start()
        self.send_json({"ok": True})

    def record_webhook_state(self, ok, order_id, rejected=False):
        _webhook_state(ok, order_id, rejected)

    def fetch_easy_order(self, order_uuid):
        cfg = load_config()
        key = cfg.get("easyorders_api_key") or ""
        if not key:
            return None
        url = f"https://api.easy-orders.net/api/v1/external-apps/orders/{order_uuid}"
        req = urllib.request.Request(url, headers={"Api-Key": key})
        try:
            with urllib.request.urlopen(req, context=ctx, timeout=20) as resp:
                return json.loads(resp.read().decode("utf-8", "replace"))
        except Exception:
            return None

    def handle_vendor_map(self, payload):
        key = (payload.get("key") or "").strip()
        product = (payload.get("product") or "").strip()
        if not key:
            self.send_json({"success": False, "error": "مجموعة غير محددة"}, status=400)
            return
        vendor_stats.set_mapping(key, product or None)
        self.send_json({
            "success": True,
            "message": "تم تحديث ربط المجموعة",
            "adgroups": vendor_stats.adgroups('الكل'),
        })

    def handle_ads_sources(self, payload):
        sources = payload.get("sources")
        if not isinstance(sources, list):
            self.send_json({"success": False, "error": "قائمة مصادر غير صحيحة"}, status=400)
            return
        valid = [s for s in sources if s in ('meta', 'google', 'manual')]
        if not valid:
            self.send_json({"success": False, "error": "لازم مصدر واحد على الأقل"}, status=400)
            return
        cfg = load_config()
        cfg["spend_sources_enabled"] = valid
        save_config(cfg)
        ads_sync.rebuild(cfg)
        self.send_json({"success": True, "message": "تم تحديث المصادر", "status": self.build_status('الكل')})

    def handle_google_sheet(self, payload):
        url = (payload.get("google_sheet_url") or "").strip()
        if not url:
            self.send_json({"success": False, "error": "يرجى إدخال رابط شيت جوجل أولاً"}, status=400)
            return
        cfg = load_config()
        cfg["google_sheet_url"] = url
        if payload.get("auto") is not None:
            cfg["sheet_auto"] = bool(payload["auto"])

        platform = cfg.get("sheet_platform") or "google"
        result = ads_sync.sync_sheet(url, cfg, platform)
        save_config(cfg)
        self.send_json({
            "success": True,
            "message": f"تم سحب {result['rows']} صف بإجمالي {result['total']:,.0f} ج.م من الشيت",
            "result": result,
            "status": self.build_status('الكل'),
        })

    # ------------------------------------------------------------------
    # الحالة والإعدادات المقنّعة
    # ------------------------------------------------------------------
    def sanitized_config(self):
        cfg = load_config()
        return {
            "easyorders_api_key": cfg.get("easyorders_api_key", ""),
            "google_sheet_url": cfg.get("google_sheet_url", ""),
            "spend_sources_enabled": ads_sync.enabled_sources(cfg),
        }

    def build_status(self, period):
        cfg = load_config()
        state = ads_sync.load_state()
        agg = ads_sync.load_aggregate()
        sources = ads_sync.enabled_sources(cfg)
        if isinstance(period, dict):
            params = period
        else:
            params = {'month': [period]}
        month = params.get('month', ['الكل'])[0]
        rng = resolve_range(params)

        meta = cfg.get("meta") or {}
        google = cfg.get("google_ads") or {}
        meta_list = ads_sync.meta_accounts(cfg)
        meta_state = state.get("meta_accounts") or {}

        meta_rows = ads_sync.load_rows().get('meta') or {}
        meta_accounts_out = []
        for acc in meta_list:
            aid = acc['ad_account_id']
            total = 0.0
            for d, items in meta_rows.items():
                if not date_ok(d, rng):
                    continue
                total += sum(float(r.get('spend') or 0) for r in items
                             if (r.get('account') or aid) == aid)
            meta_accounts_out.append({
                "id": aid,
                "label": acc.get('label') or '',
                "spend": round(total, 2),
                "last_sync": meta_state.get(aid),
            })

        per_source = {}
        for platform in ('meta', 'google'):
            total = 0.0
            for d, items in (ads_sync.load_rows().get(platform) or {}).items():
                if not date_ok(d, rng):
                    continue
                total += sum(r['spend'] for r in items)
            per_source[platform] = round(total, 2)
        manual_total = 0.0
        for d, products in ads_sync.load_manual().items():
            if not date_ok(d, rng):
                continue
            manual_total += sum(products.values())
        per_source['manual'] = round(manual_total, 2)

        campaigns = []
        for key, c in ads_sync.load_campaigns().items():
            campaigns.append({
                "key": key,
                "platform": c.get("platform"),
                "name": c.get("name"),
                "product": c.get("product"),
                "line": (cfg.get("line_map") or {}).get(key) or ads_sync.line_from_text(c.get("name")),
                "spend": round(float(c.get("spend") or 0), 2),
                "mapped": key in (cfg.get("campaign_map") or {}),
            })
        campaigns.sort(key=lambda c: c["spend"], reverse=True)

        adgroups = {}
        for platform in ('meta', 'google'):
            for d, items in (ads_sync.load_rows().get(platform) or {}).items():
                for r in items:
                    key = ads_sync.adgroup_key(platform, r)
                    if not key:
                        continue
                    item = adgroups.get(key)
                    if not item:
                        item = adgroups[key] = {
                            "key": key,
                            "platform": platform,
                            "campaign": r["name"],
                            "name": r["adgroup"],
                            "product": ads_sync.row_product(platform, r, cfg),
                            "line": ads_sync.line_for(platform, r, cfg),
                            "spend": 0.0,
                            "mapped": key in (cfg.get("campaign_map") or {}),
                        }
                    item["spend"] = round(item["spend"] + float(r.get("spend") or 0), 2)
        adgroups = sorted(adgroups.values(), key=lambda x: x["spend"], reverse=True)

        line_totals = {}
        for line, dates in (ads_sync.load_lines() or {}).items():
            total = 0.0
            for d, products in dates.items():
                if date_ok(d, rng):
                    total += sum(products.values())
            line_totals[line] = round(total, 2)

        sheet_state = state.get("sheet") or {}
        sheet_platform = sheet_state.get("platform")
        google_api_ready = bool(google.get("refresh_token") and google.get("customer_id")
                                and google.get("developer_token"))
        google_ready = google_api_ready or sheet_platform == 'google'

        missing = []
        if 'meta' in sources and not meta_list:
            missing.append('meta')
        if 'google' in sources and not google_ready:
            missing.append('google')

        return {
            "month": month,
            "sources_enabled": sources,
            "source_totals": per_source,
            "total_spend": round(spend_total(agg, rng), 2),
            "meta": {
                "configured": bool(meta_list),
                "accounts": meta_accounts_out,
                "token_set": bool(meta_list),
                "auto": bool(cfg.get("meta_auto")),
                "auto_minutes": int(cfg.get("meta_auto_minutes") or 15),
                "auto_last": state.get("meta_auto_last"),
                "error": state.get("meta_error"),
                "last_sync": state.get("meta"),
            },
            "google": {
                "configured": google_ready,
                "api_ready": google_api_ready,
                "customer_id": google.get("customer_id", ""),
                "developer_token_set": bool(google.get("developer_token")),
                "oauth_set": bool(google.get("refresh_token")),
                "last_sync": state.get("google"),
            },
            "google_sheet_url": cfg.get("google_sheet_url", ""),
            "sheet": {
                "url": cfg.get("google_sheet_url", ""),
                "auto": bool(cfg.get("sheet_auto")),
                "last_sync": state.get("sheet"),
                "error": state.get("sheet_error"),
            },
            "campaigns": campaigns[:150],
            "adgroups": adgroups[:200],
            "lines": line_totals,
            "missing_credentials": missing,
        }

    def get_ads_status(self, params):
        return self.build_status(params)

    # ------------------------------------------------------------------
    # API البيانات
    # ------------------------------------------------------------------
    def send_json(self, data, status=200):
        body = json.dumps(data, ensure_ascii=False).encode('utf-8')
        self.send_response(status)
        self.send_header('Content-Type', 'application/json; charset=utf-8')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def filter_orders(self, orders, params):
        prod_filter = params.get('product', ['الكل'])[0]
        rng = resolve_range(params)

        filtered = []
        for r in orders:
            d_str = r.get('تاريخ الإنشاء', '')
            prod = classify_product(r.get('المحتوى', ''))

            if not product_matches(prod_filter, prod):
                continue
            if not date_ok(d_str, rng):
                continue

            filtered.append(r)
        return filtered

    def _easy_bucket_counts(self, params):
        """عدد أوردرات إيزي أوردر لايف لكل باقة منتج."""
        start = params.get('start', [None])[0] or None
        end = params.get('end', [None])[0] or None
        month = params.get('month', ['الكل'])[0] or 'الكل'
        by_bucket = {}
        by_status = {}
        total = 0
        value = 0.0
        vendor_names = _vendor_product_names()
        for r in easy_orders.load():
            d = r.get('date', '')
            if start and end:
                try:
                    d0 = datetime.strptime(d, '%d-%m-%Y').date()
                    s0 = datetime.strptime(start, '%Y-%m-%d').date()
                    e0 = datetime.strptime(end, '%Y-%m-%d').date()
                    if not (s0 <= d0 <= e0):
                        continue
                except ValueError:
                    continue
            elif month != 'الكل' and not d.endswith(f'-{month}'):
                continue
            items = [i for i in (r.get('items') or '').split(' | ') if i]
            buckets = set()
            for item in items:
                if _is_vendor_item(item, vendor_names):
                    continue
                buckets.add(classify_product(item))
            if not buckets:
                continue  # أوردر فيندور — مش تابع لكذا ميزا
            total += 1
            status = r.get('status') or 'unknown'
            by_status[status] = by_status.get(status, 0) + 1
            for b in buckets:
                by_bucket[b] = by_bucket.get(b, 0) + 1
            try:
                value += float(r.get('total') or 0)
            except ValueError:
                pass
        return {'total': total, 'by_bucket': by_bucket,
                'by_status': by_status, 'value': round(value, 0)}

    def get_stats(self, params):
        orders = load_orders()
        filtered = self.filter_orders(orders, params)
        agg = load_ad_spend()

        prod_filter = params.get('product', ['الكل'])[0]
        rng = resolve_range(params)

        tot_orders = len(filtered)
        delivered_cnt = 0
        wallet_comm = 0.0
        shipping_cnt = 0
        shipping_comm = 0.0
        returned_cnt = 0
        cancelled_cnt = 0
        pending_cnt = 0

        for r in filtered:
            comm = float(r.get('العمولة') or 0)
            st_ship = r.get('حالة الشحن', '')
            st_req = r.get('حالة الطلب', '')
            w = (r.get('نزلت المحفظة') == 'نعم')

            if st_ship == 'تم تسليمها للعميل' or w:
                delivered_cnt += 1
                wallet_comm += comm
            elif st_ship in ('خرج للشحن', 'جاهز للشحن') or (st_req == 'طلب تم تأكيده' and st_ship in ('خرج للشحن', 'جاهز للشحن')):
                shipping_cnt += 1
                shipping_comm += comm
            elif st_ship == 'مرتجع في المخزن' or st_req == 'مرتجع في المخزن':
                returned_cnt += 1
            elif 'الغاء' in st_req or 'مغلق' in st_req or 'مكرر' in st_req:
                cancelled_cnt += 1
            else:
                pending_cnt += 1

        total_spend_val = spend_total(agg, rng, prod_filter)

        net_profit_actual = wallet_comm - total_spend_val
        net_profit_expected = (wallet_comm + (shipping_comm * 0.70)) - total_spend_val

        cpa = (total_spend_val / tot_orders) if tot_orders else 0
        cpa_delivered = (total_spend_val / delivered_cnt) if delivered_cnt else 0
        deliv_rate = (delivered_cnt / tot_orders * 100) if tot_orders else 0

        live = self._easy_bucket_counts(params)
        live_total = live['by_bucket'].get(prod_filter, 0) if prod_filter and prod_filter != 'الكل' else live['total']
        orders_source = 'kzamiza'
        kzamiza_orders = tot_orders
        if live_total:
            orders_source = 'easy'
            tot_orders = live_total
            cpa = (total_spend_val / tot_orders) if tot_orders else 0
            deliv_rate = (delivered_cnt / tot_orders * 100) if tot_orders else 0

        cfg = load_config()
        return {
            "total_orders": tot_orders,
            "orders_source": orders_source,
            "kzamiza_orders": kzamiza_orders,
            "live_value": live['value'],
            "live_statuses": live['by_status'],
            "delivered": delivered_cnt,
            "delivered_rate": round(deliv_rate, 1),
            "wallet_comm": round(wallet_comm, 0),
            "shipping": shipping_cnt,
            "shipping_comm": round(shipping_comm, 0),
            "returned": returned_cnt,
            "cancelled": cancelled_cnt,
            "pending": pending_cnt,
            "total_spend": round(total_spend_val, 0),
            "net_profit_actual": round(net_profit_actual, 0),
            "net_profit_expected": round(net_profit_expected, 0),
            "cpa": round(cpa, 1),
            "cpa_delivered": round(cpa_delivered, 1),
            "spend_sources": ads_sync.enabled_sources(cfg),
        }

    def get_platform_profit(self, params):
        month = params.get('month', ['11-2026'])[0]
        start = params.get('start', [None])[0]
        end = params.get('end', [None])[0]
        cfg = load_config()

        matches = attribution.match('kzamiza', month, start, end)
        match_map = matches['matches']
        match_stats = matches['stats']

        spend_map = ads_sync.product_platform_spend(cfg, month, start, end)
        orders = self.filter_orders(load_orders(), params)

        products = {}
        for p in ads_sync.PRODUCTS:
            products[p] = {
                'name': p,
                'orders': 0,
                'wallet': 0.0,
                'meta_rev': 0.0,
                'google_rev': 0.0,
                'direct_rev': 0.0,
                'other_rev': 0.0,
                'meta_spend': 0.0,
                'google_spend': 0.0,
            }

        matched_orders = 0
        for r in orders:
            p = classify_product(r.get('المحتوى', ''))
            item = products.setdefault(p, {
                'name': p, 'orders': 0, 'wallet': 0.0,
                'meta_rev': 0.0, 'google_rev': 0.0, 'direct_rev': 0.0, 'other_rev': 0.0,
                'meta_spend': 0.0, 'google_spend': 0.0,
            })
            item['orders'] += 1
            rec = match_map.get(r.get('رقم الطلب')) or {}
            if rec.get('easy_id'):
                matched_orders += 1
            source = rec.get('source') or 'unknown'
            paid = (r.get('نزلت المحفظة') == 'نعم' or r.get('حالة الشحن') == 'تم تسليمها للعميل')
            if not paid:
                continue
            try:
                comm = float(r.get('العمولة') or 0)
            except ValueError:
                comm = 0.0
            item['wallet'] += comm
            if source in ('fb', 'ig', 'th', 'facebook'):
                item['meta_rev'] += comm
            elif source == 'google':
                item['google_rev'] += comm
            elif source == 'direct':
                item['direct_rev'] += comm
            else:
                item['other_rev'] += comm

        rows = []
        totals = {'meta_rev': 0.0, 'google_rev': 0.0, 'direct_rev': 0.0, 'other_rev': 0.0,
                  'meta_spend': 0.0, 'google_spend': 0.0}
        for p, item in products.items():
            split = spend_map.get(p) or {}
            item['meta_spend'] = round(float(split.get('meta', 0.0)), 2)
            item['google_spend'] = round(float(split.get('google', 0.0)), 2)
            for key in ('meta_rev', 'google_rev', 'direct_rev', 'other_rev'):
                item[key] = round(item[key], 0)
                totals[key] += item[key]
            totals['meta_spend'] += item['meta_spend']
            totals['google_spend'] += item['google_spend']
            item['meta_net'] = round(item['meta_rev'] - item['meta_spend'], 0)
            item['google_net'] = round(item['google_rev'] - item['google_spend'], 0)
            item['total_net'] = round(item['wallet'] - item['meta_spend'] - item['google_spend'], 0)
            if item['orders'] or item['meta_spend'] or item['google_spend']:
                rows.append(item)

        rows.sort(key=lambda x: x['total_net'])
        totals['meta_net'] = round(totals['meta_rev'] - totals['meta_spend'], 0)
        totals['google_net'] = round(totals['google_rev'] - totals['google_spend'], 0)
        totals['net'] = round(totals['meta_rev'] + totals['google_rev'] + totals['direct_rev']
                               + totals['other_rev'] - totals['meta_spend'] - totals['google_spend'], 0)

        return {
            'products': rows,
            'totals': totals,
            'coverage': {
                'matched': matched_orders,
                'total': len(orders),
                'pct': round(matched_orders / len(orders) * 100, 1) if orders else 0,
            },
            'sources': match_stats.get('by_source') or {},
        }

    def get_products_platforms(self, params):
        month = params.get('month', ['11-2026'])[0]
        products = self.get_products_summary(params)
        spend_map = ads_sync.product_platform_spend(
            load_config(), month,
            params.get('start', [None])[0],
            params.get('end', [None])[0])

        rows = []
        for p in products:
            split = spend_map.get(p['name']) or {}
            meta_spend = round(float(split.get('meta', 0.0)), 2)
            google_spend = round(float(split.get('google', 0.0)), 2)
            total = round(meta_spend + google_spend, 2)
            wallet = float(p['wallet'])
            row = {
                'name': p['name'],
                'orders': p['orders'],
                'wallet': wallet,
                'meta': meta_spend,
                'google': google_spend,
                'total_spend': total,
                'net': round(wallet - total, 0),
            }
            # تقدير توزيع الربح بنسبة المصروف (لأن الأوردرات مش متتبعة بالمصدر)
            if total > 0:
                row['meta_share'] = round(meta_spend / total * 100, 1)
                row['google_share'] = round(google_spend / total * 100, 1)
                row['meta_net_est'] = round(wallet * (meta_spend / total) - meta_spend, 0)
                row['google_net_est'] = round(wallet * (google_spend / total) - google_spend, 0)
            else:
                row['meta_share'] = 0
                row['google_share'] = 0
                row['meta_net_est'] = 0
                row['google_net_est'] = 0
            rows.append(row)
        rows.sort(key=lambda r: r['total_spend'], reverse=True)
        return rows

    def get_products_summary(self, params):
        orders = load_orders()
        rng = resolve_range(params)
        agg = load_ad_spend()
        spend_map = spend_by_product(agg, rng)

        prods = ['نيو بوما', 'بوما Puma', 'كوتشي إم', 'كوتشي ميجا', 'أخرى']
        summary = {p: {
            'name': p,
            'orders': 0,
            'delivered': 0,
            'wallet': 0.0,
            'shipping': 0,
            'ship_comm': 0.0,
            'returned': 0,
            'cancelled': 0,
            'ad_spend': 0.0
        } for p in prods}

        for r in orders:
            d_str = r.get('تاريخ الإنشاء', '')
            if not date_ok(d_str, rng):
                continue
            p = classify_product(r.get('المحتوى', ''))
            comm = float(r.get('العمولة') or 0)
            st_ship = r.get('حالة الشحن', '')
            st_req = r.get('حالة الطلب', '')
            w = (r.get('نزلت المحفظة') == 'نعم')

            if p in summary:
                summary[p]['orders'] += 1
                if st_ship == 'تم تسليمها للعميل' or w:
                    summary[p]['delivered'] += 1
                    summary[p]['wallet'] += comm
                elif st_ship in ('خرج للشحن', 'جاهز للشحن') or (st_req == 'طلب تم تأكيده' and st_ship in ('خرج للشحن', 'جاهز للشحن')):
                    summary[p]['shipping'] += 1
                    summary[p]['ship_comm'] += comm
                elif st_ship == 'مرتجع في المخزن' or st_req == 'مرتجع في المخزن':
                    summary[p]['returned'] += 1
                elif 'الغاء' in st_req or 'مغلق' in st_req or 'مكرر' in st_req:
                    summary[p]['cancelled'] += 1

        for p in prods:
            summary[p]['ad_spend'] = spend_map.get(p, 0.0)

        live = self._easy_bucket_counts(params)
        if live['total']:
            for bucket, count in live['by_bucket'].items():
                if bucket in summary:
                    summary[bucket]['orders'] = count
                    summary[bucket]['orders_source'] = 'easy'

        res = []
        for p, d in summary.items():
            deliv_rate = (d['delivered'] / d['orders'] * 100) if d['orders'] else 0
            net_actual = d['wallet'] - d['ad_spend']
            net_exp = (d['wallet'] + (d['ship_comm'] * 0.70)) - d['ad_spend']
            cpa = (d['ad_spend'] / d['orders']) if d['orders'] else 0
            res.append({
                "name": p,
                "orders": d['orders'],
                "orders_source": d.get('orders_source'),
                "delivered": d['delivered'],
                "deliv_rate": round(deliv_rate, 1),
                "wallet": round(d['wallet'], 0),
                "shipping": d['shipping'],
                "ship_comm": round(d['ship_comm'], 0),
                "returned": d['returned'],
                "ad_spend": round(d['ad_spend'], 0),
                "net_actual": round(net_actual, 0),
                "net_expected": round(net_exp, 0),
                "cpa": round(cpa, 1)
            })
        return res

    def get_chart_data(self, params):
        orders = load_orders()
        filtered = self.filter_orders(orders, params)
        agg = load_ad_spend()
        rng = resolve_range(params)
        prod_filter = params.get('product', ['الكل'])[0]

        daily = collections.defaultdict(lambda: {'orders': 0, 'delivered': 0, 'wallet': 0.0, 'shipping': 0.0, 'spend': 0.0})
        for r in filtered:
            d = r.get('تاريخ الإنشاء', '')
            comm = float(r.get('العمولة') or 0)
            st_ship = r.get('حالة الشحن', '')
            w = (r.get('نزلت المحفظة') == 'نعم')

            daily[d]['orders'] += 1
            if st_ship == 'تم تسليمها للعميل' or w:
                daily[d]['delivered'] += 1
                daily[d]['wallet'] += comm
            elif st_ship in ('خرج للشحن', 'جاهز للشحن'):
                daily[d]['shipping'] += comm

        for d, products in agg.items():
            if not date_ok(d, rng):
                continue
            daily[d]['spend'] += sum(a for p, a in products.items() if product_matches(prod_filter, p))

        sorted_dates = sorted(daily.keys(), key=lambda x: _date_sort_key(x))
        return {
            "dates": sorted_dates,
            "orders": [daily[d]['orders'] for d in sorted_dates],
            "delivered": [daily[d]['delivered'] for d in sorted_dates],
            "wallet": [round(daily[d]['wallet'], 0) for d in sorted_dates],
            "spend": [round(daily[d]['spend'], 0) for d in sorted_dates],
        }

    def get_orders_list(self, params):
        orders = load_orders()
        filtered = self.filter_orders(orders, params)
        q = params.get('q', [''])[0].strip()
        status_filter = params.get('status', ['الكل'])[0]

        results = []
        for r in filtered:
            if q:
                combined = f"{r.get('رقم الطلب', '')} {r.get('اسم العميل', '')} {r.get('رقم العميل', '')} {r.get('المحافظة', '')}".lower()
                if q.lower() not in combined:
                    continue

            st_ship = r.get('حالة الشحن', '')
            st_req = r.get('حالة الطلب', '')

            if status_filter != 'الكل':
                if status_filter == 'مُسلّم' and st_ship != 'تم تسليمها للعميل':
                    continue
                elif status_filter == 'شحن' and st_ship not in ('خرج للشحن', 'جاهز للشحن'):
                    continue
                elif status_filter == 'مرتجع' and 'مرتجع' not in st_ship:
                    continue

            results.append({
                "id": r.get('رقم الطلب'),
                "date": r.get('تاريخ الإنشاء'),
                "name": r.get('اسم العميل'),
                "phone": r.get('رقم العميل'),
                "gov": r.get('المحافظة'),
                "status_req": st_req,
                "status_ship": st_ship,
                "items": r.get('المحتوى'),
                "total": r.get('الإجمالي'),
                "comm": r.get('العمولة'),
                "in_wallet": r.get('نزلت المحفظة')
            })

        page = int(params.get('page', [1])[0])
        limit = int(params.get('limit', [25])[0])
        start = (page - 1) * limit
        end = start + limit

        return {
            "total": len(results),
            "page": page,
            "orders": results[start:end]
        }


def bootstrap():
    cfg = load_config()
    ads_sync.ensure_defaults(cfg)
    # ترحيل الحساب الواحد القديم إلى قائمة حسابات
    if not cfg.get("meta_accounts"):
        legacy = cfg.get("meta") or {}
        if legacy.get("access_token") and legacy.get("ad_account_id"):
            account_id = legacy["ad_account_id"]
            if not account_id.startswith("act_"):
                account_id = "act_" + account_id
            cfg["meta_accounts"] = [{
                "ad_account_id": account_id,
                "access_token": legacy["access_token"],
                "api_version": legacy.get("api_version") or "v21.0",
                "label": "",
            }]
    save_config(cfg)
    if ads_sync.migrate_legacy_spend():
        print("تم ترحيل ad_spend.json القديم إلى manual_spend.json")
    ads_sync.rebuild(cfg)


def sheet_auto_loop():
    """كل 5 دقايق: لو التحديث التلقائي مفعّل، اسحب الشيت من جديد."""
    while True:
        time.sleep(300)
        try:
            cfg = load_config()
            if cfg.get("sheet_auto") and cfg.get("google_sheet_url"):
                ads_sync.sync_sheet(
                    cfg["google_sheet_url"], cfg, cfg.get("sheet_platform") or "google")
        except Exception as e:
            try:
                state = ads_sync.load_state()
                state["sheet_error"] = str(e)
                ads_sync.save_state(state)
            except Exception:
                pass


def _month_key(offset=0):
    today = datetime.now()
    month = today.month + offset
    year = today.year
    while month <= 0:
        month += 12
        year -= 1
    while month > 12:
        month -= 12
        year += 1
    return f"{month:02d}-{year}"


def meta_auto_loop():
    """كل 15 دقيقة: مزامنة كل حسابات ميتا للشهر الحالي والسابق."""
    time.sleep(20)
    while True:
        cfg = load_config()
        minutes = int(cfg.get("meta_auto_minutes") or 15)
        try:
            if cfg.get("meta_auto") and ads_sync.meta_accounts(cfg):
                errors = []
                for month in (_month_key(0), _month_key(-1)):
                    try:
                        ads_sync.sync("meta", cfg, month, None)
                    except Exception as e:
                        errors.append(str(e))
                state = ads_sync.load_state()
                state["meta_auto_last"] = datetime.now().strftime("%Y-%m-%d %H:%M")
                state["meta_error"] = "; ".join(errors)[:300] if errors else None
                ads_sync.save_state(state)
        except Exception:
            pass
        time.sleep(max(5, minutes) * 60)


def easy_gap_loop():
    """كل 10 دقايق: يسحب أي أوردرات ناقصة من إيزي أوردر بالـ ID."""
    time.sleep(90)
    while True:
        try:
            cfg = load_config()
            if cfg.get("easy_auto_gap"):
                easy_orders.sync_missing(60)
        except Exception:
            pass
        time.sleep(600)


if __name__ == "__main__":
    bootstrap()
    threading.Thread(target=sheet_auto_loop, daemon=True).start()
    threading.Thread(target=meta_auto_loop, daemon=True).start()
    threading.Thread(target=easy_gap_loop, daemon=True).start()

    class ThreadingServer(socketserver.ThreadingTCPServer):
        allow_reuse_address = True
        daemon_threads = True

    server = None
    bound_port = None
    for candidate in PORT_CANDIDATES:
        try:
            server = ThreadingServer((HOST, candidate), DashboardHandler)
            bound_port = candidate
            break
        except OSError:
            continue

    if server is None:
        print("تعذر تشغيل السيرفر على أي بورت من 5000 إلى 5010")
        raise SystemExit(1)

    url = f"http://localhost:{bound_port}"
    print(f"تشغيل سيرفر لوحة التحكم على: {url}")
    if bound_port != PORT:
        print(f"ملاحظة: البورت {PORT} محجوز على الجهاز، شغّلنا على {bound_port}.")
    if not os.environ.get("DASHBOARD_NO_BROWSER"):
        try:
            import webbrowser
            webbrowser.open(url)
        except Exception:
            pass

    with server:
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            print("\nتم إيقاف السيرفر.")
