<?php

declare(strict_types=1);

namespace App\Models;

use App\Core\BaseModel;
use PDO;

final class EmailQueueModel extends BaseModel
{
    protected string $table = 'email_queue';
    protected array $fillable = [
        'campaign_id', 'campaign_recipient_id', 'business_line_id', 'to_email', 'to_name', 'from_email',
        'subject', 'body_html', 'status', 'scheduled_for', 'created_by',
    ];
    protected bool $timestamps = false;
    protected bool $softDeletes = false;
    protected bool $auditStamps = false;

    /** Claims up to $limit pending, due rows atomically (UPDATE ... then SELECT) so two overlapping worker runs never double-send the same row. */
    public function claimDue(int $limit = 50): array
    {
        $pdo = $this->db;
        $pdo->beginTransaction();

        try {
            $ids = $pdo->prepare(
                "SELECT id FROM email_queue
                 WHERE status = 'pending' AND scheduled_for <= NOW() AND attempts < 5
                 ORDER BY scheduled_for LIMIT :limit FOR UPDATE"
            );
            $ids->bindValue('limit', $limit, PDO::PARAM_INT);
            $ids->execute();
            $rowIds = $ids->fetchAll(PDO::FETCH_COLUMN);

            if ($rowIds === []) {
                $pdo->commit();

                return [];
            }

            $placeholders = implode(',', array_fill(0, count($rowIds), '?'));
            $pdo->prepare("UPDATE email_queue SET status = 'processing' WHERE id IN ({$placeholders})")->execute($rowIds);
            $pdo->commit();

            $select = $pdo->prepare("SELECT * FROM email_queue WHERE id IN ({$placeholders})");
            $select->execute($rowIds);

            return $select->fetchAll();
        } catch (\Throwable $e) {
            $pdo->rollBack();
            throw $e;
        }
    }

    public function markSent(int $id): void
    {
        $this->db->prepare("UPDATE email_queue SET status = 'sent', sent_at = NOW() WHERE id = :id")->execute(['id' => $id]);
    }

    public function markFailed(int $id, string $error): void
    {
        $this->db->prepare(
            "UPDATE email_queue SET status = 'pending', attempts = attempts + 1, last_error = :error WHERE id = :id"
        )->execute(['id' => $id, 'error' => substr($error, 0, 500)]);

        // After 5 attempts claimDue() naturally stops selecting it — mark it failed for good so it stops retrying.
        $this->db->prepare(
            "UPDATE email_queue SET status = 'failed' WHERE id = :id AND attempts >= 5"
        )->execute(['id' => $id]);
    }

    public function queueDepth(): array
    {
        return $this->db->query(
            "SELECT status, COUNT(*) AS count FROM email_queue GROUP BY status"
        )->fetchAll(PDO::FETCH_KEY_PAIR);
    }
}
