<?php
/**
 * سودانا — Context Memory v21 (HARDENED)
 *
 * DB = Source of Truth (حل مشكلة multi-device)
 * SESSION = Cache فقط (لتسريع الـ reads)
 *
 * الفلسفة المُصلَحة:
 *   كتابة: DB أولاً → SESSION ثانياً
 *   قراءة: SESSION إذا متوفر → DB إذا لا
 *   فقدان SESSION: DB يُعيد بناء السياق تلقائياً
 */
class ContextMemory {

    private const MAX_HISTORY = 10;    // آخر 10 تبادلات لكل جلسة
    private const SESSION_TTL = 86400; // 24 ساعة

    // ── READ: DB = source of truth ──────────────────────────────
    public static function getSession(int $userId, string $sessionHash = ''): array {
        // SESSION CACHE (fastest — لكن ليس مصدر الحقيقة)
        $cacheKey = 'ctx_' . $userId . '_' . ($sessionHash ?: 'any');
        if (!empty($_SESSION[$cacheKey]) && is_array($_SESSION[$cacheKey])) {
            return array_slice($_SESSION[$cacheKey], -self::MAX_HISTORY);
        }

        // DB = SOURCE OF TRUTH
        try {
            $params = [$userId];
            $where  = 'user_id = ?';
            if ($sessionHash) {
                $where  .= ' AND session_hash = ?';
                $params[] = $sessionHash;
            }
            $st = db()->prepare(
                "SELECT role, content FROM context_memory
                 WHERE {$where}
                 ORDER BY created_at DESC LIMIT " . self::MAX_HISTORY
            );
            $st->execute($params);
            $rows = array_reverse($st->fetchAll());

            if (!empty($rows)) {
                // Populate session cache from DB
                $_SESSION[$cacheKey] = $rows;
            }
            return $rows;
        } catch (Throwable $ignored) {}
        return [];
    }

    // ── WRITE: DB first, SESSION second ─────────────────────────
    public static function save(int $userId, string $sessionHash, string $question, string $answer, string $service = 'general'): void {
        $q = mb_substr(strip_tags($question), 0, 500);
        $a = mb_substr(strip_tags($answer),   0, 1000);

        // 1. WRITE TO DB FIRST (source of truth)
        try {
            $db = db();

            // Enforce max rows per session to prevent unbounded growth
            $count = (int)$db->prepare(
                'SELECT COUNT(*) FROM context_memory WHERE user_id=? AND session_hash=?'
            )->execute([$userId, $sessionHash])
              ? $db->query("SELECT FOUND_ROWS()")->fetchColumn()
              : 0;

            // Simpler count
            $cnt = $db->prepare('SELECT COUNT(*) FROM context_memory WHERE user_id=? AND session_hash=?');
            $cnt->execute([$userId, $sessionHash]);
            if ((int)$cnt->fetchColumn() >= self::MAX_HISTORY * 2) {
                // Remove oldest 2 pairs to stay within limit
                $db->prepare(
                    'DELETE FROM context_memory WHERE user_id=? AND session_hash=?
                     ORDER BY created_at ASC LIMIT 4'
                )->execute([$userId, $sessionHash]);
            }

            $ins = $db->prepare(
                'INSERT INTO context_memory (user_id, session_hash, role, content, service)
                 VALUES (?,?,?,?,?)'
            );
            $ins->execute([$userId, $sessionHash, 'user',      $q, $service]);
            $ins->execute([$userId, $sessionHash, 'assistant', $a, $service]);

        } catch (Throwable $e) {
            error_log('[ContextMemory::save] DB error: ' . $e->getMessage());
            // DB write failed — still update SESSION cache as degraded fallback
        }

        // 2. UPDATE SESSION CACHE (secondary)
        $cacheKey = 'ctx_' . $userId . '_' . $sessionHash;
        if (!isset($_SESSION[$cacheKey]) || !is_array($_SESSION[$cacheKey])) {
            $_SESSION[$cacheKey] = [];
        }
        $_SESSION[$cacheKey][] = ['role'=>'user',      'content'=>$q];
        $_SESSION[$cacheKey][] = ['role'=>'assistant',  'content'=>$a];

        // Trim session cache
        if (count($_SESSION[$cacheKey]) > self::MAX_HISTORY * 2) {
            $_SESSION[$cacheKey] = array_slice($_SESSION[$cacheKey], -(self::MAX_HISTORY * 2));
        }
    }

    // ── INVALIDATE: clear both DB and SESSION for user ───────────
    public static function clear(int $userId, string $sessionHash = ''): void {
        try {
            if ($sessionHash) {
                db()->prepare('DELETE FROM context_memory WHERE user_id=? AND session_hash=?')
                   ->execute([$userId, $sessionHash]);
            } else {
                db()->prepare('DELETE FROM context_memory WHERE user_id=?')
                   ->execute([$userId]);
            }
        } catch (Throwable $ignored) {}

        // Clear session cache
        foreach (array_keys($_SESSION) as $key) {
            if (str_starts_with($key, 'ctx_' . $userId)) {
                unset($_SESSION[$key]);
            }
        }
    }

    // ── USER PROFILE (read-through cache) ───────────────────────
    public static function getUserProfile(int $userId): array {
        $profileKey = 'profile_' . $userId;

        // SESSION CACHE
        if (!empty($_SESSION[$profileKey]) && is_array($_SESSION[$profileKey])) {
            return $_SESSION[$profileKey];
        }

        // DB
        try {
            $st = db()->prepare(
                'SELECT id, name, state, city, neighborhood_id, subscription_tier,
                        learning_track, has_children,
                        provider_verified, avg_rating
                 FROM users WHERE id = ? LIMIT 1'
            );
            $st->execute([$userId]);
            $profile = $st->fetch() ?: [];

            if ($profile) {
                // Check if user has children (medical context)
                if (!isset($profile['has_children'])) {
                    try {
                        $ch = db()->prepare('SELECT COUNT(*) FROM family_members WHERE user_id=?');
                        $ch->execute([$userId]);
                        $profile['has_children'] = (int)$ch->fetchColumn() > 0;
                    } catch (Throwable $ignored) {
                        $profile['has_children'] = false;
                    }
                }
                $_SESSION[$profileKey] = $profile;
            }
            return $profile;
        } catch (Throwable $ignored) {}
        return [];
    }
}
