<?php
require_once __DIR__ . '/../Core.php';

class QueueJob extends Model
{
    public function create(string $type, array $payload, ?string $availableAt = null): int
    {
        $stmt = $this->db->prepare('INSERT INTO queue_jobs (job_type, payload_json, status, available_at) VALUES (?, ?, "queued", COALESCE(?, NOW()))');
        $stmt->execute([$type, json_encode($payload), $availableAt]);
        return (int)$this->db->lastInsertId();
    }

    public function nextRunnable(?string $type = null): ?array
    {
        $sql = 'SELECT * FROM queue_jobs WHERE status = "queued" AND available_at <= NOW()';
        $params = [];
        if ($type) {
            $sql .= ' AND job_type = ?';
            $params[] = $type;
        }
        $sql .= ' ORDER BY available_at ASC, id ASC LIMIT 1';
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetch() ?: null;
    }

    public function markProcessing(int $id): void
    {
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "processing", attempts = attempts + 1, updated_at = NOW() WHERE id = ?');
        $stmt->execute([$id]);
    }

    public function markDone(int $id): void
    {
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "done", updated_at = NOW() WHERE id = ?');
        $stmt->execute([$id]);
    }

    public function releaseForRetry(int $id, string $error, int $delayMinutes): void
    {
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "queued", last_error = ?, available_at = DATE_ADD(NOW(), INTERVAL ? MINUTE), updated_at = NOW() WHERE id = ?');
        $stmt->execute([$error, $delayMinutes, $id]);
    }

    public function markFailed(int $id, string $error): void
    {
        $job = $this->find($id);
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "failed", last_error = ?, updated_at = NOW() WHERE id = ?');
        $stmt->execute([$error, $id]);
        if ($job) {
            $archive = $this->db->prepare('INSERT INTO queue_dead_letters (queue_job_id, job_type, payload_json, last_error, attempts) VALUES (?, ?, ?, ?, ?)');
            $archive->execute([$id, $job['job_type'], $job['payload_json'], $error, $job['attempts']]);
        }
    }

    public function requeue(int $id): void
    {
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "queued", available_at = NOW(), updated_at = NOW() WHERE id = ?');
        $stmt->execute([$id]);
    }

    public function requeueDeadLetter(int $deadLetterId): ?int
    {
        $stmt = $this->db->prepare('SELECT * FROM queue_dead_letters WHERE id = ? LIMIT 1');
        $stmt->execute([$deadLetterId]);
        $row = $stmt->fetch();
        if (!$row) {
            return null;
        }
        return $this->create((string)$row['job_type'], json_decode((string)$row['payload_json'], true) ?: [], null);
    }

    public function find(int $id): ?array
    {
        $stmt = $this->db->prepare('SELECT * FROM queue_jobs WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        return $stmt->fetch() ?: null;
    }

    public function stats(): array
    {
        $stats = $this->db->query('SELECT SUM(status = "queued") queued_jobs, SUM(status = "processing") processing_jobs, SUM(status = "done") done_jobs, SUM(status = "failed") failed_jobs FROM queue_jobs')->fetch() ?: [];
        $stats['dead_letter_jobs'] = (int)($this->db->query('SELECT COUNT(*) FROM queue_dead_letters')->fetchColumn() ?: 0);
        return $stats;
    }

    public function recent(int $limit = 20): array
    {
        $stmt = $this->db->prepare('SELECT * FROM queue_jobs ORDER BY updated_at DESC, id DESC LIMIT ?');
        $stmt->bindValue(1, $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll();
    }


    public function failed(int $limit = 25): array
    {
        $stmt = $this->db->prepare('SELECT * FROM queue_jobs WHERE status = "failed" ORDER BY updated_at DESC, id DESC LIMIT ?');
        $stmt->bindValue(1, $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    public function retryFailed(int $id): void
    {
        $stmt = $this->db->prepare('UPDATE queue_jobs SET status = "queued", available_at = NOW(), last_error = NULL, updated_at = NOW() WHERE id = ? AND status = "failed"');
        $stmt->execute([$id]);
    }

    public function deadLetters(int $limit = 20): array
    {
        $stmt = $this->db->prepare('SELECT * FROM queue_dead_letters ORDER BY created_at DESC, id DESC LIMIT ?');
        $stmt->bindValue(1, $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll();
    }
}
