Files

1 line
687 KiB
JSON

{"repo": "ahmedabdelkreem404/-------", "files": {"backend/app/Controllers/ApiController.php": {"adcb726c4b6e780c4fedc9b465c2319aa2caf6f2": {"t": 1785745792.8571177, "n": 80065, "h": "2c79832f4a7cd925", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function startTrialSilently(int $userId): void {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$userId,$start,$end,now(),now()]);\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function appVersionCheck(): void {\n $in = $_SERVER['REQUEST_METHOD'] === 'POST' ? json_input() : $_GET;\n $platform = in_array(($in['platform'] ?? 'android'), ['android','ios'], true) ? $in['platform'] : 'android';\n $current = (string)($in['version'] ?? '1.0.0');\n $row = self::one('SELECT * FROM app_version_control WHERE platform=? LIMIT 1', [$platform]) ?: ['latest_version'=>'1.0.0','minimum_supported_version'=>'1.0.0','update_required'=>0,'update_title'=>'تحديث متاح','update_message'=>'يوجد إصدار أحدث من التطبيق.','play_store_url'=>'','direct_apk_url'=>null];\n $forced = version_compare($current, (string)$row['minimum_supported_version'], '<') || (bool)$row['update_required'];\n Response::json(true,'حالة الإصدار',['current_version'=>$current,'platform'=>$platform,'latest_version'=>$row['latest_version'],'minimum_supported_version'=>$row['minimum_supported_version'],'update_required'=>$forced,'optional_update'=>!$forced && version_compare($current,(string)$row['latest_version'],'<'),'update_title'=>$row['update_title'],'update_message'=>$row['update_message'],'play_store_url'=>$row['play_store_url'],'direct_apk_url'=>$row['direct_apk_url']]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n self::updateExtendedHealthProfile((int)$u['id'], $in);\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function healthDashboard(): void {\n $u=Auth::user(); $id=(int)$u['id'];\n $today = date('Y-m-d');\n $todayMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? AND measurement_date=? ORDER BY id DESC LIMIT 1',[$id,$today]);\n $latestMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 1',[$id]);\n $urgentReadings = [];\n if ($latestMeasurement) {\n if ((float)($latestMeasurement['systolic'] ?? 0) >= 180) $urgentReadings[] = 'systolic_bp';\n if ((float)($latestMeasurement['glucose'] ?? 0) >= 300) $urgentReadings[] = 'glucose_high';\n if ((float)($latestMeasurement['glucose'] ?? 999) > 0 && (float)($latestMeasurement['glucose'] ?? 999) <= 70) $urgentReadings[] = 'glucose_low';\n }\n Response::json(true,'لوحة الصحة',[\n 'mode'=>'general_health_companion',\n 'user'=>self::cleanUser($u),\n 'summary'=>[\n 'today'=>$today,\n 'measurements_today'=>(bool)$todayMeasurement,\n 'active_medications'=>(int)self::scalar('SELECT COUNT(*) FROM user_medications WHERE user_id=? AND currently_using=1',[$id]),\n 'symptoms_today'=>(int)self::safeScalar('SELECT COUNT(*) FROM symptom_logs WHERE user_id=? AND DATE(logged_at)=?',[$id,$today]),\n 'urgent_readings'=>$urgentReadings,\n ],\n 'next_medication'=>self::one('SELECT * FROM user_medications WHERE user_id=? AND currently_using=1 ORDER BY id DESC LIMIT 1',[$id]),\n 'today_measurement'=>$todayMeasurement,\n 'latest_measurement'=>$latestMeasurement,\n 'active_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? AND status=\"active\" ORDER BY id DESC',[$id]),\n 'active_program_keys'=>array_column(self::all('SELECT program_key FROM user_health_programs WHERE user_id=? AND status=\"active\"',[$id]), 'program_key'),\n 'program_cards'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'safety_note'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.',\n ]);\n }\n public static function healthPrograms(): void {\n $u=Auth::user();\n Response::json(true,'البرامج الصحية',['programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']])]);\n }\n public static function userHealthPrograms(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'POST') {\n $in=json_input();\n $programKey=(string)($in['program_key'] ?? '');\n if ($programKey === '') Response::json(false,'مفتاح البرنامج مطلوب',[],400);\n $program=self::one('SELECT * FROM health_programs WHERE program_key=? AND is_active=1 LIMIT 1',[$programKey]);\n if (!$program) Response::json(false,'البرنامج غير موجود',[],404);\n $existing=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=? AND status=\"active\" LIMIT 1',[$u['id'],$programKey]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,?,?, ?,1,?,?,?)')\n ->execute([$u['id'],$programKey,'active',now(),'undecided',now(),now()]);\n }\n }\n Response::json(true,'برامج المستخدم الصحية',[\n 'programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function altayyibatProgram(string $action): void {\n $u=Auth::user(); $pdo=self::pdo(); $now=now();\n if($action==='start'){\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1',[$u['id']]);\n if(!$p){$pdo->prepare(\"INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,'altayyibat_14_day','active',?,1,'undecided',?,?)\")->execute([$u['id'],$now,$now,$now]); if(!self::currentTrial((int)$u['id'])) self::startTrialSilently((int)$u['id']);}\n Response::json(true,'تم تفعيل برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']])]);\n }\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$p) Response::json(false,'لا يوجد برنامج طيبات لهذا المستخدم',[],404);\n if($action==='stop') $pdo->prepare(\"UPDATE user_health_programs SET status='stopped',stopped_at=?,decision='stop',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n if($action==='continue') $pdo->prepare(\"UPDATE user_health_programs SET status='completed',completed_at=COALESCE(completed_at,?),decision='continue',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n Response::json(true,'تم تحديث حالة برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE id=?',[$p['id']])]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'] ?? null,$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'القياسات أو الأعراض التي سجلتها قد تحتاج تقييم طبي. إذا كانت الحالة شديدة أو مستمرة، تواصل مع طبيب فورًا.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'رؤى المساعد الذكي',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة الذكاء الاصطناعي',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,schedule_times,start_date,end_date,prescribed_by_doctor,currently_using,reminders_enabled,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, json_encode($schedule, JSON_UNESCAPED_UNICODE),\n $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,schedule_times=?,start_date=?,end_date=?,prescribed_by_doctor=?,currently_using=?,reminders_enabled=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n json_encode($schedule, JSON_UNESCAPED_UNICODE), $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function medicationLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT ml.*, um.medication_name FROM medication_logs ml LEFT JOIN user_medications um ON um.id=ml.medication_id WHERE ml.user_id=? ORDER BY ml.created_at DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأدوية',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $status=(string)($in['status'] ?? 'taken');\n if (!in_array($status, ['taken','skipped','missed'], true)) Response::json(false,'حالة تسجيل الدواء غير صحيحة',[],400);\n $medId=!empty($in['medication_id']) ? (int)$in['medication_id'] : null;\n if ($medId && !self::one('SELECT id FROM user_medications WHERE id=? AND user_id=? LIMIT 1',[$medId,$u['id']])) Response::json(false,'الدواء غير موجود',[],404);\n $pdo->prepare('INSERT INTO medication_logs(user_id,medication_id,scheduled_at,status,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?)')\n ->execute([$u['id'],$medId,$in['scheduled_at'] ?? null,$status,$in['notes'] ?? null,now(),now()]);\n Response::json(true,'تم حفظ سجل الدواء',['log'=>self::one('SELECT * FROM medication_logs WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function notificationSettings(): void {\n $u=Auth::user(); $pdo=self::pdo();\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_notification_settings(user_id,medication_reminders_enabled,measurement_reminder_enabled,measurement_reminder_time,checkin_reminder_enabled,checkin_reminder_time,altayyibat_reminder_enabled,altayyibat_reminder_time,created_at,updated_at) VALUES(?,1,1,?,1,?,1,?,?,?)')\n ->execute([$u['id'],'21:00:00','21:30:00','20:30:00',now(),now()]);\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n }\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n Response::json(true,'إعدادات التنبيهات',['settings'=>$existing]);\n }\n $in=json_input();\n $measurementTime=self::normalizeTime((string)($in['measurement_reminder_time'] ?? $existing['measurement_reminder_time']));\n $checkinTime=self::normalizeTime((string)($in['checkin_reminder_time'] ?? $existing['checkin_reminder_time']));\n $altayyibatTime=self::normalizeTime((string)($in['altayyibat_reminder_time'] ?? $existing['altayyibat_reminder_time']));\n $pdo->prepare('UPDATE user_notification_settings SET medication_reminders_enabled=?, measurement_reminder_enabled=?, measurement_reminder_time=?, checkin_reminder_enabled=?, checkin_reminder_time=?, altayyibat_reminder_enabled=?, altayyibat_reminder_time=?, updated_at=? WHERE user_id=?')\n ->execute([\n bool_int($in['medication_reminders_enabled'] ?? $existing['medication_reminders_enabled']),\n bool_int($in['measurement_reminder_enabled'] ?? $existing['measurement_reminder_enabled']),\n $measurementTime,\n bool_int($in['checkin_reminder_enabled'] ?? $existing['checkin_reminder_enabled']),\n $checkinTime,\n bool_int($in['altayyibat_reminder_enabled'] ?? $existing['altayyibat_reminder_enabled']),\n $altayyibatTime,\n now(),\n $u['id'],\n ]);\n Response::json(true,'تم حفظ إعدادات التنبيهات',['settings'=>self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']])]);\n }\n public static function measurementTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"measurement\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'blood_pressure','label_ar'=>'ضغط الدم','unit'=>'mmHg'],\n ['field_key'=>'glucose','label_ar'=>'سكر الدم','unit'=>'mg/dL'],\n ['field_key'=>'weight','label_ar'=>'الوزن','unit'=>'kg'],\n ['field_key'=>'pulse','label_ar'=>'النبض','unit'=>'bpm'],\n ];\n }\n Response::json(true,'أنواع القياسات',['types'=>$rows]);\n }\n public static function symptomTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"symptom\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'chest_pain','label_ar'=>'ألم صدر'],\n ['field_key'=>'shortness_of_breath','label_ar'=>'ضيق تنفس'],\n ['field_key'=>'dizziness','label_ar'=>'دوخة'],\n ['field_key'=>'fatigue','label_ar'=>'إرهاق'],\n ];\n }\n Response::json(true,'أنواع الأعراض',['types'=>$rows]);\n }\n public static function symptomLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأعراض',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $key=trim((string)($in['symptom_key'] ?? 'general'));\n if ($key === '') $key='general';\n $severity=max(1,min(10,(int)($in['severity'] ?? 1)));\n $loggedAt=(string)($in['logged_at'] ?? now());\n $pdo->prepare('INSERT INTO symptom_logs(user_id,symptom_key,severity,duration_note,notes,logged_at,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$key,$severity,$in['duration_note'] ?? null,$in['notes'] ?? null,$loggedAt,now(),now()]);\n $urgent=in_array($key, ['chest_pain','shortness_of_breath','fainting','severe_bleeding','stroke_symptoms'], true) || $severity >= 8;\n Response::json(true,'تم حفظ العرض',[\n 'log'=>self::one('SELECT * FROM symptom_logs WHERE id=?',[(int)$pdo->lastInsertId()]),\n 'urgent'=>$urgent,\n 'urgent_message'=>$urgent ? 'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.' : null,\n ],201);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients && $available) {\n $altValidation = AltayebaatFoodRuleService::validateIngredients($available);\n $safeIngredients = array_values(array_filter(array_map(fn($food) => (string)($food['name_ar'] ?? ''), $altValidation['allowed'] ?? [])));\n $forbiddenHits = [];\n $unknownHits = [];\n foreach (($altValidation['blocked'] ?? []) as $blocked) {\n if (($blocked['status'] ?? '') === 'forbidden' && !empty($blocked['food']['name_ar'])) {\n $forbiddenHits[] = (string)$blocked['food']['name_ar'];\n } else {\n $unknownHits[] = (string)($blocked['input'] ?? '');\n }\n }\n $forbiddenHits = array_values(array_unique(array_filter($forbiddenHits)));\n $unknownHits = array_values(array_unique(array_filter($unknownHits)));\n }\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n if (!empty($in['force_rule_based'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح المساعد لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $altayyibatActive = (bool)self::one('SELECT id FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1', [$u['id']]);\n $asksAltayyibat = str_contains((string)$in['message'], 'الطيبات') || str_contains((string)$in['message'], 'نظام الطيبات');\n if ($altayyibatActive || $asksAltayyibat) {\n $altMedReply = AltayebaatAiGuardService::medicationSafety((string)$in['message']);\n if ($altMedReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altMedReply,0,now()]);\n Response::json(true,'رد أمان الطيبات',['reply'=>$altMedReply,'safety_flags'=>['medication_safety']]);\n }\n $altDetected = AltayebaatFoodRuleService::detectInText((string)$in['message']);\n $altReply = AltayebaatFoodRuleService::forbiddenTextReply($altDetected['forbidden'] ?? []);\n if ($altReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altReply,0,now()]);\n Response::json(true,'رد قواعد الطيبات',['reply'=>$altReply,'safety_flags'=>['altayyibat_forbidden_food']]);\n }\n }\n if ($altayyibatActive || $asksAltayyibat) {\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n if ($altayyibatActive || $asksAltayyibat) {\n $blockedReply = AltayebaatFoodRuleService::forbiddenTextReply(AltayebaatFoodRuleService::detectInText($safe)['forbidden'] ?? []);\n if ($blockedReply) $safe = $blockedReply;\n }\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function aiHealthChat(): void {\n $u=Auth::user();\n $in=json_input();\n $message=(string)($in['message'] ?? '');\n if (self::isEmergencyMessage($message)) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.',\n 'safety_flags'=>['emergency_symptoms'],\n ]);\n }\n $medReply = self::generalMedicationSafetyReply($message);\n if ($medReply) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>$medReply,\n 'safety_flags'=>['medication_safety'],\n ]);\n }\n self::aiChat();\n }\n public static function labFiles(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM lab_files WHERE user_id=? ORDER BY COALESCE(test_date, DATE(created_at)) DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'ملفات التحاليل',['files'=>$s->fetchAll()]);\n }\n $in=json_input();\n $category=trim((string)($in['category'] ?? 'general'));\n $pdo->prepare('INSERT INTO lab_files(user_id,category,file_name,file_path,file_type,test_date,notes,values_json,ocr_text,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)')\n ->execute([\n $u['id'],\n $category ?: 'general',\n $in['file_name'] ?? null,\n $in['file_path'] ?? null,\n $in['file_type'] ?? null,\n $in['test_date'] ?? null,\n $in['notes'] ?? null,\n isset($in['values']) ? json_encode($in['values'], JSON_UNESCAPED_UNICODE) : null,\n $in['ocr_text'] ?? null,\n now(),\n now(),\n ]);\n Response::json(true,'تم حفظ بيانات ملف التحليل',['file'=>self::one('SELECT * FROM lab_files WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function explainLab(): void {\n Auth::user();\n Response::json(true,'شرح التحليل',[\n 'reply'=>'أقدر أساعدك في تنظيم بيانات التحليل وشرح المعاني العامة، لكن لا أقدم تشخيصًا ولا أغير علاجًا. شارك التقرير مع طبيبك خاصة لو في نتائج خارج الطبيعي.',\n ]);\n }\n public static function syncPush(): void {\n $u=Auth::user(); $in=json_input(); $records=$in['records'] ?? [];\n if (!is_array($records)) Response::json(false,'بيانات المزامنة يجب أن تكون قائمة',[],400);\n $pdo=self::pdo(); $saved=0;\n foreach($records as $record){\n if (!is_array($record)) continue;\n $entity=(string)($record['entity_type'] ?? $record['type'] ?? 'unknown');\n $payload=$record['payload'] ?? $record;\n $pdo->prepare('INSERT INTO sync_events(user_id,client_id,entity_type,entity_id,operation,payload_json,synced_at,created_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$record['client_id'] ?? null,$entity,$record['entity_id'] ?? null,$record['operation'] ?? 'create',json_encode($payload, JSON_UNESCAPED_UNICODE),now(),now()]);\n $saved++;\n }\n Response::json(true,'تم قبول المزامنة',['saved'=>$saved,'accepted'=>$saved]);\n }\n public static function syncPull(): void {\n $u=Auth::user();\n Response::json(true,'بيانات المزامنة',[\n 'server_time'=>now(),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 100',[$u['id']]),\n 'symptom_logs'=>self::safeAll('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100',[$u['id']]),\n 'health_programs'=>self::safeAll('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function importAltayebaat(): void {\n self::requireLocal();\n $summary = AltayebaatImportService::importAll();\n Response::json(true,'تم استيراد بيانات الطيبات للمراجعة',['summary'=>$summary]);\n }\n public static function paymentOptions(): void {\n Auth::user();\n Response::json(true,'خيارات الدفع',[\n 'manual_payments_enabled'=>self::setting('manual_payments_enabled') !== '0',\n 'google_play_billing_enabled'=>self::setting('google_play_billing_enabled') === '1',\n 'google_play_warning'=>'إذا تم نشر التطبيق على متجر جوجل وكانت المدفوعات تفتح مزايا رقمية، فقد يلزم استخدام نظام دفع المتجر حسب سياساته.',\n 'methods'=>self::safeAll('SELECT id,method_key,title_ar,account_label_ar,account_value,instructions_ar FROM payment_methods WHERE is_active=1 ORDER BY id',[]),\n 'packages'=>self::safeAll('SELECT id,package_key,title_ar,description_ar,price,currency,duration_days FROM subscription_packages WHERE is_active=1 ORDER BY price',[]),\n ]);\n }\n public static function createManualPaymentRequest(): void {\n $u=Auth::user();\n if (self::setting('manual_payments_enabled') === '0') Response::json(false,'الدفع اليدوي غير متاح حالياً',[],403);\n $in=json_input();\n Validator::required($in,['package_id','payment_method_id']);\n $package=self::one('SELECT * FROM subscription_packages WHERE id=? AND is_active=1',[(int)$in['package_id']]);\n $method=self::one('SELECT * FROM payment_methods WHERE id=? AND is_active=1',[(int)$in['payment_method_id']]);\n if (!$package || !$method) Response::json(false,'باقة الدفع أو طريقة الدفع غير متاحة',[],404);\n $prefix=self::setting('payment_reference_prefix') ?: 'TYB';\n $reference=$prefix.'-'.$u['id'].'-'.date('YmdHis').'-'.random_int(100,999);\n self::pdo()->prepare('INSERT INTO manual_payment_requests(user_id,package_id,payment_method_id,reference_code,transaction_reference,screenshot_path,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?, ?,?)')\n ->execute([$u['id'],$package['id'],$method['id'],$reference,$in['transaction_reference'] ?? null,$in['screenshot_path'] ?? null,'pending',now(),now()]);\n $request=self::one('SELECT * FROM manual_payment_requests WHERE reference_code=?',[$reference]);\n Response::json(true,'تم إرسال طلب الدفع للمراجعة',['request'=>$request],201);\n }\n public static function paymentRequests(): void {\n $u=Auth::user();\n $rows=self::safeAll('SELECT r.*, p.title_ar package_title, p.price, p.currency, m.title_ar method_title FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id JOIN payment_methods m ON m.id=r.payment_method_id WHERE r.user_id=? ORDER BY r.id DESC',[$u['id']]);\n Response::json(true,'طلبات الدفع',['requests'=>$rows]);\n }\n public static function adminPaymentReview(string $id, string $action): void {\n self::requireLocal();\n $request=self::one('SELECT r.*, p.package_key, p.duration_days FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id WHERE r.id=?',[(int)$id]);\n if (!$request) Response::json(false,'طلب الدفع غير موجود',[],404);\n $status=$action === 'approve' ? 'approved' : 'rejected';\n self::pdo()->prepare('UPDATE manual_payment_requests SET status=?, reviewed_at=?, updated_at=? WHERE id=?')\n ->execute([$status,now(),now(),$request['id']]);\n if ($status === 'approved') {\n $plan=$request['package_key'] === 'premium_yearly' ? 'premium_yearly' : 'premium_monthly';\n $expires=date('Y-m-d H:i:s', strtotime('+' . (int)$request['duration_days'] . ' days'));\n EntitlementService::apply((int)$request['user_id'],$plan,$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,?, 'manual_payment','active',?,?,?,?,?,?)\")\n ->execute([$request['user_id'],$plan,now(),$expires,'manual',$request['reference_code'],now(),now()]);\n }\n Response::json(true,$status === 'approved' ? 'تم قبول الدفع وتفعيل الاشتراك' : 'تم رفض طلب الدفع');\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n $altProgram = self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1', [$u['id']]);\n $asksAltayyibat = str_contains($message, 'الطيبات') || str_contains($message, 'نظام الطيبات');\n $context = [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n if (($altProgram && ($altProgram['status'] ?? '') === 'active') || $asksAltayyibat) {\n $context['altayyibat_mode'] = [\n 'strict_food_mode' => (bool)($altProgram && ($altProgram['status'] ?? '') === 'active'),\n 'program_status' => $altProgram['status'] ?? 'inactive',\n 'current_day' => $altProgram['current_day'] ?? ($t['current_day'] ?? null),\n ];\n $context['altayyibat_rules'] = self::safeAll('SELECT rule_key,title_ar,description_ar FROM altayyibat_rules WHERE is_active=1 ORDER BY display_order,id', []);\n $context['altayyibat_frequency_guide'] = self::safeAll('SELECT level_key,title_ar,description_ar,frequency_note FROM altayyibat_frequency_levels WHERE is_active=1 ORDER BY display_order,id', []);\n $context['altayyibat_allowed_foods'] = self::safeAll(\"SELECT name_ar,description_ar,usage_notes_ar FROM altayyibat_food_items WHERE type='allowed' AND is_active=1 ORDER BY display_order,id LIMIT 120\", []);\n $context['altayyibat_forbidden_foods'] = self::safeAll(\"SELECT name_ar,reason_ar,usage_notes_ar FROM altayyibat_food_items WHERE type='forbidden' AND is_active=1 ORDER BY display_order,id LIMIT 120\", []);\n $context['altayyibat_relevant_theories'] = self::safeAll('SELECT theory_number,title_ar,summary_ar,key_points_json FROM altayyibat_theories WHERE is_active=1 ORDER BY display_order,id LIMIT 8', []);\n $context['altayyibat_doctor_profile'] = self::one('SELECT title_ar,subtitle_ar,bio_ar,quotes_json FROM altayyibat_doctor_profile WHERE is_active=1 ORDER BY id DESC LIMIT 1', []);\n $context['altayyibat_relevant_articles'] = self::safeAll('SELECT title_ar,category_ar,excerpt_ar FROM altayyibat_articles WHERE is_active=1 ORDER BY id DESC LIMIT 8', []);\n }\n return $context;\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function isEmergencyMessage(string $message): bool {\n foreach (['ألم صدر','الم صدر','ضيق تنفس','إغماء','اغماء','نزيف شديد','زغللة شديدة','هبوط شديد','stroke','chest pain','shortness of breath','fainting'] as $needle) {\n if (mb_stripos($message, $needle) !== false) return true;\n }\n return false;\n }\n private static function updateExtendedHealthProfile(int $userId, array $in): void {\n $allowed = [\n 'birth_date','country','city','phone','preferred_dialect','diabetes_type',\n 'has_thyroid','digestive_issues','allergies','surgeries','family_history',\n 'activity_level','smoking','sleep_pattern','goals_json',\n ];\n $sets = [];\n $values = [];\n foreach ($allowed as $key) {\n if (!array_key_exists($key, $in)) continue;\n $sets[] = \"`$key`=?\";\n $value = $in[$key];\n if ($key === 'goals_json') $value = json_encode($value, JSON_UNESCAPED_UNICODE);\n if ($key === 'has_thyroid') $value = bool_int($value);\n $values[] = $value;\n }\n if (!$sets) return;\n try {\n $values[] = now();\n $values[] = $userId;\n self::pdo()->prepare('UPDATE health_profiles SET ' . implode(',', $sets) . ', updated_at=? WHERE user_id=?')->execute($values);\n } catch (Throwable) {\n }\n }\n private static function generalMedicationSafetyReply(string $message): ?string {\n $hasMedication = preg_match('/دواء|أدوية|ادوية|علاج|جرعة|الضغط|السكر|كوليسترول|medication|medicine|dose/i', $message);\n $hasChangeIntent = preg_match('/أوقف|اوقف|وقف|أقلل|اقلل|قلل|أغير|اغير|غير|تغيير|stop|reduce|change|taper|discontinue/i', $message);\n if ($hasMedication && $hasChangeIntent) {\n return 'لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك داخل التطبيق ومشاركة التقرير مع طبيبك.';\n }\n return null;\n }\n private static function normalizeTime(string $time): string {\n if (preg_match('/^(\\d{1,2}):(\\d{2})/', $time, $m)) {\n $hour = max(0, min(23, (int)$m[1]));\n $minute = max(0, min(59, (int)$m[2]));\n return str_pad((string)$hour, 2, '0', STR_PAD_LEFT) . ':' . str_pad((string)$minute, 2, '0', STR_PAD_LEFT) . ':00';\n }\n return '21:00:00';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function scalar(string $sql,array $p): mixed { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchColumn(); }\n private static function safeScalar(string $sql,array $p): mixed { try { return self::scalar($sql,$p); } catch (Throwable) { return 0; } }\n private static function safeAll(string $sql,array $p): array { try { return self::all($sql,$p); } catch (Throwable) { return []; } }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "1617958838bad77b1b71a4103a7e16cab12c6b7f": {"t": 1785756539.6636422, "n": 76511, "h": "fa7ba29c628355ed", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function startTrialSilently(int $userId): void {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$userId,$start,$end,now(),now()]);\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function appVersionCheck(): void {\n $in = $_SERVER['REQUEST_METHOD'] === 'POST' ? json_input() : $_GET;\n $platform = in_array(($in['platform'] ?? 'android'), ['android','ios'], true) ? $in['platform'] : 'android';\n $current = (string)($in['version'] ?? '1.0.0');\n $row = self::one('SELECT * FROM app_version_control WHERE platform=? LIMIT 1', [$platform]) ?: ['latest_version'=>'1.0.0','minimum_supported_version'=>'1.0.0','update_required'=>0,'update_title'=>'تحديث متاح','update_message'=>'يوجد إصدار أحدث من التطبيق.','play_store_url'=>'','direct_apk_url'=>null];\n $forced = version_compare($current, (string)$row['minimum_supported_version'], '<') || (bool)$row['update_required'];\n Response::json(true,'حالة الإصدار',['current_version'=>$current,'platform'=>$platform,'latest_version'=>$row['latest_version'],'minimum_supported_version'=>$row['minimum_supported_version'],'update_required'=>$forced,'optional_update'=>!$forced && version_compare($current,(string)$row['latest_version'],'<'),'update_title'=>$row['update_title'],'update_message'=>$row['update_message'],'play_store_url'=>$row['play_store_url'],'direct_apk_url'=>$row['direct_apk_url']]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n self::updateExtendedHealthProfile((int)$u['id'], $in);\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function healthDashboard(): void {\n $u=Auth::user(); $id=(int)$u['id'];\n $today = date('Y-m-d');\n $todayMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? AND measurement_date=? ORDER BY id DESC LIMIT 1',[$id,$today]);\n $latestMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 1',[$id]);\n $urgentReadings = [];\n if ($latestMeasurement) {\n if ((float)($latestMeasurement['systolic'] ?? 0) >= 180) $urgentReadings[] = 'systolic_bp';\n if ((float)($latestMeasurement['glucose'] ?? 0) >= 300) $urgentReadings[] = 'glucose_high';\n if ((float)($latestMeasurement['glucose'] ?? 999) > 0 && (float)($latestMeasurement['glucose'] ?? 999) <= 70) $urgentReadings[] = 'glucose_low';\n }\n Response::json(true,'لوحة الصحة',[\n 'mode'=>'general_health_companion',\n 'user'=>self::cleanUser($u),\n 'summary'=>[\n 'today'=>$today,\n 'measurements_today'=>(bool)$todayMeasurement,\n 'active_medications'=>(int)self::scalar('SELECT COUNT(*) FROM user_medications WHERE user_id=? AND currently_using=1',[$id]),\n 'symptoms_today'=>(int)self::safeScalar('SELECT COUNT(*) FROM symptom_logs WHERE user_id=? AND DATE(logged_at)=?',[$id,$today]),\n 'urgent_readings'=>$urgentReadings,\n ],\n 'next_medication'=>self::one('SELECT * FROM user_medications WHERE user_id=? AND currently_using=1 ORDER BY id DESC LIMIT 1',[$id]),\n 'today_measurement'=>$todayMeasurement,\n 'latest_measurement'=>$latestMeasurement,\n 'active_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? AND status=\"active\" ORDER BY id DESC',[$id]),\n 'active_program_keys'=>array_column(self::all('SELECT program_key FROM user_health_programs WHERE user_id=? AND status=\"active\"',[$id]), 'program_key'),\n 'program_cards'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'safety_note'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.',\n ]);\n }\n public static function healthPrograms(): void {\n $u=Auth::user();\n Response::json(true,'البرامج الصحية',['programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']])]);\n }\n public static function userHealthPrograms(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'POST') {\n $in=json_input();\n $programKey=(string)($in['program_key'] ?? '');\n if ($programKey === '') Response::json(false,'مفتاح البرنامج مطلوب',[],400);\n $program=self::one('SELECT * FROM health_programs WHERE program_key=? AND is_active=1 LIMIT 1',[$programKey]);\n if (!$program) Response::json(false,'البرنامج غير موجود',[],404);\n $existing=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=? AND status=\"active\" LIMIT 1',[$u['id'],$programKey]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,?,?, ?,1,?,?,?)')\n ->execute([$u['id'],$programKey,'active',now(),'undecided',now(),now()]);\n }\n }\n Response::json(true,'برامج المستخدم الصحية',[\n 'programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function altayyibatProgram(string $action): void {\n $u=Auth::user(); $pdo=self::pdo(); $now=now();\n if($action==='start'){\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1',[$u['id']]);\n if(!$p){$pdo->prepare(\"INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,'altayyibat_14_day','active',?,1,'undecided',?,?)\")->execute([$u['id'],$now,$now,$now]); if(!self::currentTrial((int)$u['id'])) self::startTrialSilently((int)$u['id']);}\n Response::json(true,'تم تفعيل برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']])]);\n }\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$p) Response::json(false,'لا يوجد برنامج طيبات لهذا المستخدم',[],404);\n if($action==='stop') $pdo->prepare(\"UPDATE user_health_programs SET status='stopped',stopped_at=?,decision='stop',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n if($action==='continue') $pdo->prepare(\"UPDATE user_health_programs SET status='completed',completed_at=COALESCE(completed_at,?),decision='continue',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n Response::json(true,'تم تحديث حالة برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE id=?',[$p['id']])]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'] ?? null,$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'القياسات أو الأعراض التي سجلتها قد تحتاج تقييم طبي. إذا كانت الحالة شديدة أو مستمرة، تواصل مع طبيب فورًا.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'رؤى المساعد الذكي',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة الذكاء الاصطناعي',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,schedule_times,start_date,end_date,prescribed_by_doctor,currently_using,reminders_enabled,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, json_encode($schedule, JSON_UNESCAPED_UNICODE),\n $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,schedule_times=?,start_date=?,end_date=?,prescribed_by_doctor=?,currently_using=?,reminders_enabled=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n json_encode($schedule, JSON_UNESCAPED_UNICODE), $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function medicationLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT ml.*, um.medication_name FROM medication_logs ml LEFT JOIN user_medications um ON um.id=ml.medication_id WHERE ml.user_id=? ORDER BY ml.created_at DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأدوية',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $status=(string)($in['status'] ?? 'taken');\n if (!in_array($status, ['taken','skipped','missed'], true)) Response::json(false,'حالة تسجيل الدواء غير صحيحة',[],400);\n $medId=!empty($in['medication_id']) ? (int)$in['medication_id'] : null;\n if ($medId && !self::one('SELECT id FROM user_medications WHERE id=? AND user_id=? LIMIT 1',[$medId,$u['id']])) Response::json(false,'الدواء غير موجود',[],404);\n $pdo->prepare('INSERT INTO medication_logs(user_id,medication_id,scheduled_at,status,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?)')\n ->execute([$u['id'],$medId,$in['scheduled_at'] ?? null,$status,$in['notes'] ?? null,now(),now()]);\n Response::json(true,'تم حفظ سجل الدواء',['log'=>self::one('SELECT * FROM medication_logs WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function notificationSettings(): void {\n $u=Auth::user(); $pdo=self::pdo();\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_notification_settings(user_id,medication_reminders_enabled,measurement_reminder_enabled,measurement_reminder_time,checkin_reminder_enabled,checkin_reminder_time,altayyibat_reminder_enabled,altayyibat_reminder_time,created_at,updated_at) VALUES(?,1,1,?,1,?,1,?,?,?)')\n ->execute([$u['id'],'21:00:00','21:30:00','20:30:00',now(),now()]);\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n }\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n Response::json(true,'إعدادات التنبيهات',['settings'=>$existing]);\n }\n $in=json_input();\n $measurementTime=self::normalizeTime((string)($in['measurement_reminder_time'] ?? $existing['measurement_reminder_time']));\n $checkinTime=self::normalizeTime((string)($in['checkin_reminder_time'] ?? $existing['checkin_reminder_time']));\n $altayyibatTime=self::normalizeTime((string)($in['altayyibat_reminder_time'] ?? $existing['altayyibat_reminder_time']));\n $pdo->prepare('UPDATE user_notification_settings SET medication_reminders_enabled=?, measurement_reminder_enabled=?, measurement_reminder_time=?, checkin_reminder_enabled=?, checkin_reminder_time=?, altayyibat_reminder_enabled=?, altayyibat_reminder_time=?, updated_at=? WHERE user_id=?')\n ->execute([\n bool_int($in['medication_reminders_enabled'] ?? $existing['medication_reminders_enabled']),\n bool_int($in['measurement_reminder_enabled'] ?? $existing['measurement_reminder_enabled']),\n $measurementTime,\n bool_int($in['checkin_reminder_enabled'] ?? $existing['checkin_reminder_enabled']),\n $checkinTime,\n bool_int($in['altayyibat_reminder_enabled'] ?? $existing['altayyibat_reminder_enabled']),\n $altayyibatTime,\n now(),\n $u['id'],\n ]);\n Response::json(true,'تم حفظ إعدادات التنبيهات',['settings'=>self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']])]);\n }\n public static function measurementTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"measurement\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'blood_pressure','label_ar'=>'ضغط الدم','unit'=>'mmHg'],\n ['field_key'=>'glucose','label_ar'=>'سكر الدم','unit'=>'mg/dL'],\n ['field_key'=>'weight','label_ar'=>'الوزن','unit'=>'kg'],\n ['field_key'=>'pulse','label_ar'=>'النبض','unit'=>'bpm'],\n ];\n }\n Response::json(true,'أنواع القياسات',['types'=>$rows]);\n }\n public static function symptomTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"symptom\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'chest_pain','label_ar'=>'ألم صدر'],\n ['field_key'=>'shortness_of_breath','label_ar'=>'ضيق تنفس'],\n ['field_key'=>'dizziness','label_ar'=>'دوخة'],\n ['field_key'=>'fatigue','label_ar'=>'إرهاق'],\n ];\n }\n Response::json(true,'أنواع الأعراض',['types'=>$rows]);\n }\n public static function symptomLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأعراض',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $key=trim((string)($in['symptom_key'] ?? 'general'));\n if ($key === '') $key='general';\n $severity=max(1,min(10,(int)($in['severity'] ?? 1)));\n $loggedAt=(string)($in['logged_at'] ?? now());\n $pdo->prepare('INSERT INTO symptom_logs(user_id,symptom_key,severity,duration_note,notes,logged_at,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$key,$severity,$in['duration_note'] ?? null,$in['notes'] ?? null,$loggedAt,now(),now()]);\n $urgent=in_array($key, ['chest_pain','shortness_of_breath','fainting','severe_bleeding','stroke_symptoms'], true) || $severity >= 8;\n Response::json(true,'تم حفظ العرض',[\n 'log'=>self::one('SELECT * FROM symptom_logs WHERE id=?',[(int)$pdo->lastInsertId()]),\n 'urgent'=>$urgent,\n 'urgent_message'=>$urgent ? 'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.' : null,\n ],201);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح المساعد لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $altayyibatActive = (bool)self::one('SELECT id FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1', [$u['id']]);\n $asksAltayyibat = str_contains((string)$in['message'], 'الطيبات') || str_contains((string)$in['message'], 'نظام الطيبات');\n if ($altayyibatActive || $asksAltayyibat) {\n $altMedReply = AltayebaatAiGuardService::medicationSafety((string)$in['message']);\n if ($altMedReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altMedReply,0,now()]);\n Response::json(true,'رد أمان الطيبات',['reply'=>$altMedReply,'safety_flags'=>['medication_safety']]);\n }\n $altDetected = AltayebaatFoodRuleService::detectInText((string)$in['message']);\n $altReply = AltayebaatFoodRuleService::forbiddenTextReply($altDetected['forbidden'] ?? []);\n if ($altReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altReply,0,now()]);\n Response::json(true,'رد قواعد الطيبات',['reply'=>$altReply,'safety_flags'=>['altayyibat_forbidden_food']]);\n }\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function aiHealthChat(): void {\n $u=Auth::user();\n $in=json_input();\n $message=(string)($in['message'] ?? '');\n if (self::isEmergencyMessage($message)) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.',\n 'safety_flags'=>['emergency_symptoms'],\n ]);\n }\n $medReply = self::generalMedicationSafetyReply($message);\n if ($medReply) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>$medReply,\n 'safety_flags'=>['medication_safety'],\n ]);\n }\n self::aiChat();\n }\n public static function labFiles(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM lab_files WHERE user_id=? ORDER BY COALESCE(test_date, DATE(created_at)) DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'ملفات التحاليل',['files'=>$s->fetchAll()]);\n }\n $in=json_input();\n $category=trim((string)($in['category'] ?? 'general'));\n $pdo->prepare('INSERT INTO lab_files(user_id,category,file_name,file_path,file_type,test_date,notes,values_json,ocr_text,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)')\n ->execute([\n $u['id'],\n $category ?: 'general',\n $in['file_name'] ?? null,\n $in['file_path'] ?? null,\n $in['file_type'] ?? null,\n $in['test_date'] ?? null,\n $in['notes'] ?? null,\n isset($in['values']) ? json_encode($in['values'], JSON_UNESCAPED_UNICODE) : null,\n $in['ocr_text'] ?? null,\n now(),\n now(),\n ]);\n Response::json(true,'تم حفظ بيانات ملف التحليل',['file'=>self::one('SELECT * FROM lab_files WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function explainLab(): void {\n Auth::user();\n Response::json(true,'شرح التحليل',[\n 'reply'=>'أقدر أساعدك في تنظيم بيانات التحليل وشرح المعاني العامة، لكن لا أقدم تشخيصًا ولا أغير علاجًا. شارك التقرير مع طبيبك خاصة لو في نتائج خارج الطبيعي.',\n ]);\n }\n public static function syncPush(): void {\n $u=Auth::user(); $in=json_input(); $records=$in['records'] ?? [];\n if (!is_array($records)) Response::json(false,'بيانات المزامنة يجب أن تكون قائمة',[],400);\n $pdo=self::pdo(); $saved=0;\n foreach($records as $record){\n if (!is_array($record)) continue;\n $entity=(string)($record['entity_type'] ?? $record['type'] ?? 'unknown');\n $payload=$record['payload'] ?? $record;\n $pdo->prepare('INSERT INTO sync_events(user_id,client_id,entity_type,entity_id,operation,payload_json,synced_at,created_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$record['client_id'] ?? null,$entity,$record['entity_id'] ?? null,$record['operation'] ?? 'create',json_encode($payload, JSON_UNESCAPED_UNICODE),now(),now()]);\n $saved++;\n }\n Response::json(true,'تم قبول المزامنة',['saved'=>$saved,'accepted'=>$saved]);\n }\n public static function syncPull(): void {\n $u=Auth::user();\n Response::json(true,'بيانات المزامنة',[\n 'server_time'=>now(),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 100',[$u['id']]),\n 'symptom_logs'=>self::safeAll('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100',[$u['id']]),\n 'health_programs'=>self::safeAll('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function importAltayebaat(): void {\n self::requireLocal();\n $summary = AltayebaatImportService::importAll();\n Response::json(true,'تم استيراد بيانات الطيبات للمراجعة',['summary'=>$summary]);\n }\n public static function paymentOptions(): void {\n Auth::user();\n Response::json(true,'خيارات الدفع',[\n 'manual_payments_enabled'=>self::setting('manual_payments_enabled') !== '0',\n 'google_play_billing_enabled'=>self::setting('google_play_billing_enabled') === '1',\n 'google_play_warning'=>'إذا تم نشر التطبيق على متجر جوجل وكانت المدفوعات تفتح مزايا رقمية، فقد يلزم استخدام نظام دفع المتجر حسب سياساته.',\n 'methods'=>self::safeAll('SELECT id,method_key,title_ar,account_label_ar,account_value,instructions_ar FROM payment_methods WHERE is_active=1 ORDER BY id',[]),\n 'packages'=>self::safeAll('SELECT id,package_key,title_ar,description_ar,price,currency,duration_days FROM subscription_packages WHERE is_active=1 ORDER BY price',[]),\n ]);\n }\n public static function createManualPaymentRequest(): void {\n $u=Auth::user();\n if (self::setting('manual_payments_enabled') === '0') Response::json(false,'الدفع اليدوي غير متاح حالياً',[],403);\n $in=json_input();\n Validator::required($in,['package_id','payment_method_id']);\n $package=self::one('SELECT * FROM subscription_packages WHERE id=? AND is_active=1',[(int)$in['package_id']]);\n $method=self::one('SELECT * FROM payment_methods WHERE id=? AND is_active=1',[(int)$in['payment_method_id']]);\n if (!$package || !$method) Response::json(false,'باقة الدفع أو طريقة الدفع غير متاحة',[],404);\n $prefix=self::setting('payment_reference_prefix') ?: 'TYB';\n $reference=$prefix.'-'.$u['id'].'-'.date('YmdHis').'-'.random_int(100,999);\n self::pdo()->prepare('INSERT INTO manual_payment_requests(user_id,package_id,payment_method_id,reference_code,transaction_reference,screenshot_path,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?, ?,?)')\n ->execute([$u['id'],$package['id'],$method['id'],$reference,$in['transaction_reference'] ?? null,$in['screenshot_path'] ?? null,'pending',now(),now()]);\n $request=self::one('SELECT * FROM manual_payment_requests WHERE reference_code=?',[$reference]);\n Response::json(true,'تم إرسال طلب الدفع للمراجعة',['request'=>$request],201);\n }\n public static function paymentRequests(): void {\n $u=Auth::user();\n $rows=self::safeAll('SELECT r.*, p.title_ar package_title, p.price, p.currency, m.title_ar method_title FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id JOIN payment_methods m ON m.id=r.payment_method_id WHERE r.user_id=? ORDER BY r.id DESC',[$u['id']]);\n Response::json(true,'طلبات الدفع',['requests'=>$rows]);\n }\n public static function adminPaymentReview(string $id, string $action): void {\n self::requireLocal();\n $request=self::one('SELECT r.*, p.package_key, p.duration_days FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id WHERE r.id=?',[(int)$id]);\n if (!$request) Response::json(false,'طلب الدفع غير موجود',[],404);\n $status=$action === 'approve' ? 'approved' : 'rejected';\n self::pdo()->prepare('UPDATE manual_payment_requests SET status=?, reviewed_at=?, updated_at=? WHERE id=?')\n ->execute([$status,now(),now(),$request['id']]);\n if ($status === 'approved') {\n $plan=$request['package_key'] === 'premium_yearly' ? 'premium_yearly' : 'premium_monthly';\n $expires=date('Y-m-d H:i:s', strtotime('+' . (int)$request['duration_days'] . ' days'));\n EntitlementService::apply((int)$request['user_id'],$plan,$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,?, 'manual_payment','active',?,?,?,?,?,?)\")\n ->execute([$request['user_id'],$plan,now(),$expires,'manual',$request['reference_code'],now(),now()]);\n }\n Response::json(true,$status === 'approved' ? 'تم قبول الدفع وتفعيل الاشتراك' : 'تم رفض طلب الدفع');\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function isEmergencyMessage(string $message): bool {\n foreach (['ألم صدر','الم صدر','ضيق تنفس','إغماء','اغماء','نزيف شديد','زغللة شديدة','هبوط شديد','stroke','chest pain','shortness of breath','fainting'] as $needle) {\n if (mb_stripos($message, $needle) !== false) return true;\n }\n return false;\n }\n private static function updateExtendedHealthProfile(int $userId, array $in): void {\n $allowed = [\n 'birth_date','country','city','phone','preferred_dialect','diabetes_type',\n 'has_thyroid','digestive_issues','allergies','surgeries','family_history',\n 'activity_level','smoking','sleep_pattern','goals_json',\n ];\n $sets = [];\n $values = [];\n foreach ($allowed as $key) {\n if (!array_key_exists($key, $in)) continue;\n $sets[] = \"`$key`=?\";\n $value = $in[$key];\n if ($key === 'goals_json') $value = json_encode($value, JSON_UNESCAPED_UNICODE);\n if ($key === 'has_thyroid') $value = bool_int($value);\n $values[] = $value;\n }\n if (!$sets) return;\n try {\n $values[] = now();\n $values[] = $userId;\n self::pdo()->prepare('UPDATE health_profiles SET ' . implode(',', $sets) . ', updated_at=? WHERE user_id=?')->execute($values);\n } catch (Throwable) {\n }\n }\n private static function generalMedicationSafetyReply(string $message): ?string {\n $hasMedication = preg_match('/دواء|أدوية|ادوية|علاج|جرعة|الضغط|السكر|كوليسترول|medication|medicine|dose/i', $message);\n $hasChangeIntent = preg_match('/أوقف|اوقف|وقف|أقلل|اقلل|قلل|أغير|اغير|غير|تغيير|stop|reduce|change|taper|discontinue/i', $message);\n if ($hasMedication && $hasChangeIntent) {\n return 'لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك داخل التطبيق ومشاركة التقرير مع طبيبك.';\n }\n return null;\n }\n private static function normalizeTime(string $time): string {\n if (preg_match('/^(\\d{1,2}):(\\d{2})/', $time, $m)) {\n $hour = max(0, min(23, (int)$m[1]));\n $minute = max(0, min(59, (int)$m[2]));\n return str_pad((string)$hour, 2, '0', STR_PAD_LEFT) . ':' . str_pad((string)$minute, 2, '0', STR_PAD_LEFT) . ':00';\n }\n return '21:00:00';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function scalar(string $sql,array $p): mixed { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchColumn(); }\n private static function safeScalar(string $sql,array $p): mixed { try { return self::scalar($sql,$p); } catch (Throwable) { return 0; } }\n private static function safeAll(string $sql,array $p): array { try { return self::all($sql,$p); } catch (Throwable) { return []; } }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "d7f1deaf91728ec452f6fc083d994f37a29155bc": {"t": 1785756543.094322, "n": 76498, "h": "2b36000dadbc59de", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function startTrialSilently(int $userId): void {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$userId,$start,$end,now(),now()]);\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function appVersionCheck(): void {\n $in = $_SERVER['REQUEST_METHOD'] === 'POST' ? json_input() : $_GET;\n $platform = in_array(($in['platform'] ?? 'android'), ['android','ios'], true) ? $in['platform'] : 'android';\n $current = (string)($in['version'] ?? '1.0.0');\n $row = self::one('SELECT * FROM app_version_control WHERE platform=? LIMIT 1', [$platform]) ?: ['latest_version'=>'1.0.0','minimum_supported_version'=>'1.0.0','update_required'=>0,'update_title'=>'تحديث متاح','update_message'=>'يوجد إصدار أحدث من التطبيق.','play_store_url'=>'','direct_apk_url'=>null];\n $forced = version_compare($current, (string)$row['minimum_supported_version'], '<') || (bool)$row['update_required'];\n Response::json(true,'حالة الإصدار',['current_version'=>$current,'platform'=>$platform,'latest_version'=>$row['latest_version'],'minimum_supported_version'=>$row['minimum_supported_version'],'update_required'=>$forced,'optional_update'=>!$forced && version_compare($current,(string)$row['latest_version'],'<'),'update_title'=>$row['update_title'],'update_message'=>$row['update_message'],'play_store_url'=>$row['play_store_url'],'direct_apk_url'=>$row['direct_apk_url']]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n self::updateExtendedHealthProfile((int)$u['id'], $in);\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function healthDashboard(): void {\n $u=Auth::user(); $id=(int)$u['id'];\n $today = date('Y-m-d');\n $todayMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? AND measurement_date=? ORDER BY id DESC LIMIT 1',[$id,$today]);\n $latestMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 1',[$id]);\n $urgentReadings = [];\n if ($latestMeasurement) {\n if ((float)($latestMeasurement['systolic'] ?? 0) >= 180) $urgentReadings[] = 'systolic_bp';\n if ((float)($latestMeasurement['glucose'] ?? 0) >= 300) $urgentReadings[] = 'glucose_high';\n if ((float)($latestMeasurement['glucose'] ?? 999) > 0 && (float)($latestMeasurement['glucose'] ?? 999) <= 70) $urgentReadings[] = 'glucose_low';\n }\n Response::json(true,'لوحة الصحة',[\n 'mode'=>'general_health_companion',\n 'user'=>self::cleanUser($u),\n 'summary'=>[\n 'today'=>$today,\n 'measurements_today'=>(bool)$todayMeasurement,\n 'active_medications'=>(int)self::scalar('SELECT COUNT(*) FROM user_medications WHERE user_id=? AND currently_using=1',[$id]),\n 'symptoms_today'=>(int)self::safeScalar('SELECT COUNT(*) FROM symptom_logs WHERE user_id=? AND DATE(logged_at)=?',[$id,$today]),\n 'urgent_readings'=>$urgentReadings,\n ],\n 'next_medication'=>self::one('SELECT * FROM user_medications WHERE user_id=? AND currently_using=1 ORDER BY id DESC LIMIT 1',[$id]),\n 'today_measurement'=>$todayMeasurement,\n 'latest_measurement'=>$latestMeasurement,\n 'active_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? AND status=\"active\" ORDER BY id DESC',[$id]),\n 'active_program_keys'=>array_column(self::all('SELECT program_key FROM user_health_programs WHERE user_id=? AND status=\"active\"',[$id]), 'program_key'),\n 'program_cards'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'safety_note'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.',\n ]);\n }\n public static function healthPrograms(): void {\n $u=Auth::user();\n Response::json(true,'البرامج الصحية',['programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']])]);\n }\n public static function userHealthPrograms(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'POST') {\n $in=json_input();\n $programKey=(string)($in['program_key'] ?? '');\n if ($programKey === '') Response::json(false,'مفتاح البرنامج مطلوب',[],400);\n $program=self::one('SELECT * FROM health_programs WHERE program_key=? AND is_active=1 LIMIT 1',[$programKey]);\n if (!$program) Response::json(false,'البرنامج غير موجود',[],404);\n $existing=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=? AND status=\"active\" LIMIT 1',[$u['id'],$programKey]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,?,?, ?,1,?,?,?)')\n ->execute([$u['id'],$programKey,'active',now(),'undecided',now(),now()]);\n }\n }\n Response::json(true,'برامج المستخدم الصحية',[\n 'programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function altayyibatProgram(string $action): void {\n $u=Auth::user(); $pdo=self::pdo(); $now=now();\n if($action==='start'){\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1',[$u['id']]);\n if(!$p){$pdo->prepare(\"INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,'altayyibat_14_day','active',?,1,'undecided',?,?)\")->execute([$u['id'],$now,$now,$now]); if(!self::currentTrial((int)$u['id'])) self::startTrialSilently((int)$u['id']);}\n Response::json(true,'تم تفعيل برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']])]);\n }\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$p) Response::json(false,'لا يوجد برنامج طيبات لهذا المستخدم',[],404);\n if($action==='stop') $pdo->prepare(\"UPDATE user_health_programs SET status='stopped',stopped_at=?,decision='stop',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n if($action==='continue') $pdo->prepare(\"UPDATE user_health_programs SET status='completed',completed_at=COALESCE(completed_at,?),decision='continue',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n Response::json(true,'تم تحديث حالة برنامج الطيبات',['program'=>self::one('SELECT * FROM user_health_programs WHERE id=?',[$p['id']])]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'] ?? null,$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'القياسات أو الأعراض التي سجلتها قد تحتاج تقييم طبي. إذا كانت الحالة شديدة أو مستمرة، تواصل مع طبيب فورًا.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة AI',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,schedule_times,start_date,end_date,prescribed_by_doctor,currently_using,reminders_enabled,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, json_encode($schedule, JSON_UNESCAPED_UNICODE),\n $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,schedule_times=?,start_date=?,end_date=?,prescribed_by_doctor=?,currently_using=?,reminders_enabled=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n json_encode($schedule, JSON_UNESCAPED_UNICODE), $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function medicationLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT ml.*, um.medication_name FROM medication_logs ml LEFT JOIN user_medications um ON um.id=ml.medication_id WHERE ml.user_id=? ORDER BY ml.created_at DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأدوية',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $status=(string)($in['status'] ?? 'taken');\n if (!in_array($status, ['taken','skipped','missed'], true)) Response::json(false,'حالة تسجيل الدواء غير صحيحة',[],400);\n $medId=!empty($in['medication_id']) ? (int)$in['medication_id'] : null;\n if ($medId && !self::one('SELECT id FROM user_medications WHERE id=? AND user_id=? LIMIT 1',[$medId,$u['id']])) Response::json(false,'الدواء غير موجود',[],404);\n $pdo->prepare('INSERT INTO medication_logs(user_id,medication_id,scheduled_at,status,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?)')\n ->execute([$u['id'],$medId,$in['scheduled_at'] ?? null,$status,$in['notes'] ?? null,now(),now()]);\n Response::json(true,'تم حفظ سجل الدواء',['log'=>self::one('SELECT * FROM medication_logs WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function notificationSettings(): void {\n $u=Auth::user(); $pdo=self::pdo();\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_notification_settings(user_id,medication_reminders_enabled,measurement_reminder_enabled,measurement_reminder_time,checkin_reminder_enabled,checkin_reminder_time,altayyibat_reminder_enabled,altayyibat_reminder_time,created_at,updated_at) VALUES(?,1,1,?,1,?,1,?,?,?)')\n ->execute([$u['id'],'21:00:00','21:30:00','20:30:00',now(),now()]);\n $existing=self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']]);\n }\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n Response::json(true,'إعدادات التنبيهات',['settings'=>$existing]);\n }\n $in=json_input();\n $measurementTime=self::normalizeTime((string)($in['measurement_reminder_time'] ?? $existing['measurement_reminder_time']));\n $checkinTime=self::normalizeTime((string)($in['checkin_reminder_time'] ?? $existing['checkin_reminder_time']));\n $altayyibatTime=self::normalizeTime((string)($in['altayyibat_reminder_time'] ?? $existing['altayyibat_reminder_time']));\n $pdo->prepare('UPDATE user_notification_settings SET medication_reminders_enabled=?, measurement_reminder_enabled=?, measurement_reminder_time=?, checkin_reminder_enabled=?, checkin_reminder_time=?, altayyibat_reminder_enabled=?, altayyibat_reminder_time=?, updated_at=? WHERE user_id=?')\n ->execute([\n bool_int($in['medication_reminders_enabled'] ?? $existing['medication_reminders_enabled']),\n bool_int($in['measurement_reminder_enabled'] ?? $existing['measurement_reminder_enabled']),\n $measurementTime,\n bool_int($in['checkin_reminder_enabled'] ?? $existing['checkin_reminder_enabled']),\n $checkinTime,\n bool_int($in['altayyibat_reminder_enabled'] ?? $existing['altayyibat_reminder_enabled']),\n $altayyibatTime,\n now(),\n $u['id'],\n ]);\n Response::json(true,'تم حفظ إعدادات التنبيهات',['settings'=>self::one('SELECT * FROM user_notification_settings WHERE user_id=?',[$u['id']])]);\n }\n public static function measurementTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"measurement\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'blood_pressure','label_ar'=>'ضغط الدم','unit'=>'mmHg'],\n ['field_key'=>'glucose','label_ar'=>'سكر الدم','unit'=>'mg/dL'],\n ['field_key'=>'weight','label_ar'=>'الوزن','unit'=>'kg'],\n ['field_key'=>'pulse','label_ar'=>'النبض','unit'=>'bpm'],\n ];\n }\n Response::json(true,'أنواع القياسات',['types'=>$rows]);\n }\n public static function symptomTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"symptom\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'chest_pain','label_ar'=>'ألم صدر'],\n ['field_key'=>'shortness_of_breath','label_ar'=>'ضيق تنفس'],\n ['field_key'=>'dizziness','label_ar'=>'دوخة'],\n ['field_key'=>'fatigue','label_ar'=>'إرهاق'],\n ];\n }\n Response::json(true,'أنواع الأعراض',['types'=>$rows]);\n }\n public static function symptomLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'سجلات الأعراض',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $key=trim((string)($in['symptom_key'] ?? 'general'));\n if ($key === '') $key='general';\n $severity=max(1,min(10,(int)($in['severity'] ?? 1)));\n $loggedAt=(string)($in['logged_at'] ?? now());\n $pdo->prepare('INSERT INTO symptom_logs(user_id,symptom_key,severity,duration_note,notes,logged_at,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$key,$severity,$in['duration_note'] ?? null,$in['notes'] ?? null,$loggedAt,now(),now()]);\n $urgent=in_array($key, ['chest_pain','shortness_of_breath','fainting','severe_bleeding','stroke_symptoms'], true) || $severity >= 8;\n Response::json(true,'تم حفظ العرض',[\n 'log'=>self::one('SELECT * FROM symptom_logs WHERE id=?',[(int)$pdo->lastInsertId()]),\n 'urgent'=>$urgent,\n 'urgent_message'=>$urgent ? 'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.' : null,\n ],201);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح AI لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $altayyibatActive = (bool)self::one('SELECT id FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1', [$u['id']]);\n $asksAltayyibat = str_contains((string)$in['message'], 'الطيبات') || str_contains((string)$in['message'], 'نظام الطيبات');\n if ($altayyibatActive || $asksAltayyibat) {\n $altMedReply = AltayebaatAiGuardService::medicationSafety((string)$in['message']);\n if ($altMedReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altMedReply,0,now()]);\n Response::json(true,'رد أمان الطيبات',['reply'=>$altMedReply,'safety_flags'=>['medication_safety']]);\n }\n $altDetected = AltayebaatFoodRuleService::detectInText((string)$in['message']);\n $altReply = AltayebaatFoodRuleService::forbiddenTextReply($altDetected['forbidden'] ?? []);\n if ($altReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$altReply,0,now()]);\n Response::json(true,'رد قواعد الطيبات',['reply'=>$altReply,'safety_flags'=>['altayyibat_forbidden_food']]);\n }\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function aiHealthChat(): void {\n $u=Auth::user();\n $in=json_input();\n $message=(string)($in['message'] ?? '');\n if (self::isEmergencyMessage($message)) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.',\n 'safety_flags'=>['emergency_symptoms'],\n ]);\n }\n $medReply = self::generalMedicationSafetyReply($message);\n if ($medReply) {\n Response::json(true,'رد أمان صحي',[\n 'reply'=>$medReply,\n 'safety_flags'=>['medication_safety'],\n ]);\n }\n self::aiChat();\n }\n public static function labFiles(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM lab_files WHERE user_id=? ORDER BY COALESCE(test_date, DATE(created_at)) DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'ملفات التحاليل',['files'=>$s->fetchAll()]);\n }\n $in=json_input();\n $category=trim((string)($in['category'] ?? 'general'));\n $pdo->prepare('INSERT INTO lab_files(user_id,category,file_name,file_path,file_type,test_date,notes,values_json,ocr_text,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)')\n ->execute([\n $u['id'],\n $category ?: 'general',\n $in['file_name'] ?? null,\n $in['file_path'] ?? null,\n $in['file_type'] ?? null,\n $in['test_date'] ?? null,\n $in['notes'] ?? null,\n isset($in['values']) ? json_encode($in['values'], JSON_UNESCAPED_UNICODE) : null,\n $in['ocr_text'] ?? null,\n now(),\n now(),\n ]);\n Response::json(true,'تم حفظ بيانات ملف التحليل',['file'=>self::one('SELECT * FROM lab_files WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function explainLab(): void {\n Auth::user();\n Response::json(true,'شرح التحليل',[\n 'reply'=>'أقدر أساعدك في تنظيم بيانات التحليل وشرح المعاني العامة، لكن لا أقدم تشخيصًا ولا أغير علاجًا. شارك التقرير مع طبيبك خاصة لو في نتائج خارج الطبيعي.',\n ]);\n }\n public static function syncPush(): void {\n $u=Auth::user(); $in=json_input(); $records=$in['records'] ?? [];\n if (!is_array($records)) Response::json(false,'بيانات المزامنة يجب أن تكون قائمة',[],400);\n $pdo=self::pdo(); $saved=0;\n foreach($records as $record){\n if (!is_array($record)) continue;\n $entity=(string)($record['entity_type'] ?? $record['type'] ?? 'unknown');\n $payload=$record['payload'] ?? $record;\n $pdo->prepare('INSERT INTO sync_events(user_id,client_id,entity_type,entity_id,operation,payload_json,synced_at,created_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$record['client_id'] ?? null,$entity,$record['entity_id'] ?? null,$record['operation'] ?? 'create',json_encode($payload, JSON_UNESCAPED_UNICODE),now(),now()]);\n $saved++;\n }\n Response::json(true,'تم قبول المزامنة',['saved'=>$saved,'accepted'=>$saved]);\n }\n public static function syncPull(): void {\n $u=Auth::user();\n Response::json(true,'بيانات المزامنة',[\n 'server_time'=>now(),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 100',[$u['id']]),\n 'symptom_logs'=>self::safeAll('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100',[$u['id']]),\n 'health_programs'=>self::safeAll('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function importAltayebaat(): void {\n self::requireLocal();\n $summary = AltayebaatImportService::importAll();\n Response::json(true,'تم استيراد بيانات الطيبات للمراجعة',['summary'=>$summary]);\n }\n public static function paymentOptions(): void {\n Auth::user();\n Response::json(true,'خيارات الدفع',[\n 'manual_payments_enabled'=>self::setting('manual_payments_enabled') !== '0',\n 'google_play_billing_enabled'=>self::setting('google_play_billing_enabled') === '1',\n 'google_play_warning'=>'إذا تم نشر التطبيق على Google Play وكانت المدفوعات تفتح مزايا رقمية، فقد يلزم استخدام Google Play Billing حسب سياسات Google.',\n 'methods'=>self::safeAll('SELECT id,method_key,title_ar,account_label_ar,account_value,instructions_ar FROM payment_methods WHERE is_active=1 ORDER BY id',[]),\n 'packages'=>self::safeAll('SELECT id,package_key,title_ar,description_ar,price,currency,duration_days FROM subscription_packages WHERE is_active=1 ORDER BY price',[]),\n ]);\n }\n public static function createManualPaymentRequest(): void {\n $u=Auth::user();\n if (self::setting('manual_payments_enabled') === '0') Response::json(false,'الدفع اليدوي غير متاح حالياً',[],403);\n $in=json_input();\n Validator::required($in,['package_id','payment_method_id']);\n $package=self::one('SELECT * FROM subscription_packages WHERE id=? AND is_active=1',[(int)$in['package_id']]);\n $method=self::one('SELECT * FROM payment_methods WHERE id=? AND is_active=1',[(int)$in['payment_method_id']]);\n if (!$package || !$method) Response::json(false,'باقة الدفع أو طريقة الدفع غير متاحة',[],404);\n $prefix=self::setting('payment_reference_prefix') ?: 'TYB';\n $reference=$prefix.'-'.$u['id'].'-'.date('YmdHis').'-'.random_int(100,999);\n self::pdo()->prepare('INSERT INTO manual_payment_requests(user_id,package_id,payment_method_id,reference_code,transaction_reference,screenshot_path,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?, ?,?)')\n ->execute([$u['id'],$package['id'],$method['id'],$reference,$in['transaction_reference'] ?? null,$in['screenshot_path'] ?? null,'pending',now(),now()]);\n $request=self::one('SELECT * FROM manual_payment_requests WHERE reference_code=?',[$reference]);\n Response::json(true,'تم إرسال طلب الدفع للمراجعة',['request'=>$request],201);\n }\n public static function paymentRequests(): void {\n $u=Auth::user();\n $rows=self::safeAll('SELECT r.*, p.title_ar package_title, p.price, p.currency, m.title_ar method_title FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id JOIN payment_methods m ON m.id=r.payment_method_id WHERE r.user_id=? ORDER BY r.id DESC',[$u['id']]);\n Response::json(true,'طلبات الدفع',['requests'=>$rows]);\n }\n public static function adminPaymentReview(string $id, string $action): void {\n self::requireLocal();\n $request=self::one('SELECT r.*, p.package_key, p.duration_days FROM manual_payment_requests r JOIN subscription_packages p ON p.id=r.package_id WHERE r.id=?',[(int)$id]);\n if (!$request) Response::json(false,'طلب الدفع غير موجود',[],404);\n $status=$action === 'approve' ? 'approved' : 'rejected';\n self::pdo()->prepare('UPDATE manual_payment_requests SET status=?, reviewed_at=?, updated_at=? WHERE id=?')\n ->execute([$status,now(),now(),$request['id']]);\n if ($status === 'approved') {\n $plan=$request['package_key'] === 'premium_yearly' ? 'premium_yearly' : 'premium_monthly';\n $expires=date('Y-m-d H:i:s', strtotime('+' . (int)$request['duration_days'] . ' days'));\n EntitlementService::apply((int)$request['user_id'],$plan,$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,?, 'manual_payment','active',?,?,?,?,?,?)\")\n ->execute([$request['user_id'],$plan,now(),$expires,'manual',$request['reference_code'],now(),now()]);\n }\n Response::json(true,$status === 'approved' ? 'تم قبول الدفع وتفعيل الاشتراك' : 'تم رفض طلب الدفع');\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function isEmergencyMessage(string $message): bool {\n foreach (['ألم صدر','الم صدر','ضيق تنفس','إغماء','اغماء','نزيف شديد','زغللة شديدة','هبوط شديد','stroke','chest pain','shortness of breath','fainting'] as $needle) {\n if (mb_stripos($message, $needle) !== false) return true;\n }\n return false;\n }\n private static function updateExtendedHealthProfile(int $userId, array $in): void {\n $allowed = [\n 'birth_date','country','city','phone','preferred_dialect','diabetes_type',\n 'has_thyroid','digestive_issues','allergies','surgeries','family_history',\n 'activity_level','smoking','sleep_pattern','goals_json',\n ];\n $sets = [];\n $values = [];\n foreach ($allowed as $key) {\n if (!array_key_exists($key, $in)) continue;\n $sets[] = \"`$key`=?\";\n $value = $in[$key];\n if ($key === 'goals_json') $value = json_encode($value, JSON_UNESCAPED_UNICODE);\n if ($key === 'has_thyroid') $value = bool_int($value);\n $values[] = $value;\n }\n if (!$sets) return;\n try {\n $values[] = now();\n $values[] = $userId;\n self::pdo()->prepare('UPDATE health_profiles SET ' . implode(',', $sets) . ', updated_at=? WHERE user_id=?')->execute($values);\n } catch (Throwable) {\n }\n }\n private static function generalMedicationSafetyReply(string $message): ?string {\n $hasMedication = preg_match('/دواء|أدوية|ادوية|علاج|جرعة|الضغط|السكر|كوليسترول|medication|medicine|dose/i', $message);\n $hasChangeIntent = preg_match('/أوقف|اوقف|وقف|أقلل|اقلل|قلل|أغير|اغير|غير|تغيير|stop|reduce|change|taper|discontinue/i', $message);\n if ($hasMedication && $hasChangeIntent) {\n return 'لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك داخل التطبيق ومشاركة التقرير مع طبيبك.';\n }\n return null;\n }\n private static function normalizeTime(string $time): string {\n if (preg_match('/^(\\d{1,2}):(\\d{2})/', $time, $m)) {\n $hour = max(0, min(23, (int)$m[1]));\n $minute = max(0, min(59, (int)$m[2]));\n return str_pad((string)$hour, 2, '0', STR_PAD_LEFT) . ':' . str_pad((string)$minute, 2, '0', STR_PAD_LEFT) . ':00';\n }\n return '21:00:00';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function scalar(string $sql,array $p): mixed { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchColumn(); }\n private static function safeScalar(string $sql,array $p): mixed { try { return self::scalar($sql,$p); } catch (Throwable) { return 0; } }\n private static function safeAll(string $sql,array $p): array { try { return self::all($sql,$p); } catch (Throwable) { return []; } }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "b52678896aee4edad5551039f216851c1f4db892": {"t": 1785756544.869744, "n": 66470, "h": "e3c373ebfdfb3b7e", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function startTrialSilently(int $userId): void {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$userId,$start,$end,now(),now()]);\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function appVersionCheck(): void {\n $in = $_SERVER['REQUEST_METHOD'] === 'POST' ? json_input() : $_GET;\n $platform = in_array(($in['platform'] ?? 'android'), ['android','ios'], true) ? $in['platform'] : 'android';\n $current = (string)($in['version'] ?? '1.0.0');\n $row = self::one('SELECT * FROM app_version_control WHERE platform=? LIMIT 1', [$platform]) ?: ['latest_version'=>'1.0.0','minimum_supported_version'=>'1.0.0','update_required'=>0,'update_title'=>'Update','update_message'=>'Update available','play_store_url'=>'','direct_apk_url'=>null];\n $forced = version_compare($current, (string)$row['minimum_supported_version'], '<') || (bool)$row['update_required'];\n Response::json(true,'Version status',['current_version'=>$current,'platform'=>$platform,'latest_version'=>$row['latest_version'],'minimum_supported_version'=>$row['minimum_supported_version'],'update_required'=>$forced,'optional_update'=>!$forced && version_compare($current,(string)$row['latest_version'],'<'),'update_title'=>$row['update_title'],'update_message'=>$row['update_message'],'play_store_url'=>$row['play_store_url'],'direct_apk_url'=>$row['direct_apk_url']]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function healthDashboard(): void {\n $u=Auth::user(); $id=(int)$u['id'];\n $today = date('Y-m-d');\n $todayMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? AND measurement_date=? ORDER BY id DESC LIMIT 1',[$id,$today]);\n $latestMeasurement = self::one('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 1',[$id]);\n $urgentReadings = [];\n if ($latestMeasurement) {\n if ((float)($latestMeasurement['systolic'] ?? 0) >= 180) $urgentReadings[] = 'systolic_bp';\n if ((float)($latestMeasurement['glucose'] ?? 0) >= 300) $urgentReadings[] = 'glucose_high';\n if ((float)($latestMeasurement['glucose'] ?? 999) > 0 && (float)($latestMeasurement['glucose'] ?? 999) <= 70) $urgentReadings[] = 'glucose_low';\n }\n Response::json(true,'Health dashboard',[\n 'mode'=>'general_health_companion',\n 'user'=>self::cleanUser($u),\n 'summary'=>[\n 'today'=>$today,\n 'measurements_today'=>(bool)$todayMeasurement,\n 'active_medications'=>(int)self::scalar('SELECT COUNT(*) FROM user_medications WHERE user_id=? AND currently_using=1',[$id]),\n 'symptoms_today'=>(int)self::safeScalar('SELECT COUNT(*) FROM symptom_logs WHERE user_id=? AND DATE(logged_at)=?',[$id,$today]),\n 'urgent_readings'=>$urgentReadings,\n ],\n 'next_medication'=>self::one('SELECT * FROM user_medications WHERE user_id=? AND currently_using=1 ORDER BY id DESC LIMIT 1',[$id]),\n 'today_measurement'=>$todayMeasurement,\n 'latest_measurement'=>$latestMeasurement,\n 'active_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? AND status=\"active\" ORDER BY id DESC',[$id]),\n 'program_cards'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'safety_note'=>'Never stop or change medication except with doctor supervision.',\n ]);\n }\n public static function healthPrograms(): void {\n $u=Auth::user();\n Response::json(true,'Health programs',['programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']])]);\n }\n public static function userHealthPrograms(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'POST') {\n $in=json_input();\n $programKey=(string)($in['program_key'] ?? '');\n if ($programKey === '') Response::json(false,'program_key is required',[],400);\n $program=self::one('SELECT * FROM health_programs WHERE program_key=? AND is_active=1 LIMIT 1',[$programKey]);\n if (!$program) Response::json(false,'Program not found',[],404);\n $existing=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=? AND status=\"active\" LIMIT 1',[$u['id'],$programKey]);\n if (!$existing) {\n $pdo->prepare('INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,?,?, ?,1,?,?,?)')\n ->execute([$u['id'],$programKey,'active',now(),'undecided',now(),now()]);\n }\n }\n Response::json(true,'User health programs',[\n 'programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function altayyibatProgram(string $action): void {\n $u=Auth::user(); $pdo=self::pdo(); $now=now();\n if($action==='start'){\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1',[$u['id']]);\n if(!$p){$pdo->prepare(\"INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,'altayyibat_14_day','active',?,1,'undecided',?,?)\")->execute([$u['id'],$now,$now,$now]); if(!self::currentTrial((int)$u['id'])) self::startTrialSilently((int)$u['id']);}\n Response::json(true,'Altayyibat program started',['program'=>self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']])]);\n }\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$p) Response::json(false,'No program',[],404);\n if($action==='stop') $pdo->prepare(\"UPDATE user_health_programs SET status='stopped',stopped_at=?,decision='stop',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n if($action==='continue') $pdo->prepare(\"UPDATE user_health_programs SET status='completed',completed_at=COALESCE(completed_at,?),decision='continue',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n Response::json(true,'Program updated',['program'=>self::one('SELECT * FROM user_health_programs WHERE id=?',[$p['id']])]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'] ?? null,$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'القياسات أو الأعراض التي سجلتها قد تحتاج تقييم طبي. إذا كانت الحالة شديدة أو مستمرة، تواصل مع طبيب فورًا.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة AI',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,schedule_times,start_date,end_date,prescribed_by_doctor,currently_using,reminders_enabled,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, json_encode($schedule, JSON_UNESCAPED_UNICODE),\n $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $schedule = $in['schedule_times'] ?? [];\n if (is_string($schedule)) $schedule = array_values(array_filter(array_map('trim', explode(',', $schedule))));\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,schedule_times=?,start_date=?,end_date=?,prescribed_by_doctor=?,currently_using=?,reminders_enabled=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n json_encode($schedule, JSON_UNESCAPED_UNICODE), $in['start_date'] ?? null, $in['end_date'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1), bool_int($in['reminders_enabled'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function medicationLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT ml.*, um.medication_name FROM medication_logs ml LEFT JOIN user_medications um ON um.id=ml.medication_id WHERE ml.user_id=? ORDER BY ml.created_at DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'Medication logs',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $status=(string)($in['status'] ?? 'taken');\n if (!in_array($status, ['taken','skipped','missed'], true)) Response::json(false,'Invalid medication log status',[],400);\n $medId=!empty($in['medication_id']) ? (int)$in['medication_id'] : null;\n if ($medId && !self::one('SELECT id FROM user_medications WHERE id=? AND user_id=? LIMIT 1',[$medId,$u['id']])) Response::json(false,'Medication not found',[],404);\n $pdo->prepare('INSERT INTO medication_logs(user_id,medication_id,scheduled_at,status,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?)')\n ->execute([$u['id'],$medId,$in['scheduled_at'] ?? null,$status,$in['notes'] ?? null,now(),now()]);\n Response::json(true,'Medication log saved',['log'=>self::one('SELECT * FROM medication_logs WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function measurementTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"measurement\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'blood_pressure','label_ar'=>'ضغط الدم','unit'=>'mmHg'],\n ['field_key'=>'glucose','label_ar'=>'سكر الدم','unit'=>'mg/dL'],\n ['field_key'=>'weight','label_ar'=>'الوزن','unit'=>'kg'],\n ['field_key'=>'pulse','label_ar'=>'النبض','unit'=>'bpm'],\n ];\n }\n Response::json(true,'Measurement types',['types'=>$rows]);\n }\n public static function symptomTypes(): void {\n $rows=self::all('SELECT * FROM dynamic_form_fields WHERE form_key=\"symptom\" AND is_active=1 ORDER BY sort_order,id',[]);\n if (!$rows) {\n $rows=[\n ['field_key'=>'chest_pain','label_ar'=>'ألم صدر'],\n ['field_key'=>'shortness_of_breath','label_ar'=>'ضيق تنفس'],\n ['field_key'=>'dizziness','label_ar'=>'دوخة'],\n ['field_key'=>'fatigue','label_ar'=>'إرهاق'],\n ];\n }\n Response::json(true,'Symptom types',['types'=>$rows]);\n }\n public static function symptomLogs(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'Symptom logs',['logs'=>$s->fetchAll()]);\n }\n $in=json_input();\n $key=trim((string)($in['symptom_key'] ?? 'general'));\n if ($key === '') $key='general';\n $severity=max(1,min(10,(int)($in['severity'] ?? 1)));\n $loggedAt=(string)($in['logged_at'] ?? now());\n $pdo->prepare('INSERT INTO symptom_logs(user_id,symptom_key,severity,duration_note,notes,logged_at,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$key,$severity,$in['duration_note'] ?? null,$in['notes'] ?? null,$loggedAt,now(),now()]);\n $urgent=in_array($key, ['chest_pain','shortness_of_breath','fainting','severe_bleeding','stroke_symptoms'], true) || $severity >= 8;\n Response::json(true,'Symptom log saved',[\n 'log'=>self::one('SELECT * FROM symptom_logs WHERE id=?',[(int)$pdo->lastInsertId()]),\n 'urgent'=>$urgent,\n 'urgent_message'=>$urgent ? 'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.' : null,\n ],201);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح AI لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function aiHealthChat(): void {\n $u=Auth::user();\n $in=json_input();\n $message=(string)($in['message'] ?? '');\n if (self::isEmergencyMessage($message)) {\n Response::json(true,'Health safety reply',[\n 'reply'=>'الأعراض دي محتاجة تقييم طبي عاجل. لو الحالة شديدة أو مستمرة، تواصل مع الطوارئ أو طبيب فورًا.',\n 'safety_flags'=>['emergency_symptoms'],\n ]);\n }\n $medReply = self::generalMedicationSafetyReply($message);\n if ($medReply) {\n Response::json(true,'Health safety reply',[\n 'reply'=>$medReply,\n 'safety_flags'=>['medication_safety'],\n ]);\n }\n self::aiChat();\n }\n public static function labFiles(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM lab_files WHERE user_id=? ORDER BY COALESCE(test_date, DATE(created_at)) DESC,id DESC LIMIT 100');\n $s->execute([$u['id']]);\n Response::json(true,'Lab files',['files'=>$s->fetchAll()]);\n }\n $in=json_input();\n $category=trim((string)($in['category'] ?? 'general'));\n $pdo->prepare('INSERT INTO lab_files(user_id,category,file_name,file_path,file_type,test_date,notes,values_json,ocr_text,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)')\n ->execute([\n $u['id'],\n $category ?: 'general',\n $in['file_name'] ?? null,\n $in['file_path'] ?? null,\n $in['file_type'] ?? null,\n $in['test_date'] ?? null,\n $in['notes'] ?? null,\n isset($in['values']) ? json_encode($in['values'], JSON_UNESCAPED_UNICODE) : null,\n $in['ocr_text'] ?? null,\n now(),\n now(),\n ]);\n Response::json(true,'Lab file metadata saved',['file'=>self::one('SELECT * FROM lab_files WHERE id=?',[(int)$pdo->lastInsertId()])],201);\n }\n public static function explainLab(): void {\n Auth::user();\n Response::json(true,'Lab explanation',[\n 'reply'=>'أقدر أساعدك في تنظيم بيانات التحليل وشرح المعاني العامة، لكن لا أقدم تشخيصًا ولا أغير علاجًا. شارك التقرير مع طبيبك خاصة لو في نتائج خارج الطبيعي.',\n ]);\n }\n public static function syncPush(): void {\n $u=Auth::user(); $in=json_input(); $records=$in['records'] ?? [];\n if (!is_array($records)) Response::json(false,'records must be an array',[],400);\n $pdo=self::pdo(); $saved=0;\n foreach($records as $record){\n if (!is_array($record)) continue;\n $entity=(string)($record['entity_type'] ?? $record['type'] ?? 'unknown');\n $payload=$record['payload'] ?? $record;\n $pdo->prepare('INSERT INTO sync_events(user_id,client_id,entity_type,entity_id,operation,payload_json,synced_at,created_at) VALUES(?,?,?,?,?,?,?,?)')\n ->execute([$u['id'],$record['client_id'] ?? null,$entity,$record['entity_id'] ?? null,$record['operation'] ?? 'create',json_encode($payload, JSON_UNESCAPED_UNICODE),now(),now()]);\n $saved++;\n }\n Response::json(true,'Sync accepted',['saved'=>$saved,'accepted'=>$saved]);\n }\n public static function syncPull(): void {\n $u=Auth::user();\n Response::json(true,'Sync payload',[\n 'server_time'=>now(),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 100',[$u['id']]),\n 'symptom_logs'=>self::safeAll('SELECT * FROM symptom_logs WHERE user_id=? ORDER BY logged_at DESC,id DESC LIMIT 100',[$u['id']]),\n 'health_programs'=>self::safeAll('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n ]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function isEmergencyMessage(string $message): bool {\n foreach (['ألم صدر','الم صدر','ضيق تنفس','إغماء','اغماء','نزيف شديد','زغللة شديدة','هبوط شديد','stroke','chest pain','shortness of breath','fainting'] as $needle) {\n if (mb_stripos($message, $needle) !== false) return true;\n }\n return false;\n }\n private static function generalMedicationSafetyReply(string $message): ?string {\n $hasMedication = preg_match('/دواء|أدوية|ادوية|علاج|جرعة|الضغط|السكر|كوليسترول|medication|medicine|dose/i', $message);\n $hasChangeIntent = preg_match('/أوقف|اوقف|وقف|أقلل|اقلل|قلل|أغير|اغير|غير|تغيير|stop|reduce|change|taper|discontinue/i', $message);\n if ($hasMedication && $hasChangeIntent) {\n return 'لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك داخل التطبيق ومشاركة التقرير مع طبيبك.';\n }\n return null;\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function scalar(string $sql,array $p): mixed { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchColumn(); }\n private static function safeScalar(string $sql,array $p): mixed { try { return self::scalar($sql,$p); } catch (Throwable) { return 0; } }\n private static function safeAll(string $sql,array $p): array { try { return self::all($sql,$p); } catch (Throwable) { return []; } }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "7a8f77b6d4b4c31d8a74af48a58fe84dee0b7b8d": {"t": 1785756546.8123982, "n": 59304, "h": "caa7cba317ad7e34", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function startTrialSilently(int $userId): void {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$userId,$start,$end,now(),now()]);\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function appVersionCheck(): void {\n $in = $_SERVER['REQUEST_METHOD'] === 'POST' ? json_input() : $_GET;\n $platform = in_array(($in['platform'] ?? 'android'), ['android','ios'], true) ? $in['platform'] : 'android';\n $current = (string)($in['version'] ?? '1.0.0');\n $row = self::one('SELECT * FROM app_version_control WHERE platform=? LIMIT 1', [$platform]) ?: ['latest_version'=>'1.0.0','minimum_supported_version'=>'1.0.0','update_required'=>0,'update_title'=>'Update','update_message'=>'Update available','play_store_url'=>'','direct_apk_url'=>null];\n $forced = version_compare($current, (string)$row['minimum_supported_version'], '<') || (bool)$row['update_required'];\n Response::json(true,'Version status',['current_version'=>$current,'platform'=>$platform,'latest_version'=>$row['latest_version'],'minimum_supported_version'=>$row['minimum_supported_version'],'update_required'=>$forced,'optional_update'=>!$forced && version_compare($current,(string)$row['latest_version'],'<'),'update_title'=>$row['update_title'],'update_message'=>$row['update_message'],'play_store_url'=>$row['play_store_url'],'direct_apk_url'=>$row['direct_apk_url']]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,ØŒ\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('ØŒ ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function healthDashboard(): void {\n $u=Auth::user(); $id=(int)$u['id'];\n Response::json(true,'Health dashboard',[\n 'mode'=>'general_health_companion',\n 'user'=>self::cleanUser($u),\n 'summary'=>['today'=>date('Y-m-d'),'measurements_today'=>(bool)self::one('SELECT id FROM measurements WHERE user_id=? AND measurement_date=? LIMIT 1',[$id,date('Y-m-d')])],\n 'next_medication'=>self::one('SELECT * FROM user_medications WHERE user_id=? AND currently_using=1 ORDER BY id DESC LIMIT 1',[$id]),\n 'latest_measurement'=>self::one('SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 1',[$id]),\n 'active_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? AND status=\"active\" ORDER BY id DESC',[$id]),\n 'program_cards'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),\n 'safety_note'=>'Never stop or change medication except with doctor supervision.',\n ]);\n }\n public static function healthPrograms(): void {\n $u=Auth::user();\n Response::json(true,'Health programs',['programs'=>self::all('SELECT * FROM health_programs WHERE is_active=1 ORDER BY id',[]),'user_programs'=>self::all('SELECT * FROM user_health_programs WHERE user_id=? ORDER BY id DESC',[$u['id']])]);\n }\n public static function altayyibatProgram(string $action): void {\n $u=Auth::user(); $pdo=self::pdo(); $now=now();\n if($action==='start'){\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" AND status=\"active\" LIMIT 1',[$u['id']]);\n if(!$p){$pdo->prepare(\"INSERT INTO user_health_programs(user_id,program_key,status,started_at,current_day,decision,created_at,updated_at) VALUES(?,'altayyibat_14_day','active',?,1,'undecided',?,?)\")->execute([$u['id'],$now,$now,$now]); if(!self::currentTrial((int)$u['id'])) self::startTrialSilently((int)$u['id']);}\n Response::json(true,'Altayyibat program started',['program'=>self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']])]);\n }\n $p=self::one('SELECT * FROM user_health_programs WHERE user_id=? AND program_key=\"altayyibat_14_day\" ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$p) Response::json(false,'No program',[],404);\n if($action==='stop') $pdo->prepare(\"UPDATE user_health_programs SET status='stopped',stopped_at=?,decision='stop',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n if($action==='continue') $pdo->prepare(\"UPDATE user_health_programs SET status='completed',completed_at=COALESCE(completed_at,?),decision='continue',updated_at=? WHERE id=?\")->execute([$now,$now,$p['id']]);\n Response::json(true,'Program updated',['program'=>self::one('SELECT * FROM user_health_programs WHERE id=?',[$p['id']])]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة AI',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,prescribed_by_doctor=?,currently_using=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,ØŒ\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('ØŒ ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('ØŒ ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح AI لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('ØŒ ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('ØŒ ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"ØŸ?ØŒ,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('ØŒ ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "74c03a71dad58af3d79757981dbc88f3401c1df1": {"t": 1785756548.895984, "n": 49552, "h": "4e69a056ae2618c7", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function about(): void {\n $defaults = [\n ['category'=>'general_rule','title'=>'ما هو نظام الطيبات؟','content'=>'الطيبات داخل التطبيق تجربة نظام غذائي وسلوك يومي مبني على قاعدة: كل واشرب ولا تسرف، مع متابعة القياسات والحالة والوجبات خلال فترة تقييم أولية.'],\n ['category'=>'general_rule','title'=>'القاعدة الذهبية: كل واشرب ولا تسرف','content'=>'الفكرة الأساسية هي الأكل عند الجوع، والشرب عند العطش، والتوقف قبل الشبع، والالتزام بالمسموحات وتجنب الممنوعات.'],\n ['category'=>'meal_rule','title'=>'لا توجد وجبات ثابتة مفروضة','content'=>'التطبيق لا يجبر المستخدم على مواعيد وجبات أو جدول يومي ثابت. يسجل المستخدم ما يحدث فعلاً ويطلب اقتراحاً عند الجوع فقط.'],\n ['category'=>'frequency_rule','title'=>'مؤشر التكرار','content'=>'بعض الأصناف تكون أساسية، وبعضها يومي أو أسبوعي أو أحياناً، وما ليس في المسموحات لا يتم اقتراحه داخل التطبيق.'],\n ['category'=>'warning','title'=>'تنبيه طبي مهم','content'=>'التطبيق لا يقدم تشخيصاً أو علاجاً. لا توقف أو تغير جرعة أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل قياساتك وأدويتك ومشاركة التقرير مع طبيبك.'],\n ];\n $items = KnowledgeService::getGeneralSystemRules();\n Response::json(true,'عن الطيبات',[\n 'items'=>$items ?: $defaults,\n 'sources'=>self::all('SELECT title,source_type,url,description,is_active FROM system_knowledge_sources ORDER BY id DESC LIMIT 20',[]),\n 'doctor_photo_url'=>self::setting('doctor_photo_url'),\n 'doctor_photo_path'=>self::setting('doctor_photo_path'),\n ]);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n $items = [];\n foreach ($s->fetchAll() as $row) {\n $rules = KnowledgeService::getRulesForFood((string)$row['name_ar']);\n $reason = null; $frequency = null; $source = null;\n foreach ($rules as $rule) {\n if (!$reason && ($rule['category'] ?? '') === 'reason') $reason = $rule['content'];\n if (!$frequency && !empty($rule['frequency_level'])) $frequency = $rule['frequency_level'];\n if (!$source && !empty($rule['source_id'])) $source = $rule['source_id'];\n }\n $row['reason'] = $reason;\n $row['frequency_level'] = $frequency;\n $row['knowledge_source'] = $source;\n $items[] = $row;\n }\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$items]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة AI',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,prescribed_by_doctor=?,currently_using=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح AI لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $safe = self::cleanAiText($safe);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function cleanAiText(string $text): string {\n $text = preg_replace('/\\*\\*/u', '', $text);\n $text = preg_replace('/^\\s*#{1,6}\\s*/um', '', $text);\n $text = preg_replace('/^\\s*[-_]{3,}\\s*$/um', '', $text);\n $text = preg_replace('/[ \\t]+/u', ' ', $text);\n $text = preg_replace('/\\n{3,}/u', \"\\n\\n\", $text);\n return trim((string)$text);\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "9a2985252ee6217efe2feb003ca19960602007d6": {"t": 1785756550.6261775, "n": 46809, "h": "44abb70d3eda1ab7", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function aiGuardTest(): void {\n self::requireLocal();\n $in=json_input(); $message=(string)($in['message'] ?? '');\n $foods=FoodRuleService::detectFoodsInText($message);\n $forbidden=FoodRuleService::findForbiddenFoods($message);\n $guard=AiGuardService::validateUserMessage($message);\n $reply=null; $deepSeekCalled=false;\n if(!$guard['valid']) $reply=AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n elseif($forbidden) $reply=self::forbiddenFoodRuleReply($message, array_column($forbidden,'name'), [], self::names('allowed'));\n else $deepSeekCalled=true;\n Response::json(true,'اختبار حراسة AI',[\n 'detected_foods'=>$foods,\n 'forbidden_foods'=>$forbidden,\n 'deepseek_would_be_called'=>$deepSeekCalled,\n 'backend_response'=>$reply,\n 'safety_flags'=>$guard['flags'],\n ]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,prescribed_by_doctor=?,currently_using=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $validation = FoodRuleService::validateMealItems($available);\n $safeIngredients = $validation['allowed'];\n $forbiddenHits = $validation['forbidden'];\n $unknownHits = $validation['unknown'];\n if (!$safeIngredients) {\n $reason = $forbiddenHits ? 'كل المكونات المتاحة فيها ممنوعات' : 'لا توجد مكونات مسموحة معروفة';\n Response::json(false, $reason, [\n 'meal'=>[\n 'meal_title'=>'لا يمكن اقتراح وجبة',\n 'items'=>[],\n 'notes'=>'اختر مكونات من المسموحات فقط. كل على قدر الجوع بدون إسراف.',\n 'warnings'=>array_values(array_filter([\n $forbiddenHits ? 'ممنوع: '.implode('، ', $forbiddenHits) : null,\n $unknownHits ? 'غير معروف داخل قاعدة المسموحات: '.implode('، ', $unknownHits) : null,\n ])),\n 'is_allowed'=>false,\n 'generated_by'=>'rule_based',\n ],\n ], 422);\n }\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'rejected_forbidden'=>$forbiddenHits,\n 'rejected_unknown'=>$unknownHits,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'notes'=>$in['notes'] ?? null,\n ], JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $itemValidation = FoodRuleService::validateMealItems((array)$json['items']);\n if (!$itemValidation['valid']) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based','warnings'=>array_merge($fallback['warnings'], ['تم رفض اقتراح AI لأنه احتوى على صنف ممنوع أو غير معروف.'])]]);\n }\n $items = $itemValidation['allowed'];\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل على قدر الجوع بدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [], $unknownHits ? ['تم تجاهل أصناف غير معروفة: '.implode('، ', $unknownHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $guard = AiGuardService::validateUserMessage((string)$in['message']);\n if (!$guard['valid']) {\n $reply = AiGuardService::safeFallback($guard['flags'][0] ?? 'prompt_injection');\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$reply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$reply,'blocked'=>true,'safety_flags'=>$guard['flags']]);\n }\n $t=self::currentTrial($u['id']);\n $system=self::strictAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"KNOWLEDGE_CONTEXT:\\n\\\"\\\"\\\"\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\\"\\\"\\\"\\n\\nUSER_MESSAGE:\\n\\\"\\\"\\\"\\n\".$in['message'].\"\\n\\\"\\\"\\\"\"]\n ];\n $res=DeepSeekService::chat($messages, ['temperature'=>0,'top_p'=>1]); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $json=json_decode($res['text'], true);\n $jsonGuard = AiGuardService::validateAiJson($json);\n if (!$jsonGuard['valid']) {\n $safe = AiGuardService::safeFallback($jsonGuard['flags'][0] ?? ($jsonGuard['reason'] ?? 'invalid_json'));\n } else {\n $safe = (string)$json['answer'];\n }\n $finalGuard = AiGuardService::validateFinalAnswer($safe);\n if (!$finalGuard['valid']) $safe = AiGuardService::safeFallback($finalGuard['flags'][0] ?? 'unsafe');\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe,'safety_flags'=>array_values(array_unique(array_merge($jsonGuard['flags'] ?? [], $finalGuard['flags'] ?? [], (array)($json['safety_flags'] ?? []))))]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function strictAiPrompt(): string {\n return <<<'PROMPT'\nأنت مساعد الطيبات داخل تطبيق الطيبات.\n\nهويتك:\n- أنت لست دكتور ضياء العوضي.\n- لا تدّعي أنك طبيب.\n- أنت مساعد مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء العوضي رحمه الله.\n- تتكلم بأدب ووضوح وبأسلوب عربي بسيط.\n\nمصادر معرفتك:\n- تعتمد فقط على قاعدة معرفة التطبيق.\n- تعتمد على قائمة المسموحات والممنوعات من قاعدة البيانات.\n- تعتمد على بيانات المستخدم المسجلة داخل التطبيق.\n- إذا لم تجد سببًا موثقًا في قاعدة المعرفة، لا تخترع سببًا.\n\nقواعد نظام الطيبات الأساسية:\n- كل عند الجوع.\n- اشرب عند العطش.\n- لا تسرف.\n- التزم بالمسموحات.\n- تجنب الممنوعات.\n- لا توجد وجبات ثابتة مفروضة.\n- الاقتراحات تكون حسب المتاح عند المستخدم وحسب قواعد النظام.\n\nقواعد صارمة لا يجوز كسرها:\n- ممنوع اقتراح أي صنف موجود في قائمة الممنوعات.\n- ممنوع اعتبار أي صنف ممنوع \"مسموح أحيانًا\" إلا إذا قاعدة البيانات تقول ذلك صراحة.\n- ممنوع اختراع أسباب منع غير موجودة في قاعدة المعرفة.\n- ممنوع إدخال أطعمة من خارج قاعدة المسموحات.\n- ممنوع تشخيص الأمراض.\n- ممنوع ادعاء علاج الأمراض.\n- ممنوع قول \"النظام يشفي\".\n- ممنوع أمر المستخدم بإيقاف أو تغيير أي دواء.\n- لو المستخدم سأل عن إيقاف علاج، قل: لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.\n\nطريقة الرد على الأكل الممنوع:\nإذا سأل المستخدم عن صنف ممنوع، قل:\n\"حسب قواعد نظام الطيبات، [الصنف] من الممنوعات. د/ضياء العوضي رحمه الله كان مانعه ضمن النظام، فالأفضل أثناء التجربة الالتزام بالمسموحات فقط.\"\n\nإذا كان سبب المنع موجودًا في قاعدة المعرفة، أضفه:\n\"والسبب المذكور في قاعدة معرفة النظام هو: [السبب].\"\n\nإذا لم يكن السبب موجودًا، قل:\n\"السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا.\"\n\nثم اقترح بدائل من المسموحات فقط.\n\nطريقة اقتراح الوجبات:\n- اقترح وجبة فقط من المسموحات.\n- لا تحدد كميات إلزامية.\n- استخدم عبارة: \"كل على قدر الجوع بدون إسراف.\"\n- إذا كانت المكونات المتاحة كلها ممنوعة، لا تقترح وجبة، واطلب من المستخدم اختيار مكونات من المسموحات.\n\nصيغة الإخراج:\nارجع JSON فقط بهذا الشكل:\n{\n \"answer\": \"النص النهائي للمستخدم\",\n \"mentioned_foods\": [],\n \"forbidden_foods_detected\": [],\n \"allowed_alternatives\": [],\n \"used_knowledge_ids\": [],\n \"safety_flags\": [],\n \"confidence\": \"high|medium|low\"\n}\nPROMPT;\n }\n private static function mealGenerationPrompt(): string {\n return self::strictAiPrompt().\"\\n\\nفي اقتراح الوجبات أرجع JSON فقط بالمفاتيح: meal_title, items, notes, warnings, is_allowed. items يجب أن تكون من available_ingredients فقط.\";\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "ebe8c0ba151c8daf85c90fb93d0fc7a178eb8890": {"t": 1785756553.06627, "n": 41389, "h": "de95be40c14dba0a", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'medications'=>self::all('SELECT * FROM user_medications WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY created_at DESC,id DESC');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type']);\n $mealType=$in['meal_type']; $status=$in['status'] ?? 'logged';\n if(!in_array($mealType,['hunger_meal','drink','snack','manual','morning','main'],true)) Response::json(false,'نوع التسجيل غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden','logged','suggested'],true)) Response::json(false,'حالة التسجيل غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $items = $in['meal_items'] ?? $in['items'] ?? [];\n if (is_string($items)) $items = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $items))));\n if (!is_array($items)) $items = [];\n $forbiddenHits = self::detectForbiddenFoods($items);\n if ($forbiddenHits && $status !== 'forbidden') {\n $status = 'forbidden';\n $in['forbidden_notes'] = 'تم رصد صنف ممنوع: '.implode('، ', $forbiddenHits);\n }\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,replacement_notes,forbidden_notes,user_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([\n $u['id'],$t['id'],$dayNumber,$mealType,$status,\n $in['meal_title'] ?? self::defaultMealTitle($mealType),\n json_encode(array_values($items), JSON_UNESCAPED_UNICODE),\n in_array(($in['generated_by'] ?? 'manual'), ['manual','ai','rule_based'], true) ? ($in['generated_by'] ?? 'manual') : 'manual',\n isset($in['hunger_level']) ? (int)$in['hunger_level'] : null,\n bool_int($in['thirst_logged'] ?? ($mealType === 'drink')),\n $in['replacement_notes']??null,\n $in['forbidden_notes']??null,\n $in['user_notes']??($in['notes']??null),\n now(),now()\n ]);\n $id=(int)self::pdo()->lastInsertId();\n Response::json(true,$status === 'forbidden' ? 'تم التسجيل مع تنبيه: يوجد صنف ممنوع' : 'تم تسجيل أكلك',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE id=?',[$id]), 'forbidden_foods'=>$forbiddenHits],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function knowledgeSearch(): void {\n Auth::user();\n $q = (string)($_GET['q'] ?? '');\n Response::json(true,'نتائج قاعدة المعرفة',['items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($q, 12))]);\n }\n public static function medications(): void {\n $u=Auth::user(); $pdo=self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM user_medications WHERE user_id=? ORDER BY currently_using DESC,id DESC');\n $s->execute([$u['id']]);\n Response::json(true,'أدويتك',['medications'=>$s->fetchAll(),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='INSERT INTO user_medications(user_id,medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([\n $u['id'], trim((string)$in['medication_name']), trim((string)$in['reason']),\n $in['dosage_note'] ?? null, bool_int($in['prescribed_by_doctor'] ?? 0),\n bool_int($in['currently_using'] ?? 1), bool_int($in['changed_during_trial'] ?? 0),\n $in['change_note'] ?? null, now(), now()\n ]);\n Response::json(true,'تم حفظ الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=?',[(int)$pdo->lastInsertId()]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.'],201);\n }\n public static function medicationUpdate(string $id): void {\n $u=Auth::user(); $in=json_input(); Validator::required($in,['medication_name','reason']);\n $sql='UPDATE user_medications SET medication_name=?,reason=?,dosage_note=?,prescribed_by_doctor=?,currently_using=?,changed_during_trial=?,change_note=?,updated_at=? WHERE id=? AND user_id=?';\n self::pdo()->prepare($sql)->execute([\n trim((string)$in['medication_name']), trim((string)$in['reason']), $in['dosage_note'] ?? null,\n bool_int($in['prescribed_by_doctor'] ?? 0), bool_int($in['currently_using'] ?? 1),\n bool_int($in['changed_during_trial'] ?? 0), $in['change_note'] ?? null, now(), (int)$id, $u['id']\n ]);\n Response::json(true,'تم تحديث الدواء',['medication'=>self::one('SELECT * FROM user_medications WHERE id=? AND user_id=?',[(int)$id,$u['id']]),'warning'=>'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب.']);\n }\n public static function generateMeal(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $in=json_input();\n $available=$in['available_ingredients'] ?? $in['ingredients'] ?? [];\n if (is_string($available)) $available = array_values(array_filter(array_map('trim', preg_split('/[,،\\n]+/u', $available))));\n if (!is_array($available)) $available = [];\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n $safeIngredients = array_values(array_filter($available, fn($x) => in_array($x, $allowed, true)));\n $forbiddenHits = self::detectForbiddenFoods($available);\n if (!$safeIngredients) $safeIngredients = array_slice($allowed, 0, 5);\n $fallback = self::ruleBasedMeal($safeIngredients, (int)($in['hunger_level'] ?? 0), $forbiddenHits);\n $key = Config::get('deepseek_api_key');\n if (!$key) Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n $context = self::aiContext($u, $t, (string)($in['prompt'] ?? ''));\n $messages=[\n ['role'=>'system','content'=>self::mealGenerationPrompt()],\n ['role'=>'user','content'=>json_encode([\n 'available_ingredients'=>$safeIngredients,\n 'hunger_level'=>$in['hunger_level'] ?? null,\n 'context'=>$context,\n 'required_json_keys'=>['meal_title','items','notes','warnings','is_allowed']\n ], JSON_UNESCAPED_UNICODE)]\n ];\n $res=DeepSeekService::chat($messages);\n $json=json_decode($res['text'], true);\n if (!is_array($json) || empty($json['items'])) {\n Response::json(true,'اقتراح وجبة من قواعد الطيبات',['meal'=>$fallback + ['generated_by'=>'rule_based']]);\n }\n $items = array_values(array_filter((array)$json['items'], fn($x) => is_string($x) && in_array($x, $allowed, true)));\n if (!$items) $items = $safeIngredients;\n $meal = [\n 'meal_title'=>(string)($json['meal_title'] ?? 'اقتراح من الطيبات'),\n 'items'=>$items,\n 'notes'=>(string)($json['notes'] ?? 'كل حسب الجوع والشبع وبدون إسراف.'),\n 'warnings'=>array_values(array_merge((array)($json['warnings'] ?? []), $forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [])),\n 'is_allowed'=>true,\n 'generated_by'=>'ai',\n ];\n Response::json(true,'اقتراح وجبة ذكي من الطيبات',['meal'=>$meal]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt') ?: self::defaultAiPrompt();\n $context = self::aiContext($u, $t, (string)$in['message']);\n $medReply = self::medicationSafetyReply((string)$in['message']);\n if ($medReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$medReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$medReply]);\n }\n $ruleReply = self::forbiddenFoodRuleReply((string)$in['message'], $context['forbidden_foods'] ?? [], $context['knowledge_items'] ?? [], $context['allowed_foods'] ?? []);\n if ($ruleReply) {\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n self::pdo()->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$ruleReply,0,now()]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$ruleReply]);\n }\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم وقاعدة معرفة الطيبات:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $safe = self::safeAiReply($res['text']);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function aiContext(array $u, ?array $t, string $message=''): array {\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden');\n return [\n 'persona'=>'مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه.',\n 'system_rules'=>[\n 'كل عندما تجوع',\n 'اشرب عندما تعطش',\n 'لا تسرف',\n 'التزم بالمسموح وتجنب الممنوع',\n 'لا توجد مواعيد وجبات ثابتة',\n 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب',\n ],\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$u['id']]),\n 'current_trial'=>$t,\n 'latest_meal_logs'=>self::all('SELECT day_number,meal_type,status,meal_title,meal_items,generated_by,hunger_level,thirst_logged,forbidden_notes,user_notes,created_at FROM meal_logs WHERE user_id=? ORDER BY id DESC LIMIT 8',[$u['id']]),\n 'latest_measurements'=>self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5',[$u['id']]),\n 'latest_checkins'=>self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms,notes FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]),\n 'medications'=>self::all('SELECT medication_name,reason,dosage_note,prescribed_by_doctor,currently_using,changed_during_trial,change_note FROM user_medications WHERE user_id=? ORDER BY id DESC',[$u['id']]),\n 'allowed_foods'=>$allowed,\n 'forbidden_foods'=>$forbidden,\n 'knowledge_items'=>KnowledgeService::formatForAiContext(KnowledgeService::searchRelevantItems($message, 12)),\n ];\n }\n private static function knowledgeForMessage(string $message): array {\n $words = array_values(array_filter(preg_split('/\\s+/u', trim($message))));\n $sql='SELECT category,title,content,related_food FROM system_knowledge_items WHERE is_active=1';\n $items=self::all($sql,[]);\n if (!$message) return array_slice($items,0,12);\n $matched=[];\n foreach($items as $item){\n $hay=mb_strtolower(($item['title'] ?? '').' '.($item['content'] ?? '').' '.($item['related_food'] ?? ''));\n foreach($words as $w){\n $w=mb_strtolower(trim($w, \"؟?،,. \"));\n if(mb_strlen($w) >= 3 && mb_stripos($hay,$w) !== false){ $matched[]=$item; break; }\n }\n }\n return array_slice($matched ?: $items,0,12);\n }\n private static function forbiddenFoodRuleReply(string $message, array $forbidden, array $knowledge, array $allowed): ?string {\n foreach($forbidden as $food){\n $needle = preg_replace('/^ال/u', '', (string)$food);\n if($food && (mb_stripos($message, $food) !== false || ($needle && mb_stripos($message, $needle) !== false))){\n $reason=null;\n foreach(KnowledgeService::getRulesForFood($food) as $k){\n if(($k['category'] ?? '') === 'reason') { $reason=$k['content']; break; }\n }\n $alternatives=array_slice(array_values($allowed),0,3);\n $reply='حسب قواعد نظام الطيبات، '.$food.' من الممنوعات. د/ضياء الله يرحمه كان مانع هذا الصنف ضمن النظام، فالأفضل أثناء التجربة تلتزم بالمسموحات فقط.';\n if($alternatives) $reply.=' تقدر تختار بديل من المسموحات مثل '.implode(' أو ', $alternatives).' حسب المتاح.';\n $reply.=$reason ? ' والسبب المذكور في قاعدة معرفة النظام هو: '.$reason : ' السبب التفصيلي غير مضاف في قاعدة معرفة التطبيق حاليًا، لكن هذا الصنف مصنف كممنوع داخل قواعد النظام.';\n return $reply;\n }\n }\n return null;\n }\n private static function medicationSafetyReply(string $message): ?string {\n foreach(['أوقف','اوقف','وقف','أغيّر','اغير','غير','جرعة','علاج','دواء','أدوية','ادوية'] as $needle){\n if(mb_stripos($message,$needle) !== false && preg_match('/دواء|أدوية|ادوية|علاج|جرعة|ضغط|سكر|كوليسترول/u', $message)){\n return 'لا توقف أو تغير أي دواء إلا تحت متابعة طبيب. يمكنك تسجيل أدويتك وقياساتك أثناء تجربة الطيبات لملاحظة التغيرات، لكن مساعد الطيبات لا يقدم قرارًا بإيقاف علاج أو تغيير جرعة.';\n }\n }\n return null;\n }\n private static function defaultAiPrompt(): string {\n return 'أنت مساعد الطيبات، مبني على قواعد نظام الطيبات المنسوبة للدكتور ضياء الله يرحمه. لا تنتحل شخصية الطبيب. لا تقدم تشخيصًا أو ادعاءات علاج أو نصائح إيقاف أدوية. القاعدة: كل عندما تجوع، واشرب عندما تعطش، ولا تسرف، والتزم بالمسموح وتجنب الممنوع.';\n }\n private static function mealGenerationPrompt(): string {\n return 'أنت مساعد توليد وجبات لنظام الطيبات. أعد JSON فقط بالمفاتيح meal_title و items و notes و warnings و is_allowed. اقترح فقط من المسموحات المتاحة. لا تستخدم الممنوعات. لا تذكر سعرات أو مواعيد وجبات أو كميات علاجية. استخدم عبارات مثل بدون إسراف وحسب الجوع والشبع. لا تقدم أي ادعاء علاجي.';\n }\n private static function ruleBasedMeal(array $safeIngredients, int $hungerLevel=0, array $forbiddenHits=[]): array {\n $items=array_slice(array_values($safeIngredients),0,4);\n return [\n 'meal_title'=>$hungerLevel >= 7 ? 'وجبة مشبعة من الطيبات' : 'اقتراح خفيف من الطيبات',\n 'items'=>$items,\n 'notes'=>'كل من هذه المسموحات حسب الجوع والشبع وبدون إسراف. لا توجد مواعيد ثابتة للوجبات.',\n 'warnings'=>$forbiddenHits ? ['تم تجاهل أصناف ممنوعة: '.implode('، ', $forbiddenHits)] : [],\n 'is_allowed'=>true,\n ];\n }\n private static function detectForbiddenFoods(array $items): array {\n $forbidden=self::names('forbidden'); $hits=[];\n foreach($items as $item){\n foreach($forbidden as $bad){\n $needle = preg_replace('/^ال/u', '', (string)$bad);\n if($bad && (mb_stripos((string)$item, $bad) !== false || ($needle && mb_stripos((string)$item, $needle) !== false))) $hits[$bad]=$bad;\n }\n }\n return array_values($hits);\n }\n private static function defaultMealTitle(string $type): string {\n return [\n 'hunger_meal'=>'وجبة عند الجوع',\n 'drink'=>'مشروب عند العطش',\n 'manual'=>'تسجيل يدوي',\n 'snack'=>'سناك',\n 'morning'=>'وجبة صباحية',\n 'main'=>'وجبة رئيسية',\n ][$type] ?? 'تسجيل أكل';\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','غيّر الجرعة','غير الجرعة','أنا دكتور ضياء','أنا الطبيب الخاص بك','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','change dosage','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو تغيير الجرعات أو ادعاء العلاج. مساعد الطيبات ليس طبيبك ولا ينتحل شخصية د/ضياء الله يرحمه. بناءً على البيانات التي سجلتها، يمكنني مساعدتك بملاحظات غذائية عامة من المسموحات فقط. إذا كنت تتناول أدوية مزمنة، لا توقفها أو تغير جرعتها إلا تحت متابعة طبيب.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "792b47241856a1eb038ca0157059289922eff682": {"t": 1785756555.5068069, "n": 27868, "h": "87e19b4d99415fb1", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function register(): void {\n $in = json_input(); Validator::required($in, ['name','email','password']);\n $name = trim((string)$in['name']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n if (mb_strlen($name) < 2) Response::json(false, 'الاسم قصير جدا', [], 400);\n if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::json(false, 'البريد غير صحيح', [], 400);\n if (strlen($password) < 6) Response::json(false, 'كلمة المرور يجب ألا تقل عن 6 أحرف', [], 400);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $existing = $stmt->fetch();\n if ($existing && !empty($existing['password_hash'])) Response::json(false, 'هذا البريد مسجل بالفعل', [], 409);\n $hash = password_hash($password, PASSWORD_DEFAULT);\n if ($existing) {\n $pdo->prepare('UPDATE users SET name=?, password_hash=?, is_active=1, deleted_at=NULL, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $hash, now(), now(), $existing['id']]);\n $id = (int)$existing['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(name,email,password_hash,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$name, $email, $hash, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم إنشاء الحساب بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)], 201);\n }\n public static function passwordLogin(): void {\n $in = json_input(); Validator::required($in, ['email','password']);\n $email = strtolower(trim((string)$in['email']));\n $password = (string)$in['password'];\n $stmt = self::pdo()->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $user = $stmt->fetch();\n if (!$user || empty($user['password_hash']) || !password_verify($password, $user['password_hash'])) {\n Response::json(false, 'بيانات الدخول غير صحيحة', [], 401);\n }\n if (!empty($user['deleted_at']) || !(int)($user['is_active'] ?? 1)) Response::json(false, 'هذا الحساب غير نشط', [], 401);\n self::pdo()->prepare('UPDATE users SET last_login_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $user['id']]);\n $user = self::userById((int)$user['id']);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => (int)$user['id'], 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY FIELD(meal_type,\"morning\",\"main\",\"snack\")');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type','status']);\n $mealType=$in['meal_type']; $status=$in['status'];\n if(!in_array($mealType,['morning','main','snack'],true)) Response::json(false,'نوع الوجبة غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden'],true)) Response::json(false,'حالة الوجبة غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,replacement_notes,forbidden_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE status=VALUES(status),replacement_notes=VALUES(replacement_notes),forbidden_notes=VALUES(forbidden_notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$dayNumber,$mealType,$status,$in['replacement_notes']??null,$in['forbidden_notes']??null,now(),now()]);\n Response::json(true,'تم حفظ حالة الوجبة',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? AND meal_type=?',[$u['id'],$t['id'],$dayNumber,$mealType])],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $checkins = self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]);\n $measurements = self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5', [$u['id']]);\n $context = [\n 'current_day' => $t['current_day'] ?? null,\n 'latest_checkins' => $checkins,\n 'latest_measurements' => $measurements,\n 'allowed_foods' => $allowed,\n 'forbidden_foods' => $forbidden,\n ];\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $safe = self::safeAiReply($res['text']);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو ادعاء العلاج. بناءً على البيانات التي سجلتها، يمكنني مساعدتك فقط بملاحظات غذائية عامة من المسموحات، ومع الأعراض القوية أوقف التجربة مؤقتًا واستشر طبيبًا.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "de98d3e36afb97b3358f1e15084e5cf39544b7ee": {"t": 1785756557.0173872, "n": 24915, "h": "fcf523edca1cc96a", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array {\n $allowed = [\n 'id',\n 'name',\n 'email',\n 'photo_url',\n 'plan',\n 'is_premium',\n 'premium_expires_at',\n 'ads_enabled',\n 'ai_chat_enabled',\n 'ai_insights_enabled',\n 'disclaimer_accepted_at',\n ];\n return array_intersect_key($u, array_flip($allowed));\n }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY FIELD(meal_type,\"morning\",\"main\",\"snack\")');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type','status']);\n $mealType=$in['meal_type']; $status=$in['status'];\n if(!in_array($mealType,['morning','main','snack'],true)) Response::json(false,'نوع الوجبة غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden'],true)) Response::json(false,'حالة الوجبة غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,replacement_notes,forbidden_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE status=VALUES(status),replacement_notes=VALUES(replacement_notes),forbidden_notes=VALUES(forbidden_notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$dayNumber,$mealType,$status,$in['replacement_notes']??null,$in['forbidden_notes']??null,now(),now()]);\n Response::json(true,'تم حفظ حالة الوجبة',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? AND meal_type=?',[$u['id'],$t['id'],$dayNumber,$mealType])],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $checkins = self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]);\n $measurements = self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5', [$u['id']]);\n $context = [\n 'current_day' => $t['current_day'] ?? null,\n 'latest_checkins' => $checkins,\n 'latest_measurements' => $measurements,\n 'allowed_foods' => $allowed,\n 'forbidden_foods' => $forbidden,\n ];\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $safe = self::safeAiReply($res['text']);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','شفيت','تم علاجك','يعالج المرض','يشفي المرض','stop medication','discontinue medicine','cured','cure disease','cured your diabetes'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو ادعاء العلاج. بناءً على البيانات التي سجلتها، يمكنني مساعدتك فقط بملاحظات غذائية عامة من المسموحات، ومع الأعراض القوية أوقف التجربة مؤقتًا واستشر طبيبًا.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "53875908d4f4db0b7383e84c661b52df4569fd6a": {"t": 1785756559.5673716, "n": 24471, "h": "0af5f6d501307030", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array { unset($u['google_id']); return $u; }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id, 'token_version' => (int)($user['token_version'] ?? 1)]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function publicSettings(): void {\n $keys=['disclaimer_text','emergency_warning_text','privacy_policy','terms_of_use','delete_account_instructions','data_export_instructions','premium_monthly_price','premium_yearly_price','admob_banner_android','ai_chat_enabled_global'];\n $in=str_repeat('?,', count($keys)-1).'?';\n $s=self::pdo()->prepare(\"SELECT setting_key,setting_value FROM app_settings WHERE setting_key IN ($in)\");\n $s->execute($keys);\n $settings=[];\n foreach($s->fetchAll() as $r) $settings[$r['setting_key']]=$r['setting_value'];\n Response::json(true,'إعدادات التطبيق العامة',['settings'=>$settings]);\n }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function deleteAccount(): void {\n $u=Auth::user();\n $email='deleted_user_'.$u['id'].'@deleted.local';\n self::pdo()->prepare('UPDATE users SET email=?, google_id=NULL, photo_url=NULL, is_active=0, deleted_at=?, token_version=token_version+1, updated_at=? WHERE id=?')->execute([$email, now(), now(), $u['id']]);\n Response::json(true,'تم حذف الحساب وتعطيل الوصول بنجاح');\n }\n public static function exportData(): void {\n $u=Auth::user(); $id=$u['id'];\n $data=[\n 'user'=>self::cleanUser($u),\n 'health_profile'=>self::one('SELECT * FROM health_profiles WHERE user_id=?',[$id]),\n 'trials'=>self::all('SELECT * FROM trials WHERE user_id=?',[$id]),\n 'baseline_measurements'=>self::all('SELECT * FROM baseline_measurements WHERE user_id=?',[$id]),\n 'meal_logs'=>self::all('SELECT * FROM meal_logs WHERE user_id=?',[$id]),\n 'daily_checkins'=>self::all('SELECT * FROM daily_checkins WHERE user_id=?',[$id]),\n 'measurements'=>self::all('SELECT * FROM measurements WHERE user_id=?',[$id]),\n 'reports'=>self::all('SELECT * FROM reports WHERE user_id=?',[$id]),\n 'subscriptions'=>self::all('SELECT * FROM subscriptions WHERE user_id=?',[$id]),\n 'ai_messages'=>self::all('SELECT id,role,message,tokens_used,created_at FROM ai_messages WHERE user_id=?',[$id]),\n ];\n Response::json(true,'تم تجهيز بياناتك للتصدير',$data);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY FIELD(meal_type,\"morning\",\"main\",\"snack\")');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type','status']);\n $mealType=$in['meal_type']; $status=$in['status'];\n if(!in_array($mealType,['morning','main','snack'],true)) Response::json(false,'نوع الوجبة غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden'],true)) Response::json(false,'حالة الوجبة غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,replacement_notes,forbidden_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE status=VALUES(status),replacement_notes=VALUES(replacement_notes),forbidden_notes=VALUES(forbidden_notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$dayNumber,$mealType,$status,$in['replacement_notes']??null,$in['forbidden_notes']??null,now(),now()]);\n Response::json(true,'تم حفظ حالة الوجبة',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? AND meal_type=?',[$u['id'],$t['id'],$dayNumber,$mealType])],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if (self::setting('ai_chat_enabled_global') === '0') Response::json(false,'مساعد الطيبات متوقف مؤقتًا من إدارة التطبيق',[],403);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n if (trim($in['message']) === '') Response::json(false,'اكتب رسالة أولًا',[],400);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $checkins = self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]);\n $measurements = self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5', [$u['id']]);\n $context = [\n 'current_day' => $t['current_day'] ?? null,\n 'latest_checkins' => $checkins,\n 'latest_measurements' => $measurements,\n 'allowed_foods' => $allowed,\n 'forbidden_foods' => $forbidden,\n ];\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $safe = self::safeAiReply($res['text']);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$safe,$res['tokens'],now()]);\n if ($res['tokens'] > 0) $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$safe]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user(); EntitlementService::syncUserEntitlements((int)$u['id']); $u=self::userById((int)$u['id']);\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function safeAiReply(string $text): string {\n foreach (['أوقف الدواء','وقف الدواء','اترك الدواء','شفيت','تم علاجك','يعالج المرض','يشفي المرض'] as $bad) {\n if (mb_stripos($text, $bad) !== false) return 'لا أستطيع تقديم نصيحة تتعلق بإيقاف الأدوية أو ادعاء العلاج. بناءً على البيانات التي سجلتها، يمكنني مساعدتك فقط بملاحظات غذائية عامة من المسموحات، ومع الأعراض القوية أوقف التجربة مؤقتًا واستشر طبيبًا.';\n }\n return $text;\n }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "f2f03e77f16686bc5bc08df45d3f222a66350ddf": {"t": 1785756561.128005, "n": 21321, "h": "eb57850010b004ea", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array { unset($u['google_id']); return $u; }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n private static function requireLocal(): void {\n if (Config::get('app_env') !== 'local') Response::json(false, 'هذا المسار متاح في بيئة التطوير فقط', [], 403);\n }\n public static function health(): void {\n Response::json(true, 'الخدمة تعمل', ['app' => 'الطيبات']);\n }\n public static function devLogin(): void {\n self::requireLocal();\n $in = json_input();\n $email = $in['email'] ?? ('dev+' . date('YmdHis') . '@altayyibat.local');\n $name = $in['name'] ?? 'مستخدم تجربة';\n $googleId = 'dev-' . sha1($email);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE email=? LIMIT 1');\n $stmt->execute([$email]);\n $u = $stmt->fetch();\n if ($u) {\n $pdo->prepare('UPDATE users SET name=?, google_id=?, last_login_at=?, updated_at=? WHERE id=?')->execute([$name, $googleId, now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$googleId, $name, $email, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل دخول التطوير', ['token' => Jwt::encode(['sub' => $id]), 'user' => self::cleanUser($user)]);\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']);\n if(!$t && $_SERVER['REQUEST_METHOD'] !== 'GET') {\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n $t=self::currentTrial($u['id']);\n }\n if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function mealLogs(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $targetDay = $day ?: $t['current_day'];\n $s=self::pdo()->prepare('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? ORDER BY FIELD(meal_type,\"morning\",\"main\",\"snack\")');\n $s->execute([$u['id'],$t['id'],(int)$targetDay]);\n Response::json(true,'سجلات الوجبات',['meal_logs'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['meal_type','status']);\n $mealType=$in['meal_type']; $status=$in['status'];\n if(!in_array($mealType,['morning','main','snack'],true)) Response::json(false,'نوع الوجبة غير صحيح',[],400);\n if(!in_array($status,['eaten','partial','skipped','replaced','forbidden'],true)) Response::json(false,'حالة الوجبة غير صحيحة',[],400);\n $dayNumber=(int)($in['day_number'] ?? $t['current_day']);\n $sql='INSERT INTO meal_logs(user_id,trial_id,day_number,meal_type,status,replacement_notes,forbidden_notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE status=VALUES(status),replacement_notes=VALUES(replacement_notes),forbidden_notes=VALUES(forbidden_notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$dayNumber,$mealType,$status,$in['replacement_notes']??null,$in['forbidden_notes']??null,now(),now()]);\n Response::json(true,'تم حفظ حالة الوجبة',['meal_log'=>self::one('SELECT * FROM meal_logs WHERE user_id=? AND trial_id=? AND day_number=? AND meal_type=?',[$u['id'],$t['id'],$dayNumber,$mealType])],201);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $checkins = self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]);\n $measurements = self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5', [$u['id']]);\n $context = [\n 'current_day' => $t['current_day'] ?? null,\n 'latest_checkins' => $checkins,\n 'latest_measurements' => $measurements,\n 'allowed_foods' => $allowed,\n 'forbidden_foods' => $forbidden,\n ];\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$res['text'],$res['tokens'],now()]);\n $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$res['text']]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user();\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n self::requireLocal();\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "3e95778202dbb8d991b3ee6a9f739bdfcc09debf": {"t": 1785756562.4309235, "n": 17548, "h": "c221a8e31b2078ea", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array { unset($u['google_id']); return $u; }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $checkins = self::all('SELECT day_number,commitment,energy,sleep,digestion,mood,hunger,symptoms FROM daily_checkins WHERE user_id=? ORDER BY day_number DESC LIMIT 5', [$u['id']]);\n $measurements = self::all('SELECT measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse FROM measurements WHERE user_id=? ORDER BY measurement_date DESC,id DESC LIMIT 5', [$u['id']]);\n $context = [\n 'current_day' => $t['current_day'] ?? null,\n 'latest_checkins' => $checkins,\n 'latest_measurements' => $measurements,\n 'allowed_foods' => $allowed,\n 'forbidden_foods' => $forbidden,\n ];\n $messages=[\n ['role'=>'system','content'=>$system],\n ['role'=>'user','content'=>\"سياق مختصر من بيانات المستخدم:\\n\".json_encode($context, JSON_UNESCAPED_UNICODE).\"\\n\\nرسالة المستخدم: \".$in['message']]\n ];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$res['text'],$res['tokens'],now()]);\n $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$res['text']]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user();\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n if($action==='activate-dev') {\n $expires = date('Y-m-d H:i:s',strtotime('+30 days'));\n EntitlementService::apply($u['id'],'premium_monthly',$expires);\n self::pdo()->prepare(\"INSERT INTO subscriptions(user_id,plan,provider,status,started_at,expires_at,renewal_status,transaction_id,created_at,updated_at) VALUES(?,'premium_monthly','dev','active',?,?,?,?,?,?)\")->execute([$u['id'], now(), $expires, 'manual', 'dev-'.$u['id'].'-'.time(), now(), now()]);\n }\n if($action==='cancel-dev') {\n EntitlementService::apply($u['id'],'free',null);\n self::pdo()->prepare(\"UPDATE subscriptions SET status='cancelled', updated_at=? WHERE user_id=? AND status='active'\")->execute([now(), $u['id']]);\n }\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function all(string $sql,array $p): array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetchAll(); }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}, "139960702fe4091d49aeb5411e0fb5856ae7e183": {"t": 1785756563.8524513, "n": 16205, "h": "cdfc51d6c7517547", "c": "<?php\nclass ApiController {\n private static function pdo(): PDO { return Database::pdo(); }\n private static function cleanUser(array $u): array { unset($u['google_id']); return $u; }\n private static function currentTrial(int $userId): ?array {\n $s = self::pdo()->prepare(\"SELECT * FROM trials WHERE user_id=? AND status IN ('active','paused') ORDER BY id DESC LIMIT 1\");\n $s->execute([$userId]); return $s->fetch() ?: null;\n }\n public static function googleLogin(): void {\n $in = json_input(); Validator::required($in, ['id_token']);\n $g = GoogleAuthService::verifyIdToken($in['id_token']);\n $pdo = self::pdo();\n $stmt = $pdo->prepare('SELECT * FROM users WHERE google_id=? OR email=? LIMIT 1');\n $stmt->execute([$g['sub'], $g['email'] ?? '']);\n $u = $stmt->fetch();\n if ($u) {\n $up = $pdo->prepare('UPDATE users SET google_id=?, name=?, photo_url=?, last_login_at=?, updated_at=? WHERE id=?');\n $up->execute([$g['sub'], $g['name'] ?? $u['name'], $g['picture'] ?? $u['photo_url'], now(), now(), $u['id']]);\n $id = (int)$u['id'];\n } else {\n $ins = $pdo->prepare(\"INSERT INTO users(google_id,name,email,photo_url,plan,is_premium,ads_enabled,ai_chat_enabled,ai_insights_enabled,daily_ai_chat_limit,ai_chat_used_today,ai_chat_last_reset_at,created_at,updated_at,last_login_at) VALUES(?,?,?,?, 'free',0,1,0,1,0,0,?,?,?,?)\");\n $ins->execute([$g['sub'], $g['name'] ?? 'مستخدم الطيبات', $g['email'] ?? null, $g['picture'] ?? null, now(), now(), now(), now()]);\n $id = (int)$pdo->lastInsertId();\n }\n $user = self::userById($id);\n Response::json(true, 'تم تسجيل الدخول بنجاح', ['token' => Jwt::encode(['sub' => $id]), 'user' => self::cleanUser($user)]);\n }\n public static function me(): void { Response::json(true, 'بيانات المستخدم', ['user' => self::cleanUser(Auth::user())]); }\n public static function acceptDisclaimer(): void {\n $u = Auth::user(); self::pdo()->prepare('UPDATE users SET disclaimer_accepted_at=?, updated_at=? WHERE id=?')->execute([now(), now(), $u['id']]);\n Response::json(true, 'تم قبول التنبيه الطبي');\n }\n public static function profile(): void {\n $u = Auth::user();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') Response::json(true, 'الملف الشخصي', ['user' => self::cleanUser($u)]);\n $in = json_input();\n self::pdo()->prepare('UPDATE users SET name=?, updated_at=? WHERE id=?')->execute([$in['name'] ?? $u['name'], now(), $u['id']]);\n Response::json(true, 'تم تحديث الملف الشخصي', ['user' => self::cleanUser(self::userById($u['id']))]);\n }\n public static function healthProfile(): void {\n $u = Auth::user(); $pdo = self::pdo();\n if ($_SERVER['REQUEST_METHOD'] === 'GET') {\n $s=$pdo->prepare('SELECT * FROM health_profiles WHERE user_id=?'); $s->execute([$u['id']]);\n Response::json(true, 'الملف الصحي', ['profile'=>$s->fetch()]);\n }\n $in = json_input(); Validator::required($in, ['age','gender','height','weight','goal']);\n $cols = ['age','gender','height','weight','goal','has_diabetes','has_hypertension','has_cholesterol','has_kidney_disease','has_liver_disease','has_heart_disease','has_gout','has_ibs','pregnant','breastfeeding','takes_medications'];\n $exists = self::one('SELECT id FROM health_profiles WHERE user_id=?', [$u['id']]);\n $vals = []; foreach ($cols as $c) $vals[$c] = in_array($c, ['gender','goal'], true) ? ($in[$c] ?? '') : (is_bool($in[$c] ?? null) ? bool_int($in[$c]) : ($in[$c] ?? 0));\n $allergies = json_encode($in['food_allergies'] ?? [], JSON_UNESCAPED_UNICODE);\n if ($exists) {\n $sql='UPDATE health_profiles SET age=?,gender=?,height=?,weight=?,goal=?,has_diabetes=?,has_hypertension=?,has_cholesterol=?,has_kidney_disease=?,has_liver_disease=?,has_heart_disease=?,has_gout=?,has_ibs=?,pregnant=?,breastfeeding=?,takes_medications=?,food_allergies=?,updated_at=? WHERE user_id=?';\n $pdo->prepare($sql)->execute([...array_values($vals), $allergies, now(), $u['id']]);\n } else {\n $sql='INSERT INTO health_profiles(user_id,age,gender,height,weight,goal,has_diabetes,has_hypertension,has_cholesterol,has_kidney_disease,has_liver_disease,has_heart_disease,has_gout,has_ibs,pregnant,breastfeeding,takes_medications,food_allergies,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n $pdo->prepare($sql)->execute([$u['id'], ...array_values($vals), $allergies, now(), now()]);\n }\n Response::json(true, 'تم حفظ الملف الصحي', ['warning' => self::needsDoctorWarning($in)]);\n }\n public static function startTrial(): void {\n $u=Auth::user(); $existing=self::currentTrial($u['id']);\n if ($existing) Response::json(true, 'لديك تجربة قائمة بالفعل', ['trial'=>$existing]);\n $start=date('Y-m-d'); $end=date('Y-m-d', strtotime('+13 days'));\n self::pdo()->prepare(\"INSERT INTO trials(user_id,status,start_date,end_date,current_day,completion_rate,created_at,updated_at) VALUES(?,'active',?,?,1,0,?,?)\")->execute([$u['id'],$start,$end,now(),now()]);\n Response::json(true, 'بدأت تجربة 14 يوم', ['trial'=>self::currentTrial($u['id'])], 201);\n }\n public static function trialAction(string $action): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $map=['pause'=>[\"paused\",\"paused_at\"],'resume'=>[\"active\",null],'stop'=>[\"stopped\",\"stopped_at\"],'complete'=>[\"completed\",\"completed_at\"]];\n [$status,$field]=$map[$action]; $sql='UPDATE trials SET status=?, updated_at=?'.($field?\", $field=?\":'').' WHERE id=?';\n $params=$field?[$status,now(),now(),$t['id']]:[$status,now(),$t['id']]; self::pdo()->prepare($sql)->execute($params);\n Response::json(true, 'تم تحديث حالة التجربة');\n }\n public static function currentTrialEndpoint(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n $day=max(1,min(14,(int)floor((strtotime(date('Y-m-d'))-strtotime($t['start_date']))/86400)+1));\n self::pdo()->prepare('UPDATE trials SET current_day=? WHERE id=?')->execute([$day,$t['id']]); $t['current_day']=$day;\n Response::json(true,'التجربة الحالية',['trial'=>$t]);\n }\n public static function baseline(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'ابدأ التجربة أولًا',[],400);\n if ($_SERVER['REQUEST_METHOD']==='GET') Response::json(true,'قياسات البداية',['baseline'=>self::one('SELECT * FROM baseline_measurements WHERE user_id=? AND trial_id=?',[$u['id'],$t['id']])]);\n $in=json_input(); Validator::required($in,['weight','energy','sleep','digestion','mood','hunger']);\n $sql='INSERT INTO baseline_measurements(user_id,trial_id,weight,waist,systolic,diastolic,fasting_glucose,post_meal_glucose,pulse,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['weight'],$in['waist']??null,$in['systolic']??null,$in['diastolic']??null,$in['fasting_glucose']??null,$in['post_meal_glucose']??null,$in['pulse']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now()]);\n Response::json(true,'تم حفظ قياسات البداية');\n }\n public static function mealPlan(?string $day=null): void {\n $u=Auth::user(); if(!$day){$t=self::currentTrial($u['id']); $day=$t['current_day']??1;}\n $s=self::pdo()->prepare('SELECT * FROM meal_plans WHERE day_number=? AND is_active=1'); $s->execute([(int)$day]); $p=$s->fetch();\n foreach(['morning_meal','main_meal','snack_meal'] as $k) if($p) $p[$k]=json_decode($p[$k],true);\n Response::json((bool)$p, $p?'خطة الوجبات':'اليوم غير موجود', ['meal_plan'=>$p], $p?200:404);\n }\n public static function foods(string $type): void {\n Auth::user(); $s=self::pdo()->prepare('SELECT f.*, c.name_ar category_name FROM food_items f LEFT JOIN food_categories c ON c.id=f.category_id WHERE f.type=? AND f.is_active=1 ORDER BY c.sort_order,f.sort_order'); $s->execute([$type]);\n Response::json(true, $type==='allowed'?'المسموحات':'الممنوعات', ['items'=>$s->fetchAll()]);\n }\n public static function checkins(?string $day=null): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM daily_checkins WHERE user_id=? AND trial_id=?'; $p=[$u['id'],$t['id']];\n if($day){$sql.=' AND day_number=?';$p[]=(int)$day;} $sql.=' ORDER BY day_number';\n $s=self::pdo()->prepare($sql); $s->execute($p); Response::json(true,'سجلات اليوم',['checkins'=>$s->fetchAll()]);\n }\n $in=json_input(); Validator::required($in,['day_number','commitment','energy','sleep','digestion','mood','hunger']);\n $danger=InsightService::dangerous($in);\n $sql='INSERT INTO daily_checkins(user_id,trial_id,day_number,checkin_date,commitment,ate_morning_meal,ate_main_meal,ate_snack,ate_forbidden_food,forbidden_food_notes,energy,sleep,digestion,mood,hunger,symptoms,notes,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE commitment=VALUES(commitment),ate_morning_meal=VALUES(ate_morning_meal),ate_main_meal=VALUES(ate_main_meal),ate_snack=VALUES(ate_snack),ate_forbidden_food=VALUES(ate_forbidden_food),forbidden_food_notes=VALUES(forbidden_food_notes),energy=VALUES(energy),sleep=VALUES(sleep),digestion=VALUES(digestion),mood=VALUES(mood),hunger=VALUES(hunger),symptoms=VALUES(symptoms),notes=VALUES(notes),updated_at=VALUES(updated_at)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['day_number'],$in['checkin_date']??date('Y-m-d'),$in['commitment'],bool_int($in['ate_morning_meal']??0),bool_int($in['ate_main_meal']??0),bool_int($in['ate_snack']??0),bool_int($in['ate_forbidden_food']??0),$in['forbidden_food_notes']??null,$in['energy'],$in['sleep'],$in['digestion'],$in['mood'],$in['hunger'],json_encode($in['symptoms']??[],JSON_UNESCAPED_UNICODE),$in['notes']??null,now(),now()]);\n Response::json(true,'تم حفظ سجل اليوم',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function measurements(bool $latest=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة حالية',[],404);\n if ($_SERVER['REQUEST_METHOD']==='GET') {\n $sql='SELECT * FROM measurements WHERE user_id=? AND trial_id=? ORDER BY measurement_date DESC, id DESC'.($latest?' LIMIT 1':'');\n $s=self::pdo()->prepare($sql); $s->execute([$u['id'],$t['id']]); Response::json(true,'القياسات',[$latest?'measurement':'measurements'=>$latest?($s->fetch() ?: null):$s->fetchAll()]);\n }\n $in=json_input(); $danger=InsightService::dangerous($in);\n $sql='INSERT INTO measurements(user_id,trial_id,measurement_date,weight,systolic,diastolic,glucose,glucose_type,pulse,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)';\n self::pdo()->prepare($sql)->execute([$u['id'],$t['id'],$in['date']??date('Y-m-d'),$in['weight']??null,$in['systolic']??null,$in['diastolic']??null,$in['glucose']??null,$in['glucose_type']??null,$in['pulse']??null,$in['notes']??null,now()]);\n Response::json(true,'تم حفظ القياس',['warning'=>$danger,'warning_text'=>$danger?'الأعراض أو القياسات التي سجلتها تحتاج انتباه. أوقف التجربة مؤقتًا واستشر طبيبًا، خاصة إذا كانت الأعراض شديدة أو متكررة.':null]);\n }\n public static function report(string $type, bool $generate=false): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']) ?: self::one('SELECT * FROM trials WHERE user_id=? ORDER BY id DESC LIMIT 1',[$u['id']]); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n if($generate) Response::json(true,'تم توليد التقرير',['report'=>ReportService::generate($u['id'],$t['id'],$type)]);\n $rt=$type==='week1'?'week1':'final14'; $r=self::one('SELECT * FROM reports WHERE user_id=? AND trial_id=? AND report_type=? ORDER BY id DESC LIMIT 1',[$u['id'],$t['id'],$rt]);\n Response::json((bool)$r,$r?'التقرير':'لم يتم توليد التقرير بعد',['report'=>$r],$r?200:404);\n }\n public static function insights(): void {\n $u=Auth::user(); $t=self::currentTrial($u['id']); if(!$t) Response::json(false,'لا توجد تجربة',[],404);\n $cards = InsightService::cards($u['id'], $t['id']); Response::json(true,'AI Insights',['insights'=>$cards]);\n }\n public static function aiUsage(): void { $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Response::json(true,'استخدام الذكاء',['used'=>(int)$u['ai_chat_used_today'],'limit'=>(int)$u['daily_ai_chat_limit']]); }\n public static function aiChat(): void {\n $u=Auth::user(); EntitlementService::resetAiIfNeeded($u); $u=self::userById($u['id']); Auth::requirePremium($u);\n if((int)$u['ai_chat_used_today'] >= (int)$u['daily_ai_chat_limit']) Response::json(false,'وصلت للحد اليومي لمساعد الطيبات. يرجى المحاولة غدًا.',[],403);\n $in=json_input(); Validator::required($in,['message']);\n $allowed=self::names('allowed'); $forbidden=self::names('forbidden'); $t=self::currentTrial($u['id']);\n $system=self::setting('ai_system_prompt');\n $messages=[['role'=>'system','content'=>$system],['role'=>'user','content'=>'اليوم الحالي: '.($t['current_day']??'غير محدد').\"\\nالمسموحات: \".implode('، ',$allowed).\"\\nالممنوعات: \".implode('، ',$forbidden).\"\\nرسالة المستخدم: \".$in['message']]];\n $res=DeepSeekService::chat($messages); $pdo=self::pdo();\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'user',$in['message'],0,now()]);\n $pdo->prepare('INSERT INTO ai_messages(user_id,role,message,tokens_used,created_at) VALUES(?,?,?,?,?)')->execute([$u['id'],'assistant',$res['text'],$res['tokens'],now()]);\n $pdo->prepare('UPDATE users SET ai_chat_used_today=ai_chat_used_today+1 WHERE id=?')->execute([$u['id']]);\n Response::json(true,'رد مساعد الطيبات',['reply'=>$res['text']]);\n }\n public static function subscription(string $action='status'): void {\n $u=Auth::user();\n if($action==='status') Response::json(true,'حالة الاشتراك',['plan'=>$u['plan'],'is_premium'=>(bool)$u['is_premium'],'ads_enabled'=>(bool)$u['ads_enabled'],'ai_chat_enabled'=>(bool)$u['ai_chat_enabled']]);\n if($action==='activate-dev') EntitlementService::apply($u['id'],'premium_monthly',date('Y-m-d H:i:s',strtotime('+30 days')));\n if($action==='cancel-dev') EntitlementService::apply($u['id'],'free',null);\n Response::json(true,'تم تحديث الاشتراك للتجربة');\n }\n private static function names(string $type): array { $s=self::pdo()->prepare('SELECT name_ar FROM food_items WHERE type=? AND is_active=1'); $s->execute([$type]); return array_column($s->fetchAll(),'name_ar'); }\n private static function setting(string $key): string { $s=self::pdo()->prepare('SELECT setting_value FROM app_settings WHERE setting_key=?'); $s->execute([$key]); return $s->fetchColumn() ?: ''; }\n private static function one(string $sql,array $p): ?array { $s=self::pdo()->prepare($sql); $s->execute($p); return $s->fetch() ?: null; }\n private static function userById(int $id): array { $s=self::pdo()->prepare('SELECT * FROM users WHERE id=?'); $s->execute([$id]); return $s->fetch(); }\n private static function needsDoctorWarning(array $in): bool {\n foreach(['has_diabetes','has_hypertension','has_kidney_disease','has_liver_disease','has_heart_disease','pregnant','breastfeeding','takes_medications'] as $k) if(!empty($in[$k])) return true;\n return false;\n }\n}\n"}}, "backend/config/env.example.php": {"adcb726c4b6e780c4fedc9b465c2319aa2caf6f2": {"t": 1785746177.9366634, "n": 1405, "h": "978f449e9d16b3e7", "c": "<?php\nreturn [\n // Clear deployment keys used by README and admins.\n 'APP_ENV' => 'local',\n 'DB_HOST' => '127.0.0.1',\n 'DB_NAME' => 'altayyibat',\n 'DB_USER' => 'root',\n 'DB_PASS' => '',\n 'JWT_SECRET' => 'change_this_long_random_secret',\n 'GOOGLE_CLIENT_ID' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'GOOGLE_CLIENT_IDS' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'DEEPSEEK_API_KEY' => '',\n 'DEEPSEEK_MODEL' => 'deepseek-chat',\n 'YOUTUBE_API_KEY' => '',\n 'YOUTUBE_CHANNEL_HANDLE' => '@DoctorDiaaAlAwady',\n 'CORS_ALLOWED_ORIGIN' => '*',\n\n // Internal lowercase aliases used by the native PHP app.\n 'app_env' => 'local',\n 'app_url' => 'http://localhost/altayyibat/backend/public',\n 'db_host' => '127.0.0.1',\n 'db_name' => 'altayyibat',\n 'db_user' => 'root',\n 'db_pass' => '',\n 'jwt_secret' => 'change_this_long_random_secret',\n 'google_client_id' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'google_client_ids' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'deepseek_api_key' => '',\n 'deepseek_model' => 'deepseek-chat',\n 'deepseek_base_url' => 'https://api.deepseek.com',\n 'youtube_api_key' => '',\n 'youtube_channel_handle' => '@DoctorDiaaAlAwady',\n 'cors_origin' => '*',\n];\n"}, "ebe8c0ba151c8daf85c90fb93d0fc7a178eb8890": {"t": 1785756540.3970633, "n": 1405, "h": "978f449e9d16b3e7", "c": "<?php\nreturn [\n // Clear deployment keys used by README and admins.\n 'APP_ENV' => 'local',\n 'DB_HOST' => '127.0.0.1',\n 'DB_NAME' => 'altayyibat',\n 'DB_USER' => 'root',\n 'DB_PASS' => '',\n 'JWT_SECRET' => 'change_this_long_random_secret',\n 'GOOGLE_CLIENT_ID' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'GOOGLE_CLIENT_IDS' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'DEEPSEEK_API_KEY' => '',\n 'DEEPSEEK_MODEL' => 'deepseek-chat',\n 'YOUTUBE_API_KEY' => '',\n 'YOUTUBE_CHANNEL_HANDLE' => '@DoctorDiaaAlAwady',\n 'CORS_ALLOWED_ORIGIN' => '*',\n\n // Internal lowercase aliases used by the native PHP app.\n 'app_env' => 'local',\n 'app_url' => 'http://localhost/altayyibat/backend/public',\n 'db_host' => '127.0.0.1',\n 'db_name' => 'altayyibat',\n 'db_user' => 'root',\n 'db_pass' => '',\n 'jwt_secret' => 'change_this_long_random_secret',\n 'google_client_id' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'google_client_ids' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'deepseek_api_key' => '',\n 'deepseek_model' => 'deepseek-chat',\n 'deepseek_base_url' => 'https://api.deepseek.com',\n 'youtube_api_key' => '',\n 'youtube_channel_handle' => '@DoctorDiaaAlAwady',\n 'cors_origin' => '*',\n];\n"}, "de98d3e36afb97b3358f1e15084e5cf39544b7ee": {"t": 1785756541.80061, "n": 1239, "h": "4ad9c9f315e290e2", "c": "<?php\nreturn [\n // Clear deployment keys used by README and admins.\n 'APP_ENV' => 'local',\n 'DB_HOST' => '127.0.0.1',\n 'DB_NAME' => 'altayyibat',\n 'DB_USER' => 'root',\n 'DB_PASS' => '',\n 'JWT_SECRET' => 'change_this_long_random_secret',\n 'GOOGLE_CLIENT_ID' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'GOOGLE_CLIENT_IDS' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'DEEPSEEK_API_KEY' => '',\n 'DEEPSEEK_MODEL' => 'deepseek-chat',\n 'CORS_ALLOWED_ORIGIN' => '*',\n\n // Internal lowercase aliases used by the native PHP app.\n 'app_env' => 'local',\n 'app_url' => 'http://localhost/altayyibat/backend/public',\n 'db_host' => '127.0.0.1',\n 'db_name' => 'altayyibat',\n 'db_user' => 'root',\n 'db_pass' => '',\n 'jwt_secret' => 'change_this_long_random_secret',\n 'google_client_id' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'google_client_ids' => 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com,YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',\n 'deepseek_api_key' => '',\n 'deepseek_model' => 'deepseek-chat',\n 'deepseek_base_url' => 'https://api.deepseek.com',\n 'cors_origin' => '*',\n];\n"}, "3e95778202dbb8d991b3ee6a9f739bdfcc09debf": {"t": 1785756543.4779327, "n": 987, "h": "9edc2b97a1893276", "c": "<?php\nreturn [\n // Clear deployment keys used by README and admins.\n 'APP_ENV' => 'local',\n 'DB_HOST' => '127.0.0.1',\n 'DB_NAME' => 'altayyibat',\n 'DB_USER' => 'root',\n 'DB_PASS' => '',\n 'JWT_SECRET' => 'change_this_long_random_secret',\n 'GOOGLE_CLIENT_ID' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'DEEPSEEK_API_KEY' => '',\n 'DEEPSEEK_MODEL' => 'deepseek-chat',\n 'CORS_ALLOWED_ORIGIN' => '*',\n\n // Internal lowercase aliases used by the native PHP app.\n 'app_env' => 'local',\n 'app_url' => 'http://localhost/altayyibat/backend/public',\n 'db_host' => '127.0.0.1',\n 'db_name' => 'altayyibat',\n 'db_user' => 'root',\n 'db_pass' => '',\n 'jwt_secret' => 'change_this_long_random_secret',\n 'google_client_id' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'deepseek_api_key' => '',\n 'deepseek_model' => 'deepseek-chat',\n 'deepseek_base_url' => 'https://api.deepseek.com',\n 'cors_origin' => '*',\n];\n"}, "139960702fe4091d49aeb5411e0fb5856ae7e183": {"t": 1785756544.6425061, "n": 457, "h": "80d8450512ed9b0f", "c": "<?php\nreturn [\n 'app_env' => 'local',\n 'app_url' => 'http://localhost/altayyibat/backend/public',\n 'db_host' => '127.0.0.1',\n 'db_name' => 'altayyibat',\n 'db_user' => 'root',\n 'db_pass' => '',\n 'jwt_secret' => 'change_this_long_random_secret',\n 'google_client_id' => 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com',\n 'deepseek_api_key' => '',\n 'deepseek_base_url' => 'https://api.deepseek.com',\n 'cors_origin' => '*',\n];\n"}}}}