<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Library\Queue\QueueManager;
use EasyChat\Model\SmsJob;










class DirectSmsDispatchOutbox {
    public const DELAY_SECONDS = 20;

    private $registry;
    private $db;
    private $prefix;

    public function __construct($registry) {
        $this->registry = $registry;
        $this->db = $registry->get('db');
        $this->prefix = easychat_db_prefix();
    }

    



    public function createLegacyMessageWithRecovery(callable $create_message): int {
         
         
        new QueueManager($this->registry);
        $this->db->query('START TRANSACTION');
        try {
            $message_id = (int)$create_message();
            if ($message_id <= 0) {
                throw new \RuntimeException('sms_dispatch_message_create_failed');
            }
            $this->enqueue($message_id);
            $this->db->query('COMMIT');
            return $message_id;
        } catch (\Throwable $e) {
            try { $this->db->query('ROLLBACK'); } catch (\Throwable $ignored) {}
            throw $e;
        }
    }

     
    public function enqueue(int $message_id): int {
        if ($message_id <= 0) {
            throw new \InvalidArgumentException('sms_dispatch_message_id_required');
        }

        $payload = json_encode([
            'message_id' => $message_id,
            'deadline_at' => time() + SmsJob::DEFAULT_TTL_SECONDS,
        ], JSON_UNESCAPED_SLASHES);
        if (!is_string($payload)) {
            throw new \RuntimeException('sms_dispatch_payload_encode_failed');
        }

        $dedupe_key = 'direct_sms_dispatch:' . $message_id;
        $available_at = time() + self::DELAY_SECONDS;
        $created_at = time();
        $this->db->query(
            "INSERT INTO `{$this->prefix}queue`
                (`job_type`, `payload`, `dedupe_key`, `status`, `attempts`, `max_attempts`, `priority`, `available_at`, `created_at`)
             VALUES
                ('direct_sms_dispatch_recovery', '" . $this->db->escape($payload) . "', '" . $this->db->escape($dedupe_key) . "',
                 'pending', 0, 5, 8, {$available_at}, {$created_at})
             ON DUPLICATE KEY UPDATE `id` = LAST_INSERT_ID(`id`)"
        );
        return (int)$this->db->getLastId();
    }

     
    public function deadlineForMessage(int $message_id): int {
        if ($message_id <= 0) return 0;
        try {
            $dedupe_key = 'direct_sms_dispatch:' . $message_id;
            $query = $this->db->query(
                "SELECT `payload` FROM `{$this->prefix}queue`
                 WHERE `job_type` = 'direct_sms_dispatch_recovery'
                   AND `dedupe_key` = '" . $this->db->escape($dedupe_key) . "'
                   AND `status` IN ('pending','processing')
                 LIMIT 1"
            );
            $payload = json_decode((string)($query->row['payload'] ?? ''), true);
            if (!is_array($payload) || (int)($payload['message_id'] ?? 0) !== $message_id) return 0;
            return max(0, (int)($payload['deadline_at'] ?? 0));
        } catch (\Throwable $e) {
             
             
            return 0;
        }
    }

     
    public function hasPendingRecovery(int $message_id): bool {
        if ($message_id <= 0) return false;
        try {
            $dedupe_key = 'direct_sms_dispatch:' . $message_id;
            $query = $this->db->query(
                "SELECT `id` FROM `{$this->prefix}queue`
                 WHERE `job_type` = 'direct_sms_dispatch_recovery'
                   AND `dedupe_key` = '" . $this->db->escape($dedupe_key) . "'
                   AND `status` IN ('pending','processing')
                 LIMIT 1"
            );
            return !empty($query->row);
        } catch (\Throwable $e) {
             
             
            return true;
        }
    }

    



    public function settleAfterImmediateAttempt(int $message_id, array $result): void {
        if ($message_id <= 0 || $this->shouldKeepPending($result)) {
            return;
        }

        $dedupe_key = 'direct_sms_dispatch:' . $message_id;
        $this->db->query(
            "UPDATE `{$this->prefix}queue`
             SET `status` = 'done', `dedupe_key` = NULL, `locked_at` = NULL,
                 `lock_token` = NULL, `error` = NULL
             WHERE `status` = 'pending'
               AND `dedupe_key` = '" . $this->db->escape($dedupe_key) . "'"
        );
    }

    private function shouldKeepPending(array $result): bool {
        $job_id = (int)($result['job_id'] ?? 0);
        if ($job_id > 0) {
            $job = (new SmsJob($this->registry))->getById($job_id);
            if ($job) {
                 
                 
                return (string)($job['status'] ?? '') === 'pending';
            }
             
             
            return true;
        }
        if (!empty($result['success'])) {
             
             
             
            return (string)($result['skipped'] ?? '') !== 'already_sent';
        }
        if (!empty($result['retryable'])) {
            return true;
        }
        return in_array((string)($result['error'] ?? ''), [
            'outbound_exception',
            'outbound_bad_result',
            'sms_device_missing',
            'sms_dispatch_busy',
            'sms_dispatch_storage_failed',
        ], true);
    }
}
