<?php
namespace EasyChat\Library\Queue\Jobs;

use EasyChat\Library\Messenger\OutboundService;
use EasyChat\Library\Messenger\SmsDeliveryProjector;
use EasyChat\Library\Messenger\SmsOutboundService;
use EasyChat\Library\Queue\JobInterface;
use EasyChat\Library\Queue\RequeueWithoutAttemptException;
use EasyChat\Model\SmsJob;

 
class DirectSmsDispatchRecoveryJob implements JobInterface {
    private $registry;

    public function __construct($registry) {
        $this->registry = $registry;
    }

    public function handle(array $payload): void {
        $message_id = (int)($payload['message_id'] ?? 0);
        $deadline_at = (int)($payload['deadline_at'] ?? 0);
        if ($message_id <= 0 || $deadline_at <= 0) {
            throw new \InvalidArgumentException('direct_sms_recovery_payload_invalid');
        }

        $job_model = new SmsJob($this->registry);
        $job = $job_model->getByMessageOrDelivery($message_id, 0);
        if (!$job) {
            try {
                 
                 
                 
                $result = (new OutboundService($this->registry))->sendMessageId($message_id, false);
            } catch (\Throwable $e) {
                if (time() >= $deadline_at) {
                    throw new \RuntimeException('sms_expiry_projection_failed', 0, $e);
                }
                $this->requeue($payload, 'sms_dispatch_recovery_failed', 60, $deadline_at);
                return;
            }
            $job = $job_model->getByMessageOrDelivery($message_id, 0);
            if (!$job) {
                $error = (string)($result['error'] ?? '');
                if ($error === 'deadline_expired' || $error === 'message_not_found') {
                    return;
                }
                if (time() >= $deadline_at) {
                    throw new \RuntimeException('sms_expiry_projection_incomplete');
                }
                if ($this->isRetryableCreationError($error)) {
                    $this->requeue($payload, $error, 60, $deadline_at);
                }
                return;
            }
        }

        if (!$job) {
            return;
        }
        $status = (string)($job['status'] ?? '');
        if ($status !== 'pending') {
             
             
             
            (new SmsDeliveryProjector($this->registry))->syncFromSmsJob($job);
            return;
        }
        if (time() >= $deadline_at) {
            $expired = $job_model->markExpired((int)$job['job_id']);
            if ($expired) {
                (new SmsDeliveryProjector($this->registry))->syncFromSmsJob($expired);
            }
            return;
        }

        try {
            $wake = (new SmsOutboundService($this->registry))->notifyExistingJob((int)$job['job_id']);
        } catch (\Throwable $e) {
            $this->requeue($payload, 'sms_push_recovery_failed', 60, $deadline_at);
            return;
        }
        $current = $job_model->getById((int)$job['job_id']);
        if (!$current || (string)($current['status'] ?? '') !== 'pending' || !empty($wake['terminal'])) {
            if ($current) {
                (new SmsDeliveryProjector($this->registry))->syncFromSmsJob($current);
            }
            return;
        }

        $this->requeue(
            $payload,
            (string)($wake['error'] ?? 'sms_waiting_for_phone'),
            max(15, (int)($wake['retry_after'] ?? 60)),
            $deadline_at
        );
    }

    private function requeue(array $payload, string $code, int $delay, int $deadline_at): void {
        $remaining = $deadline_at - time();
        if ($remaining <= 0) {
            throw new \RuntimeException('sms_recovery_deadline_reached');
        }
        $code = preg_match('/^[a-z0-9_.-]{1,64}$/D', $code) ? $code : 'sms_dispatch_recovery_failed';
        throw new RequeueWithoutAttemptException(
            $code,
            max(1, min($delay, $remaining)),
            $payload,
            true
        );
    }

    private function isRetryableCreationError(string $error): bool {
        return in_array($error, [
            'sms_device_missing',
            'sms_dispatch_busy',
            'sms_dispatch_storage_failed',
            'outbound_exception',
        ], true);
    }
}
