<?php
namespace Core\Services;
use Core\Database;

/**
 * AutomationEngine — Rule-Based Proactive Automation for OTIS
 *
 * Architecture:
 *  - Each rule is a self-contained method called processRule_*
 *  - Rules fire at most once per cooldown window (stored in automation_log)
 *  - Triggered actions write to `notifications` table (real-time alerts)
 *  - Some rules also trigger GamificationEngine events or smart_reminders
 *
 * How to add a new rule:
 *  1. Add a protected method processRule_myRuleName($userId): ?array
 *  2. Register it in $this->rules[] with code, name, cooldown_hours
 *  3. Done — it runs automatically on next request
 */
class AutomationEngine {

    private static $instance = null;
    private $db;

    /** Rule registry — each entry maps code → metadata */
    private $rules = [
        ['code' => 'no_tasks_done',        'name' => 'لا منجزات اليوم',             'cooldown_hours' => 6],
        ['code' => 'overspending',         'name' => 'إنفاق مفرط',                 'cooldown_hours' => 24],
        ['code' => 'fasting_broken',       'name' => 'انقطاع الصيام',              'cooldown_hours' => 24],
        ['code' => 'habit_streak_at_risk', 'name' => 'سلسلة عادة في خطر',          'cooldown_hours' => 8],
        ['code' => 'overdue_tasks',        'name' => 'مهام متأخرة متراكمة',         'cooldown_hours' => 12],
        ['code' => 'no_water_logged',      'name' => 'لم تُسجل الماء اليوم',        'cooldown_hours' => 8],
        ['code' => 'comeback_bonus',       'name' => 'مكافأة العودة',              'cooldown_hours' => 48],
        ['code' => 'titan_mode',           'name' => 'وضع التيتان (أداء متميز)',    'cooldown_hours' => 24],
    ];

    private function __construct() {
        $this->db = Database::getInstance();
    }

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new AutomationEngine();
        }
        return self::$instance;
    }

    // =========================================================
    // MAIN ENTRY POINT — call this once per request from layout
    // =========================================================
    public function run($userId): void {
        if (!$userId) return;

        $hour = (int)date('H');
        // Only evaluate automation between 7am–11pm to avoid spam at night
        if ($hour < 7 || $hour > 23) return;

        foreach ($this->rules as $rule) {
            if (!$this->isCooledDown($userId, $rule['code'], $rule['cooldown_hours'])) {
                continue; // Still in cooldown — skip
            }

            $method = 'processRule_' . $rule['code'];
            if (!method_exists($this, $method)) continue;

            try {
                $result = $this->$method($userId);
                if ($result !== null) {
                    $this->fireNotification($userId, $result);
                    $this->logTrigger($userId, $rule['code'], $result['title']);
                }
            } catch (\Exception $e) {
                error_log("[AutomationEngine] Rule {$rule['code']} error: " . $e->getMessage());
            }
        }
    }

    // =========================================================
    // RULE: no_tasks_done — After 3pm, no completed tasks
    // =========================================================
    private function processRule_no_tasks_done($userId): ?array {
        $hour = (int)date('H');
        if ($hour < 15) return null;

        $pending = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND status!='done' AND target_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        $done = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND status='done' AND target_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        if ($done == 0 && $pending > 0) {
            return [
                'title'   => '⚡ لا منجزات حتى الآن!',
                'message' => "اليوم يمضي ولم تُكمل أي مهمة بعد، لديك {$pending} مهمة قيد الانتظار. ابدأ بأسهل واحدة الآن.",
                'type'    => 'warning',
                'action_target' => '/todo',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: overspending — Daily spend > 3x average daily spend
    // =========================================================
    private function processRule_overspending($userId): ?array {
        $todaySpend = $this->db->query(
            "SELECT COALESCE(SUM(amount),0) FROM finance_expenses WHERE user_id=? AND expense_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        if ($todaySpend < 50) return null; // Not enough data

        $avgDaily = $this->db->query(
            "SELECT COALESCE(AVG(daily_total),0) FROM (
                SELECT SUM(amount) as daily_total FROM finance_expenses
                WHERE user_id=? AND expense_date BETWEEN DATE_SUB(CURDATE(),INTERVAL 30 DAY) AND DATE_SUB(CURDATE(),INTERVAL 1 DAY)
                AND deleted_at IS NULL GROUP BY expense_date
            ) AS t",
            [$userId]
        )->fetchColumn();

        if ($avgDaily > 0 && $todaySpend > ($avgDaily * 2.5)) {
            $formatted = number_format($todaySpend, 0);
            $avgFmt    = number_format($avgDaily, 0);
            return [
                'title'   => '🚨 إنفاق مفرط اليوم!',
                'message' => "صرفت {$formatted} ج.م اليوم — أكثر من 2.5× معدلك اليومي ({$avgFmt} ج.م). توقف وراجع نفقاتك!",
                'type'    => 'danger',
                'action_target' => '/finance',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: fasting_broken — Active fast that seems abandoned
    // =========================================================
    private function processRule_fasting_broken($userId): ?array {
        // Look for a fast started today but with no end, and > 1hr ago with a meal logged
        $fastStart = $this->db->query(
            "SELECT id, start_time FROM health_fasts WHERE user_id=? AND status='active' AND DATE(start_time)=CURDATE()",
            [$userId]
        )->fetch();

        if (!$fastStart) return null;

        $mealAfterFast = $this->db->query(
            "SELECT COUNT(*) FROM health_meals WHERE user_id=? AND DATE(eaten_at)=CURDATE() AND eaten_at > ?",
            [$userId, $fastStart->start_time]
        )->fetchColumn();

        if ($mealAfterFast > 0) {
            return [
                'title'   => '⚠️ صيامك قد ينكسر!',
                'message' => 'تم تسجيل وجبة أثناء الصيام النشط. هل أنهيت الصيام؟ قم بتحديث سجل الصيام.',
                'type'    => 'warning',
                'action_target' => '/health',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: habit_streak_at_risk — Habit with streak ≥ 3 not logged today
    // =========================================================
    private function processRule_habit_streak_at_risk($userId): ?array {
        $hour = (int)date('H');
        if ($hour < 18) return null; // Only warn in evening

        $habits = $this->db->query(
            "SELECT h.id, h.name FROM habits h WHERE h.user_id=? AND h.deleted_at IS NULL",
            [$userId]
        )->fetchAll(\PDO::FETCH_OBJ);

        foreach ($habits as $habit) {
            // Check logged today
            $loggedToday = $this->db->query(
                "SELECT COUNT(*) FROM habit_logs WHERE habit_id=? AND user_id=? AND log_date=CURDATE() AND deleted_at IS NULL",
                [$habit->id, $userId]
            )->fetchColumn();
            if ($loggedToday) continue;

            // Count yesterday streak
            $streak = 0;
            for ($i = 1; $i <= 30; $i++) {
                $d = date('Y-m-d', strtotime("-{$i} days"));
                $cnt = $this->db->query(
                    "SELECT COUNT(*) FROM habit_logs WHERE habit_id=? AND user_id=? AND log_date=? AND deleted_at IS NULL",
                    [$habit->id, $userId, $d]
                )->fetchColumn();
                if ($cnt) $streak++; else break;
            }

            if ($streak >= 3) {
                return [
                    'title'   => "🔥 سلسلة '{$habit->name}' في خطر!",
                    'message' => "سلسلة {$streak} يوم متواصل ستنكسر إذا لم تُسجل عادتك '{$habit->name}' اليوم. أنت تستحق الاستمرار!",
                    'type'    => 'warning',
                    'action_target' => '/habits',
                    'action_type'   => 'navigate'
                ];
            }
        }
        return null;
    }

    // =========================================================
    // RULE: overdue_tasks — More than 5 overdue tasks
    // =========================================================
    private function processRule_overdue_tasks($userId): ?array {
        $overdue = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND status='pending' AND due_date < CURDATE() AND due_date IS NOT NULL AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        if ($overdue >= 5) {
            return [
                'title'   => '📋 مهام متأخرة متراكمة!',
                'message' => "لديك {$overdue} مهمة متأخرة عن موعدها. هذا يؤثر على مؤشر انضباطك. راجع قائمتك فوراً.",
                'type'    => 'danger',
                'action_target' => '/todo',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: no_water_logged — After 2pm, no water entry today
    // =========================================================
    private function processRule_no_water_logged($userId): ?array {
        $hour = (int)date('H');
        if ($hour < 14) return null;

        $cups = $this->db->query(
            "SELECT COALESCE(SUM(cups),0) FROM health_water_logs WHERE user_id=? AND log_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        if ($cups == 0) {
            return [
                'title'   => '💧 تذكر شرب الماء!',
                'message' => 'لم تُسجل أي كمية ماء اليوم حتى الآن. الجفاف يُضعف تركيزك وطاقتك. اشرب الآن وسجّل.',
                'type'    => 'info',
                'action_target' => '/health',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: comeback_bonus — Returned after 2+ days of inactivity
    // =========================================================
    private function processRule_comeback_bonus($userId): ?array {
        $yesterday = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND DATE(updated_at)=DATE_SUB(CURDATE(),INTERVAL 1 DAY)",
            [$userId]
        )->fetchColumn();

        $dayBefore = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND DATE(updated_at)=DATE_SUB(CURDATE(),INTERVAL 2 DAY)",
            [$userId]
        )->fetchColumn();

        $today = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND DATE(updated_at)=CURDATE()",
            [$userId]
        )->fetchColumn();

        if ($yesterday == 0 && $dayBefore == 0 && $today > 0) {
            // Fire XP bonus for comeback
            require_once __DIR__ . '/GamificationEngine.php';
            GamificationEngine::getInstance()->onEvent('task_completed', $userId);

            return [
                'title'   => '🏆 مكافأة العودة!',
                'message' => 'مرحباً بعودتك! حصلت على نقاط XP إضافية كمكافأة على رجوعك للنظام. استمر يا بطل!',
                'type'    => 'success',
                'action_target' => '/gamification',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // RULE: titan_mode — All tasks done, habits logged, good spending
    // =========================================================
    private function processRule_titan_mode($userId): ?array {
        $hour = (int)date('H');
        if ($hour < 18) return null;

        $pending = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND status!='done' AND target_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        $done = $this->db->query(
            "SELECT COUNT(*) FROM todo_tasks WHERE user_id=? AND status='done' AND target_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        $habitsDone = $this->db->query(
            "SELECT COUNT(DISTINCT habit_id) FROM habit_logs WHERE user_id=? AND log_date=CURDATE() AND deleted_at IS NULL",
            [$userId]
        )->fetchColumn();

        if ($done >= 3 && $pending == 0 && $habitsDone >= 2) {
            // Boost XP
            require_once __DIR__ . '/GamificationEngine.php';
            GamificationEngine::getInstance()->onEvent('task_completed', $userId);

            return [
                'title'   => '⚡ وضع التيتان مفعّل!',
                'message' => "أداء استثنائي! أكملت {$done} مهام، وسجّلت {$habitsDone} عادات اليوم. النظام منحك مكافأة XP إضافية!",
                'type'    => 'success',
                'action_target' => '/gamification',
                'action_type'   => 'navigate'
            ];
        }
        return null;
    }

    // =========================================================
    // HELPERS
    // =========================================================

    /**
     * Returns true if the rule is ready to fire (outside cooldown window).
     */
    private function isCooledDown($userId, $ruleCode, $cooldownHours): bool {
        $last = $this->db->query(
            "SELECT triggered_at FROM automation_log WHERE user_id=? AND rule_code=? ORDER BY triggered_at DESC LIMIT 1",
            [$userId, $ruleCode]
        )->fetchColumn();

        if (!$last) return true;
        return (time() - strtotime($last)) >= ($cooldownHours * 3600);
    }

    /**
     * Write a notification into the notifications table.
     */
    private function fireNotification($userId, array $data): void {
        $this->db->query(
            "INSERT INTO notifications (user_id, title, message, type, action_target, action_type, is_read)
             VALUES (?, ?, ?, ?, ?, ?, 0)",
            [
                $userId,
                $data['title'],
                $data['message'],
                $data['type'] ?? 'info',
                $data['action_target'] ?? null,
                $data['action_type']   ?? null,
            ]
        );
    }

    /**
     * Write a log entry for a fired rule.
     */
    private function logTrigger($userId, $ruleCode, $action): void {
        $this->db->query(
            "INSERT INTO automation_log (user_id, rule_code, action_taken, triggered_at) VALUES (?, ?, ?, NOW())",
            [$userId, $ruleCode, $action]
        );
    }
}
