<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Library\Queue\QueueManager;
use EasyChat\Model\Messenger as MessengerModel;
use EasyChat\Model\MessengerItemContext;
use EasyChat\Model\Setting;

class YoulaItemContextService {
    private const JOB_TYPE = 'messenger_youla_item_context_enrich';
    private const DEDUPE_WINDOW_SECONDS = 60;

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

    public function __construct($registry) {
        $this->registry = $registry;
        $this->db = $registry->get('db');
        $this->prefix = easychat_db_prefix();
        $this->logger = new MessengerLogger($registry, YoulaProvider::PROVIDER);
    }

    public function remember(int $channel_id, array $context, string $reason = 'context_seen'): array {
        $item_id = $this->normalizeProductId((string)($context['item_id'] ?? $context['id'] ?? ''));
        if ($channel_id <= 0 || $item_id === '') {
            return [];
        }

        $model = new MessengerItemContext($this->registry);
        $row = $model->upsertSnapshot(YoulaProvider::PROVIDER, $channel_id, $item_id, $context);
        $this->enqueueIfDue($channel_id, $item_id, $reason, $reason === 'webhook_context' ? 0 : 10);
        return $row;
    }

    public function enqueueIfDue(int $channel_id, string $item_id, string $reason = 'context_seen', int $delay = 10): bool {
        $item_id = $this->normalizeProductId($item_id);
        if ($channel_id <= 0 || $item_id === '') {
            return false;
        }

        try {
            $model = new MessengerItemContext($this->registry);
            if (!$model->shouldEnrich(YoulaProvider::PROVIDER, $channel_id, $item_id)) {
                return false;
            }
            if ($this->hasRecentQueueJob($channel_id, $item_id)) {
                return false;
            }
            (new QueueManager($this->registry))->push(self::JOB_TYPE, [
                'channel_id' => $channel_id,
                'item_id' => $this->limitChars($item_id, 64),
                'reason' => $this->limitChars($reason, 64),
            ], max(0, min(300, $delay)), 3);
            return true;
        } catch (\Throwable $e) {
            $this->logger->warning('youla_item_context_enqueue_failed', [
                'channel_id' => $channel_id,
                'item_hash' => MessengerLogger::hashExternalId($item_id),
                'error' => $this->safeError($e->getMessage()),
            ]);
            return false;
        }
    }

    public function syncByPayload(array $payload, array $http_options = []): array {
        $channel_id = (int)($payload['channel_id'] ?? 0);
        $item_id = $this->normalizeProductId((string)($payload['item_id'] ?? ''));
        if ($channel_id <= 0 || $item_id === '') {
            return ['success' => true, 'skip' => 'payload_incomplete'];
        }

        $model = new MessengerItemContext($this->registry);
        $messenger = new MessengerModel($this->registry);
        $channel = $messenger->getChannelById($channel_id);
        if (!$channel || (string)($channel['provider'] ?? '') !== YoulaProvider::PROVIDER || (string)($channel['status'] ?? '') !== 'active') {
            $model->markUnavailable(YoulaProvider::PROVIDER, $channel_id, $item_id, 'failed', 'channel_inactive');
            return ['success' => true, 'skip' => 'channel_inactive'];
        }

        $credentials = json_decode((string)($channel['credentials_json'] ?? ''), true);
        $token = is_array($credentials) ? $this->youlaTokenFromCredentials($credentials) : '';
        if ($token === '') {
            $model->markUnavailable(YoulaProvider::PROVIDER, $channel_id, $item_id, 'failed', 'auth_missing');
            return ['success' => false, 'error' => 'auth_missing'];
        }

        $result = (new YoulaProvider($this->registry))->getProductInfo($token, $item_id, $http_options);
        if (empty($result['success'])) {
            $http_code = (int)($result['http_code'] ?? 0);
            $error = $this->safeError((string)($result['error'] ?? 'item_context_lookup_failed'));
            if (in_array($http_code, [403, 404, 405], true)) {
                $model->markUnavailable(YoulaProvider::PROVIDER, $channel_id, $item_id, 'unsupported', $error);
                return ['success' => true, 'skip' => 'item_api_unsupported'];
            }
            $model->markUnavailable(YoulaProvider::PROVIDER, $channel_id, $item_id, 'failed', $error);
            return ['success' => false, 'error' => $error ?: 'item_context_lookup_failed'];
        }

        $item = is_array($result['item_context'] ?? null) ? $result['item_context'] : [];
        if (!$item) {
            $model->markUnavailable(YoulaProvider::PROVIDER, $channel_id, $item_id, 'unsupported', 'item_context_empty');
            return ['success' => true, 'skip' => 'item_context_empty'];
        }

        $model->markEnriched(YoulaProvider::PROVIDER, $channel_id, $item_id, $item);
        return ['success' => true, 'item_id' => $item_id];
    }

    private function hasRecentQueueJob(int $channel_id, string $item_id): bool {
        $since = time() - self::DEDUPE_WINDOW_SECONDS;
        $channel_needle = '%"channel_id":' . (int)$channel_id . '%';
        $item_needle = '%"item_id":"' . $this->db->escape($item_id) . '"%';
        $query = $this->db->query(
            "SELECT `id`
             FROM `{$this->prefix}queue`
             WHERE `job_type` = '" . self::JOB_TYPE . "'
               AND `status` IN ('pending', 'processing')
               AND `created_at` >= " . (int)$since . "
               AND `payload` LIKE '" . $this->db->escape($channel_needle) . "'
               AND `payload` LIKE '" . $this->db->escape($item_needle) . "'
             LIMIT 1"
        );
        return $query && $query->num_rows > 0;
    }

    private function youlaTokenFromCredentials(array $credentials): string {
        $token = $this->decryptSecretOrPlain((string)($credentials['bot_token'] ?? ''));
        if ($token !== '') {
            return $token;
        }
        $bearer = $this->decryptSecretOrPlain((string)($credentials['token'] ?? ''));
        return $bearer;
    }

    private function decryptSecretOrPlain(string $stored): string {
        $stored = trim($stored);
        if ($stored === '') {
            return '';
        }
        if (strpos($stored, 'enc:v1:') !== 0) {
            return $stored;
        }
        $install_secret = (new Setting($this->registry))->get('chat_install_secret', '');
        return $install_secret !== '' ? SecretBox::decrypt($stored, $install_secret) : '';
    }

    private function safeError(string $error): string {
        $error = preg_replace('/[^a-z0-9_:-]/i', '_', $error);
        return $this->limitChars(trim((string)$error), 120);
    }

    private function limitChars(string $value, int $limit): string {
        if (function_exists('mb_substr')) {
            return mb_substr($value, 0, $limit, 'UTF-8');
        }
        return substr($value, 0, $limit);
    }

    private function normalizeProductId(string $value): string {
        $value = strtolower(trim($value));
        return preg_match('/^[a-f0-9]{24}$/', $value) === 1 ? $value : '';
    }
}
