<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Library\AppClock;
use EasyChat\Model\Messenger;
use PhpWA\Client;
use PhpWA\Client\SessionEvent;

 
final class WhatsAppPersonalWorker {
    private $registry;
    private $model;
    private $lifecycle;

    public function __construct($registry) {
        $this->registry = $registry;
        $this->model = new Messenger($registry);
        $this->lifecycle = new WhatsAppPersonalLifecycle($registry);
    }

    public function run(array $options = []): array {
        $started = microtime(true);
        $channel = $this->lifecycle->ensureChannel();
        $generation = $this->lifecycle->storedGeneration($channel);
        $this->lifecycle->recordCronTick((int)$channel['channel_id'], $generation, (int)floor($started));
        if ((string)($channel['status'] ?? '') !== 'active') {
            return ['success' => true, 'skipped' => 'channel_inactive'];
        }
        if ($this->hasPendingOutbound((int)$channel['channel_id'])) {
             
             
             
            return ['success' => true, 'skipped' => 'outbound_priority'];
        }
        $client = $this->lifecycle->client($channel, $generation);
        if (!$client->isPaired()) {
            $this->lifecycle->updateWorkerState((int)$channel['channel_id'], $generation, [
                'status' => 'authorization_required',
                'last_finished_at' => time(),
            ], ['status' => 'error', 'last_error' => 'authorization_required']);
            return ['success' => false, 'error' => 'authorization_required'];
        }

        $events = [];
        $client->onSessionEvent(function (SessionEvent $event) use (&$events): void {
            $events[] = $event;
        });
        try {
             
             
             
            $network = $client->runFor(23.0, 800);
            if (!$network->lockAcquired()) {
                return ['success' => true, 'skipped' => 'account_busy'];
            }
            $fatal = $this->fatalSessionEvent($events);
            if ($fatal !== '') {
                $this->lifecycle->updateWorkerState((int)$channel['channel_id'], $generation, [
                    'status' => 'authorization_required',
                    'last_finished_at' => time(),
                    'last_error_code' => $fatal,
                ], ['status' => 'error', 'last_error' => $fatal]);
                return ['success' => false, 'error' => $fatal];
            }

            $outbox = $client->withOpenSession(function (Client $online) use ($channel) {
                return $online->consumeOutbox(
                    new WhatsAppPersonalOutboxHandler($this->registry, $channel, $online),
                    11.0,
                    100
                );
            }, 16.0, 600, 0.5);
            $success = $outbox->failed() === 0;
            $now = time();
             
             
             
            $channel_patch = [
                'status' => 'active',
                'last_error' => $success ? '' : 'whatsapp_outbox_degraded',
            ];
            if ($outbox->delivered() > 0) {
                $channel_patch['last_inbound_at'] = (new AppClock($this->registry))->nowSql();
            }
            $worker_state = [
                'status' => $success ? 'ready' : 'degraded',
                'last_started_at' => (int)floor($started),
                'last_finished_at' => $now,
                'duration_ms' => (int)round((microtime(true) - $started) * 1000),
                'nodes' => $network->nodesProcessed(),
                'delivered_events' => $outbox->delivered(),
                'failed_events' => $outbox->failed(),
                'dead_lettered_events' => $outbox->deadLettered(),
                'last_error_code' => (string)($outbox->lastErrorCode() ?? ''),
            ];
            if ($success) {
                $worker_state['last_success_at'] = $now;
            }
            $this->lifecycle->updateWorkerState(
                (int)$channel['channel_id'],
                $generation,
                $worker_state,
                $channel_patch
            );
            return [
                'success' => $success,
                'nodes' => $network->nodesProcessed(),
                'events' => $outbox->delivered(),
                'failed' => $outbox->failed(),
                'dead_lettered' => $outbox->deadLettered(),
            ];
        } catch (\PhpWA\Exception\AccountBusyException $e) {
            return ['success' => true, 'skipped' => 'account_busy'];
        } catch (\Throwable $e) {
            $code = $this->safeErrorCode($e);
            $this->lifecycle->updateWorkerState((int)$channel['channel_id'], $generation, [
                'status' => 'degraded',
                'last_finished_at' => time(),
                'duration_ms' => (int)round((microtime(true) - $started) * 1000),
                'last_error_code' => $code,
            ], ['last_error' => $code]);
            return ['success' => false, 'error' => $code];
        }
    }

    private function fatalSessionEvent(array $events): string {
        foreach ($events as $event) {
            if ($event instanceof SessionEvent && $event->type() === SessionEvent::DISCONNECTED
                && $event->credentialResetRequired()) {
                return 'authorization_required';
            }
        }
        return '';
    }

    private function safeErrorCode(\Throwable $error): string {
        $class = strtolower(get_class($error));
        if (strpos($class, 'generation') !== false) return 'session_replaced';
        if (strpos($class, 'timeout') !== false) return 'provider_timeout';
        if (strpos($class, 'storage') !== false || strpos($class, 'pdo') !== false) return 'storage_unavailable';
        return 'whatsapp_worker_failed';
    }

    private function hasPendingOutbound(int $channel_id): bool {
        if ($channel_id <= 0) return false;
        $db = $this->registry->get('db');
        $prefix = easychat_db_prefix();
        $query = $db->query(
            "SELECT `delivery_id` FROM `{$prefix}messenger_deliveries`
             WHERE `channel_id` = {$channel_id}
               AND `direction` = 'out'
               AND `status` = 'pending'
             ORDER BY `delivery_id` ASC LIMIT 1"
        );
        return !empty($query->row);
    }
}
