<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Library\AppClock;
use EasyChat\Model\Messenger as MessengerModel;
use EasyChat\Model\Setting;








class AvitoHealthService {
    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 report(): array {
        $model_setting = new Setting($this->registry);
        $model_messenger = new MessengerModel($this->registry);
        $channel = $model_messenger->getChannelByProvider(AvitoProvider::PROVIDER)
            ?: $model_messenger->ensureChannel(AvitoProvider::PROVIDER, 'Avito');
        $channel_id = (int)($channel['channel_id'] ?? 0);
        $credentials = $this->decodeCredentials($channel);
        $token = $this->tokenFromCredentials($credentials);
        $provider = new AvitoProvider($this->registry);
        $webhook_url = $this->buildWebhookUrl($channel);
        $bot_user_id = preg_replace('/\D+/', '', (string)($channel['bot_user_id'] ?? ''));
        $recommendations = [];

        $report = [
            'success' => true,
            'checked_at' => (new AppClock($this->registry))->nowSql(),
            'channel' => [
                'status' => (string)($channel['status'] ?? 'disabled'),
                'active' => (string)($channel['status'] ?? '') === 'active',
                'credentials_present' => $token !== '',
                'bot_user_id_present' => $bot_user_id !== '',
                'api_account_present' => false,
                'webhook_synced_at' => (string)($channel['webhook_synced_at'] ?? ''),
                'last_error' => $this->safeDiagnosticText((string)($channel['last_error'] ?? '')),
                'expected_webhook_hash' => MessengerLogger::hashExternalId($webhook_url),
            ],
            'avatars' => [
                'enabled' => $model_setting->get('chat_messenger_avatars_status', '1') === '1',
            ],
            'subscriptions' => [
                'checked' => false,
                'ok' => false,
                'webhook_present' => false,
                'count' => 0,
                'http_code' => 0,
                'registered_webhook_hashes' => [],
                'error' => '',
            ],
            'contacts' => [
                'total' => 0,
                'sample' => [],
            ],
            'queue' => [
                'recent_total' => 0,
                'recent_failed' => 0,
                'types' => [],
            ],
            'recommendations' => [],
            'access_incident' => (new AvitoAccessIncidentService($this->registry))->summary(),
        ];

        if (!$report['channel']['active']) {
            $this->addRecommendation($recommendations, 'Канал Авито выключен. Включите переключатель и выполните «Проверить и подключить».');
        }
        if ($token === '') {
            $this->addRecommendation($recommendations, 'Не найдены сохраненные Client ID и Client Secret. Введите их в блоке Авито.');
        }

        if ($token !== '') {
            if ($bot_user_id === '') {
                $bot = $provider->getBotInfo($token);
                if (!empty($bot['success']) && !empty($bot['id'])) {
                    $bot_user_id = preg_replace('/\D+/', '', (string)$bot['id']);
                    $report['channel']['api_account_present'] = $bot_user_id !== '';
                    $this->addRecommendation($recommendations, 'Avito API доступен, но локальный bot_user_id не сохранен. Повторите «Проверить и подключить», чтобы закрепить аккаунт канала.');
                } else {
                    $this->addRecommendation($recommendations, 'Avito API не подтвердил учетные данные. Проверьте Client ID/Client Secret и доступ к Messenger API.');
                }
            } else {
                $report['channel']['api_account_present'] = true;
            }

            $subscriptions = $provider->listSubscriptions($token);
            $report['subscriptions']['checked'] = true;
            $report['subscriptions']['http_code'] = (int)($subscriptions['http_code'] ?? 0);
            if (!empty($subscriptions['success'])) {
                $urls = $this->subscriptionUrls($subscriptions['json'] ?? []);
                $hashes = [];
                foreach ($urls as $url) {
                    $hashes[] = MessengerLogger::hashExternalId($url);
                }
                $report['subscriptions']['ok'] = true;
                $report['subscriptions']['count'] = count($urls);
                $report['subscriptions']['webhook_present'] = in_array($webhook_url, $urls, true);
                $report['subscriptions']['registered_webhook_hashes'] = array_slice(array_values(array_unique($hashes)), 0, 8);
                if (!$report['subscriptions']['webhook_present']) {
                    $this->addRecommendation($recommendations, 'Список subscriptions Avito доступен, но ожидаемый webhook EasyChat не найден. Повторите подключение и проверьте HTTPS/443.');
                }
            } else {
                $report['subscriptions']['error'] = $this->safeDiagnosticText((string)($subscriptions['error'] ?? 'subscription_check_failed'));
                $this->addRecommendation($recommendations, 'Не удалось прочитать subscriptions Avito. Это мешает подтвердить webhook V3.');
            }
        }

        $contacts = $this->recentContacts($channel_id, 5);
        $report['contacts']['total'] = $this->countContacts($channel_id);
        $chat_ids = [];
        $contact_ids = [];
        foreach ($contacts as $contact) {
            $chat_id = (int)($contact['chat_id'] ?? 0);
            $contact_id = (int)($contact['contact_id'] ?? 0);
            if ($chat_id > 0) {
                $chat_ids[] = $chat_id;
            }
            if ($contact_id > 0) {
                $contact_ids[] = $contact_id;
            }
        }

        $context_counts = $this->contextCounts($channel_id, $chat_ids);
        $local_out_ids = $this->localOutgoingExternalIds($channel_id, $chat_ids);
        $queue_stats = $this->queueStats($chat_ids, $contact_ids);
        $report['queue'] = $queue_stats['summary'];

        if ($report['queue']['recent_failed'] > 0) {
            $this->addRecommendation($recommendations, 'В очереди есть failed-задачи Avito. Проверьте последние ошибки history/avatar jobs.');
        }
        if ($report['contacts']['total'] === 0) {
            $this->addRecommendation($recommendations, 'Локальных Avito-контактов пока нет. Отправьте тестовое сообщение из Авито после подключения webhook.');
        }

        $api_checks_left = 3;
        $avatar_service = new ContactAvatarService($this->registry);
        foreach ($contacts as $contact) {
            $chat_id = (int)($contact['chat_id'] ?? 0);
            $contact_id = (int)($contact['contact_id'] ?? 0);
            $external_chat_id = trim((string)($contact['external_chat_id'] ?? ''));
            $metadata = json_decode((string)($contact['metadata_json'] ?? ''), true);
            $metadata = is_array($metadata) ? $metadata : [];
            $counterparty_author_id = preg_replace('/\D+/', '', (string)($metadata['counterparty_author_id'] ?? $metadata['author_id'] ?? ''));
            $avatar_file = basename((string)($contact['avatar_file'] ?? ''));
            $avatar_file_state = $avatar_file !== '' ? $avatar_service->resolveServableFile($avatar_file) : [];

            $sample = [
                'contact_id' => $contact_id,
                'chat_id' => $chat_id,
                'display_name' => $this->safeDisplayName((string)($contact['display_name'] ?? '')),
                'external_chat_hash' => $external_chat_id !== '' ? MessengerLogger::hashExternalId($external_chat_id) : '',
                'external_chat_present' => $external_chat_id !== '',
                'counterparty_author_id_present' => $counterparty_author_id !== '',
                'item_id_present' => trim((string)($metadata['item_id'] ?? '')) !== '',
                'avatar_file_present' => $avatar_file !== '',
                'avatar_file_exists' => !empty($avatar_file_state),
                'avatar_last_error' => $this->safeDiagnosticText((string)($contact['avatar_last_error'] ?? '')),
                'avatar_checked_at' => (string)($contact['avatar_checked_at'] ?? ''),
                'avatar_updated_at' => (string)($contact['avatar_updated_at'] ?? ''),
                'local_context_count' => (int)($context_counts[$chat_id] ?? 0),
                'local_out_external_count' => isset($local_out_ids[$chat_id]) ? count($local_out_ids[$chat_id]) : 0,
                'api_checked' => false,
                'api_profile_ok' => false,
                'api_avatar_present' => null,
                'api_item_context_present' => null,
                'api_messages_count' => null,
                'api_out_count' => null,
                'history_out_missing_count' => null,
                'history_out_missing_hashes' => [],
                'api_error' => '',
                'queue' => [
                    'history' => $queue_stats['by_chat'][$chat_id] ?? $this->emptyQueueBucket(),
                    'avatar' => $queue_stats['by_contact'][$contact_id] ?? $this->emptyQueueBucket(),
                ],
            ];

            if ($token !== '' && $bot_user_id !== '' && $external_chat_id !== '' && $api_checks_left > 0) {
                $api_checks_left--;
                $sample['api_checked'] = true;
                $profile = $provider->getChatUserInfo($token, $bot_user_id, $external_chat_id, $counterparty_author_id);
                if (!empty($profile['success'])) {
                    $sample['api_profile_ok'] = true;
                    $sample['api_avatar_present'] = trim((string)($profile['avatar_url'] ?? '')) !== '';
                    $sample['api_item_context_present'] = !empty($profile['item_context']);
                    if ($sample['api_avatar_present'] && $report['avatars']['enabled'] && !$sample['avatar_file_exists']) {
                        $this->addRecommendation($recommendations, 'Avito отдает avatar URL, но локальная аватарка еще не подготовлена. Проверьте Cron и очередь messenger_avatar_fetch: EasyChat должен догрузить аватарку фоновым backfill.');
                    }
                    if ($sample['api_item_context_present'] && $sample['local_context_count'] === 0) {
                        $this->addRecommendation($recommendations, 'В карточке Avito есть контекст объявления, но локальный диалог еще не содержит marker/context. Проверьте history sync.');
                    }
                } else {
                    $sample['api_error'] = $this->safeDiagnosticText((string)($profile['error'] ?? 'profile_lookup_failed'));
                }

                $history = $provider->getChatMessages($token, $bot_user_id, $external_chat_id, 50, 0);
                if (!empty($history['success'])) {
                    (new AvitoAccessIncidentService($this->registry))->observeSuccess($channel_id, $contact_id, $chat_id, $external_chat_id);
                    $api_out_ids = [];
                    $api_context_count = 0;
                    $chat_context = !empty($profile['item_context']) && is_array($profile['item_context']) ? $profile['item_context'] : [];
                    foreach (($history['messages'] ?? []) as $message) {
                        if (!is_array($message)) {
                            continue;
                        }
                        $event = $provider->normalizeHistoryMessage($message, $bot_user_id, $external_chat_id, $chat_context);
                        if (!empty($event['item_context'])) {
                            $api_context_count++;
                        }
                        if ((string)($event['direction'] ?? '') === 'out' && trim((string)($event['external_message_id'] ?? '')) !== '') {
                            $api_out_ids[] = (string)$event['external_message_id'];
                        }
                    }
                    $local_ids = $local_out_ids[$chat_id] ?? [];
                    $missing = array_values(array_diff(array_values(array_unique($api_out_ids)), $local_ids));
                    $sample['api_messages_count'] = count($history['messages'] ?? []);
                    $sample['api_out_count'] = count(array_unique($api_out_ids));
                    $sample['history_out_missing_count'] = count($missing);
                    $sample['history_out_missing_hashes'] = array_slice(array_map([MessengerLogger::class, 'hashExternalId'], $missing), 0, 5);
                    if ($api_context_count > 0 && $sample['local_context_count'] === 0) {
                        $this->addRecommendation($recommendations, 'История Avito содержит сообщения с объявлением, но локальный context еще не записан. Дождитесь queue worker или проверьте его cron.');
                    }
                    if ($missing) {
                        $this->addRecommendation($recommendations, 'В истории Avito есть исходящие ответы с avito.ru, которых нет локально. Откройте диалог и проверьте задачу messenger_avito_history_sync.');
                    }
                } else {
                    (new AvitoAccessIncidentService($this->registry))->observeFailure($channel_id, $contact_id, $chat_id, $external_chat_id, $history);
                    $error = $this->safeDiagnosticText((string)($history['error'] ?? 'history_lookup_failed'));
                    $sample['api_error'] = $sample['api_error'] !== '' ? $sample['api_error'] . '; ' . $error : $error;
                }
            }

            $report['contacts']['sample'][] = $sample;
        }

        $report['recommendations'] = array_values($recommendations);
        $report['access_incident'] = (new AvitoAccessIncidentService($this->registry))->summary();
        return $report;
    }

    private function decodeCredentials(array $channel): array {
        $credentials = json_decode((string)($channel['credentials_json'] ?? ''), true);
        return is_array($credentials) ? $credentials : [];
    }

    private function buildWebhookUrl(array $channel): string {
        return HTTP_SERVER . 'index.php?route=messenger/webhook&provider=avito&id=' . urlencode((string)($channel['public_id'] ?? ''));
    }

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

        $client_id = $this->decryptSecretOrPlain((string)($credentials['client_id'] ?? ''));
        $client_secret = $this->decryptSecretOrPlain((string)($credentials['client_secret'] ?? ''));
        if ($client_id === '' || $client_secret === '') {
            return '';
        }

        return json_encode([
            'client_id' => $client_id,
            'client_secret' => $client_secret,
        ], JSON_UNESCAPED_UNICODE);
    }

    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 recentContacts(int $channel_id, int $limit): array {
        if ($channel_id <= 0) {
            return [];
        }
        $limit = max(1, min(10, $limit));
        $query = $this->db->query(
            "SELECT mc.*
             FROM `{$this->prefix}messenger_contacts` mc
             WHERE mc.`channel_id` = " . (int)$channel_id . "
               AND mc.`provider` = 'avito'
             ORDER BY mc.`last_seen_at` DESC, mc.`contact_id` DESC
             LIMIT " . (int)$limit
        );
        return $query->rows ?: [];
    }

    private function countContacts(int $channel_id): int {
        if ($channel_id <= 0) {
            return 0;
        }
        $query = $this->db->query(
            "SELECT COUNT(*) AS total
             FROM `{$this->prefix}messenger_contacts`
             WHERE `channel_id` = " . (int)$channel_id . "
               AND `provider` = 'avito'"
        );
        return (int)($query->row['total'] ?? 0);
    }

    private function contextCounts(int $channel_id, array $chat_ids): array {
        $chat_ids = $this->positiveIds($chat_ids);
        if ($channel_id <= 0 || !$chat_ids) {
            return [];
        }
        $query = $this->db->query(
            "SELECT `chat_id`, COUNT(*) AS total
             FROM `{$this->prefix}messenger_message_contexts`
             WHERE `provider` = 'avito'
               AND `channel_id` = " . (int)$channel_id . "
               AND `chat_id` IN (" . implode(',', $chat_ids) . ")
             GROUP BY `chat_id`"
        );

        $counts = [];
        foreach ($query->rows as $row) {
            $counts[(int)$row['chat_id']] = (int)$row['total'];
        }
        return $counts;
    }

    private function localOutgoingExternalIds(int $channel_id, array $chat_ids): array {
        $chat_ids = $this->positiveIds($chat_ids);
        if ($channel_id <= 0 || !$chat_ids) {
            return [];
        }
        $query = $this->db->query(
            "SELECT `chat_id`, `external_message_id`
             FROM `{$this->prefix}messages`
             WHERE `source_provider` = 'avito'
               AND `source_channel_id` = " . (int)$channel_id . "
               AND `author` IN (2, 3)
               AND `external_message_id` != ''
               AND `chat_id` IN (" . implode(',', $chat_ids) . ")"
        );

        $ids = [];
        foreach ($query->rows as $row) {
            $chat_id = (int)$row['chat_id'];
            if (!isset($ids[$chat_id])) {
                $ids[$chat_id] = [];
            }
            $ids[$chat_id][] = (string)$row['external_message_id'];
        }
        foreach ($ids as $chat_id => $values) {
            $ids[$chat_id] = array_values(array_unique($values));
        }
        return $ids;
    }

    private function queueStats(array $chat_ids, array $contact_ids): array {
        $chat_ids = $this->positiveIds($chat_ids);
        $contact_ids = $this->positiveIds($contact_ids);
        $stats = [
            'summary' => [
                'recent_total' => 0,
                'recent_failed' => 0,
                'types' => [],
            ],
            'by_chat' => [],
            'by_contact' => [],
        ];
        foreach ($chat_ids as $chat_id) {
            $stats['by_chat'][$chat_id] = $this->emptyQueueBucket();
        }
        foreach ($contact_ids as $contact_id) {
            $stats['by_contact'][$contact_id] = $this->emptyQueueBucket();
        }

        try {
            $query = $this->db->query(
                "SELECT `job_type`, `payload`, `status`, `error`, `created_at`
                 FROM `{$this->prefix}queue`
                 WHERE `job_type` IN ('messenger_avito_history_sync','messenger_avatar_fetch')
                 ORDER BY `id` DESC
                 LIMIT 300"
            );
        } catch (\Throwable $e) {
            return $stats;
        }

        foreach ($query->rows as $row) {
            $job_type = (string)($row['job_type'] ?? '');
            $status = (string)($row['status'] ?? '');
            $payload = json_decode((string)($row['payload'] ?? ''), true);
            if (!is_array($payload)) {
                continue;
            }
            if ($job_type === 'messenger_avatar_fetch' && strtolower((string)($payload['provider'] ?? '')) !== 'avito') {
                continue;
            }

            $stats['summary']['recent_total']++;
            if (!isset($stats['summary']['types'][$job_type])) {
                $stats['summary']['types'][$job_type] = ['pending' => 0, 'processing' => 0, 'done' => 0, 'failed' => 0, 'other' => 0];
            }
            $status_key = isset($stats['summary']['types'][$job_type][$status]) ? $status : 'other';
            $stats['summary']['types'][$job_type][$status_key]++;
            if ($status === 'failed') {
                $stats['summary']['recent_failed']++;
            }

            if ($job_type === 'messenger_avito_history_sync') {
                $chat_id = (int)($payload['chat_id'] ?? 0);
                if (isset($stats['by_chat'][$chat_id])) {
                    $this->appendQueueBucket($stats['by_chat'][$chat_id], $status, (string)($row['error'] ?? ''));
                }
            }
            if ($job_type === 'messenger_avatar_fetch') {
                $contact_id = (int)($payload['contact_id'] ?? 0);
                if (isset($stats['by_contact'][$contact_id])) {
                    $this->appendQueueBucket($stats['by_contact'][$contact_id], $status, (string)($row['error'] ?? ''));
                }
            }
        }

        return $stats;
    }

    private function emptyQueueBucket(): array {
        return [
            'total' => 0,
            'pending' => 0,
            'processing' => 0,
            'done' => 0,
            'failed' => 0,
            'last_error' => '',
        ];
    }

    private function appendQueueBucket(array &$bucket, string $status, string $error): void {
        $bucket['total']++;
        if (isset($bucket[$status])) {
            $bucket[$status]++;
        }
        if ($status === 'failed' && $bucket['last_error'] === '') {
            $bucket['last_error'] = $this->safeDiagnosticText($error);
        }
    }

    private function positiveIds(array $ids): array {
        return array_values(array_unique(array_filter(array_map('intval', $ids), static function ($id) {
            return $id > 0;
        })));
    }

    private function subscriptionUrls($value): array {
        $urls = [];
        if (is_array($value)) {
            foreach ($value as $item) {
                foreach ($this->subscriptionUrls($item) as $url) {
                    $urls[] = $url;
                }
            }
            return array_values(array_unique($urls));
        }
        if (is_string($value)) {
            $value = trim($value);
            if (preg_match('/^https?:\/\//i', $value)) {
                $urls[] = $value;
            }
        }
        return array_values(array_unique($urls));
    }

    private function addRecommendation(array &$recommendations, string $message): void {
        if ($message !== '') {
            $recommendations[$message] = $message;
        }
    }

    private function safeDisplayName(string $value): string {
        $normalized = preg_replace('/\s+/u', ' ', $value);
        $value = trim($normalized !== null ? $normalized : $value);
        if ($value === '') {
            return 'Avito contact';
        }
        return mb_substr($value, 0, 80, 'UTF-8');
    }

    private function safeDiagnosticText(string $value): string {
        $value = trim((string)$value);
        if ($value === '') {
            return '';
        }
        $without_urls = preg_replace('/https?:\/\/[^\s]+/iu', '[url]', $value);
        $value = $without_urls !== null ? $without_urls : $value;
        $without_secrets = preg_replace('/(token|secret|client_secret|access_token|refresh_token)=?[^\s,;]*/iu', '$1=[hidden]', $value);
        $value = $without_secrets !== null ? $without_secrets : $value;
        $normalized = preg_replace('/\s+/u', ' ', $value);
        $value = $normalized !== null ? $normalized : $value;
        return mb_substr($value, 0, 180, 'UTF-8');
    }
}
