<?php

namespace App\Core;

/**
 * Job Queue System - نظام طابور المهام
 * يدعم retry mechanism و monitoring
 */
class JobQueue
{
    private \PDO $db;
    private Logger $logger;
    private int $maxRetries;
    private int $retryDelay;

    public function __construct(\PDO $db, Logger $logger, int $maxRetries = 3, int $retryDelay = 60)
    {
        $this->db = $db;
        $this->logger = $logger;
        $this->maxRetries = $maxRetries;
        $this->retryDelay = $retryDelay;
        
        $this->ensureTableExists();
    }

    /**
     * التأكد من وجود جدول jobs
     */
    private function ensureTableExists(): void
    {
        try {
            $this->db->query('SELECT id FROM jobs LIMIT 1');
        } catch (\Throwable $e) {
            $sql = "CREATE TABLE IF NOT EXISTS jobs (
                id INT AUTO_INCREMENT PRIMARY KEY,
                queue VARCHAR(100) NOT NULL DEFAULT 'default',
                job_type VARCHAR(100) NOT NULL,
                payload TEXT NOT NULL,
                attempts INT DEFAULT 0,
                max_attempts INT DEFAULT 3,
                status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
                error_message TEXT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                available_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                processed_at TIMESTAMP NULL,
                tenant_id INT NULL,
                INDEX idx_queue_status (queue, status, available_at),
                INDEX idx_tenant (tenant_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
            $this->db->exec($sql);
        }
    }

    /**
     * إضافة مهمة جديدة
     */
    public function push(string $jobType, array $payload, string $queue = 'default', ?int $tenantId = null, int $delay = 0): int
    {
        $availableAt = date('Y-m-d H:i:s', time() + $delay);
        
        $stmt = $this->db->prepare("
            INSERT INTO jobs (queue, job_type, payload, max_attempts, available_at, tenant_id)
            VALUES (?, ?, ?, ?, ?, ?)
        ");
        
        $stmt->execute([
            $queue,
            $jobType,
            json_encode($payload, JSON_UNESCAPED_UNICODE),
            $this->maxRetries,
            $availableAt,
            $tenantId
        ]);
        
        $jobId = (int)$this->db->lastInsertId();
        $this->logger->info("Job queued", ['job_id' => $jobId, 'type' => $jobType, 'queue' => $queue]);
        
        return $jobId;
    }

    /**
     * معالجة المهام المعلقة
     */
    public function process(string $queue = 'default', int $limit = 10): int
    {
        $processed = 0;
        
        // جلب المهام المعلقة
        $stmt = $this->db->prepare("
            SELECT id, job_type, payload, attempts, max_attempts, tenant_id
            FROM jobs
            WHERE queue = ? AND status = 'pending' AND available_at <= NOW()
            ORDER BY created_at ASC
            LIMIT ?
            FOR UPDATE SKIP LOCKED
        ");
        $stmt->execute([$queue, $limit]);
        $jobs = $stmt->fetchAll();

        foreach ($jobs as $job) {
            $this->processJob($job);
            $processed++;
        }

        return $processed;
    }

    /**
     * معالجة مهمة واحدة
     */
    private function processJob(array $job): void
    {
        $jobId = (int)$job['id'];
        $jobType = $job['job_type'];
        $payload = json_decode($job['payload'], true);
        $attempts = (int)$job['attempts'];
        $maxAttempts = (int)$job['max_attempts'];
        $tenantId = $job['tenant_id'] ? (int)$job['tenant_id'] : null;

        // تحديث الحالة إلى processing
        $this->db->prepare("UPDATE jobs SET status = 'processing', attempts = attempts + 1 WHERE id = ?")->execute([$jobId]);

        try {
            // تنفيذ المهمة
            $this->executeJob($jobType, $payload, $tenantId);
            
            // تحديث الحالة إلى completed
            $this->db->prepare("
                UPDATE jobs 
                SET status = 'completed', processed_at = NOW() 
                WHERE id = ?
            ")->execute([$jobId]);
            
            $this->logger->info("Job completed", ['job_id' => $jobId, 'type' => $jobType]);
            
        } catch (\Throwable $e) {
            $errorMessage = $e->getMessage();
            $this->logger->error("Job failed", [
                'job_id' => $jobId,
                'type' => $jobType,
                'error' => $errorMessage,
                'attempt' => $attempts + 1
            ]);

            if ($attempts + 1 >= $maxAttempts) {
                // فشل نهائي
                $this->db->prepare("
                    UPDATE jobs 
                    SET status = 'failed', error_message = ?, processed_at = NOW() 
                    WHERE id = ?
                ")->execute([$errorMessage, $jobId]);
            } else {
                // إعادة المحاولة
                $availableAt = date('Y-m-d H:i:s', time() + $this->retryDelay * ($attempts + 1));
                $this->db->prepare("
                    UPDATE jobs 
                    SET status = 'pending', available_at = ?, error_message = ? 
                    WHERE id = ?
                ")->execute([$availableAt, $errorMessage, $jobId]);
            }
        }
    }

    /**
     * تنفيذ المهمة
     */
    private function executeJob(string $jobType, array $payload, ?int $tenantId): void
    {
        switch ($jobType) {
            case 'whatsapp_send':
                $this->handleWhatsAppSend($payload, $tenantId);
                break;
            case 'sms_send':
                $this->handleSMSSend($payload, $tenantId);
                break;
            case 'email_send':
                $this->handleEmailSend($payload, $tenantId);
                break;
            case 'backup_database':
                $this->handleBackupDatabase($payload);
                break;
            case 'cleanup_old_data':
                $this->handleCleanupOldData($payload);
                break;
            default:
                throw new \Exception("Unknown job type: {$jobType}");
        }
    }

    /**
     * معالجة إرسال واتساب
     */
    private function handleWhatsAppSend(array $payload, ?int $tenantId): void
    {
        require_once __DIR__ . '/WhatsApp.php';
        $whatsapp = new WhatsApp($this->db, $tenantId);
        $whatsapp->sendMessage($payload['phone'], $payload['message']);
    }

    /**
     * معالجة إرسال SMS
     */
    private function handleSMSSend(array $payload, ?int $tenantId): void
    {
        require_once __DIR__ . '/SMS.php';
        $sms = new SMS($this->db, $tenantId);
        $sms->send($payload['phone'], $payload['message']);
    }

    /**
     * معالجة إرسال Email
     */
    private function handleEmailSend(array $payload, ?int $tenantId): void
    {
        require_once __DIR__ . '/EmailService.php';
        $emailConfig = $this->getEmailConfig($tenantId);
        $emailService = new \App\Core\EmailService($emailConfig);
        
        $to = $payload['to'] ?? '';
        $subject = $payload['subject'] ?? '';
        $template = $payload['template'] ?? 'welcome';
        $variables = $payload['variables'] ?? [];
        
        if ($to && $subject) {
            $emailService->send($to, $subject, $template, $variables);
        }
    }

    /**
     * جلب إعدادات Email
     */
    private function getEmailConfig(?int $tenantId): array
    {
        // جلب من config أو settings table
        return [
            'from_email' => 'noreply@lablims.com',
            'from_name' => 'Smart Lab LIMS',
            'smtp' => [
                'enabled' => false
            ]
        ];
    }

    /**
     * معالجة backup قاعدة البيانات
     */
    private function handleBackupDatabase(array $payload): void
    {
        require_once __DIR__ . '/../scripts/backup_database.php';
        // Backup logic will be in backup_database.php
    }

    /**
     * معالجة تنظيف البيانات القديمة
     */
    private function handleCleanupOldData(array $payload): void
    {
        $days = $payload['days'] ?? 90;
        $cutoffDate = date('Y-m-d', strtotime("-{$days} days"));
        
        // حذف audit logs القديمة
        $this->db->prepare("DELETE FROM audit_log WHERE created_at < ?")->execute([$cutoffDate]);
        
        // حذف jobs المكتملة القديمة
        $this->db->prepare("DELETE FROM jobs WHERE status IN ('completed', 'failed') AND processed_at < ?")->execute([$cutoffDate]);
        
        $this->logger->info("Old data cleaned", ['cutoff_date' => $cutoffDate]);
    }

    /**
     * جلب إحصائيات المهام
     */
    public function getStats(string $queue = 'default'): array
    {
        $stmt = $this->db->prepare("
            SELECT 
                status,
                COUNT(*) as count
            FROM jobs
            WHERE queue = ?
            GROUP BY status
        ");
        $stmt->execute([$queue]);
        $results = $stmt->fetchAll();
        
        $stats = [
            'pending' => 0,
            'processing' => 0,
            'completed' => 0,
            'failed' => 0
        ];
        
        foreach ($results as $row) {
            $stats[$row['status']] = (int)$row['count'];
        }
        
        return $stats;
    }

    /**
     * إعادة محاولة المهام الفاشلة
     */
    public function retryFailed(string $queue = 'default', int $limit = 10): int
    {
        $stmt = $this->db->prepare("
            UPDATE jobs 
            SET status = 'pending', attempts = 0, available_at = NOW(), error_message = NULL
            WHERE queue = ? AND status = 'failed' AND attempts < max_attempts
            LIMIT ?
        ");
        $stmt->execute([$queue, $limit]);
        return $stmt->rowCount();
    }
}
