<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Library\AppClock;





class MaxPersonalIncidentService {
    const AUTHORIZATION_REVOKED = 'personal_authorization_revoked';
    const SESSION_STORAGE_UNAVAILABLE = 'personal_session_storage_unavailable';
    const DATABASE_UNAVAILABLE = 'personal_database_unavailable';

    private $db;
    private $prefix;
    private $clock;
    private $spool;

    public function __construct($registry) {
        $this->db = $registry->get('db');
        $this->prefix = easychat_db_prefix();
        $this->clock = new AppClock($registry);
        try {
            $this->spool = new MaxOperationalSpool();
        } catch (\Throwable $e) {
             
             
            $this->spool = null;
        }
    }

    public function observeFailure(array $channel, string $category, array $settings): bool {
        $channel_id = (int)($channel['channel_id'] ?? 0);
        if ($channel_id <= 0 || (string)($channel['status'] ?? '') !== 'active') {
            return false;
        }
        $code = $category === 'authorization'
            ? self::AUTHORIZATION_REVOKED
            : ($category === 'storage'
                ? self::SESSION_STORAGE_UNAVAILABLE
                : ($category === 'database' ? self::DATABASE_UNAVAILABLE : ''));
        if ($code === '') {
            return false;
        }

         
         
        if ($code === self::AUTHORIZATION_REVOKED && max(0, (int)($settings['polling_api_last_ok_ts'] ?? 0)) <= 0) {
            return false;
        }
         
         
        if ($code === self::DATABASE_UNAVAILABLE && max(0, (int)($settings['polling_error_count'] ?? 0)) < 2) {
            return false;
        }

        $generation = max(0, (int)($settings['polling_session_generation'] ?? $settings['session_generation'] ?? 0));
        $occurred_at = time();
        try {
            $this->drainSpool();
            $this->persistFailure($channel_id, $code, $occurred_at);
        } catch (\Throwable $e) {
            $this->appendSpool($channel_id, $generation, $category, $occurred_at);
            return false;
        }
        return true;
    }

    public function observeSuccess(int $channel_id): void {
        $this->resolve($channel_id);
    }

    public function resolve(int $channel_id): void {
        if ($channel_id <= 0) {
            return;
        }
        $now = $this->clock->nowSql();
        try {
            $this->drainSpool();
            $this->db->query(
                "UPDATE `{$this->prefix}messenger_provider_incidents`
             SET `status` = 'resolved', `resolved_at` = '" . $this->db->escape($now) . "'
                 WHERE `provider` = 'max' AND `channel_id` = " . (int)$channel_id . " AND `status` = 'active'"
            );
        } catch (\Throwable $e) {
             
        }
    }

    public function summary(): array {
        $this->drainSpool();
        $query = $this->db->query(
            "SELECT `incident_id`, `incident_code`, `first_seen_at`, `last_seen_at`, `occurrence_count`
             FROM `{$this->prefix}messenger_provider_incidents`
             WHERE `provider` = 'max' AND `status` = 'active'
             ORDER BY `last_seen_at` DESC, `incident_id` DESC LIMIT 1"
        );
        $row = (array)($query->row ?? []);
        if (!$row) {
            return ['active' => false, 'count' => 0, 'key' => ''];
        }
        $code = (string)($row['incident_code'] ?? '');
        $storage = $code === self::SESSION_STORAGE_UNAVAILABLE;
        $database = $code === self::DATABASE_UNAVAILABLE;
        return [
            'active' => true,
            'count' => 1,
            'key' => 'max:' . (int)($row['incident_id'] ?? 0) . ':' . (string)($row['last_seen_at'] ?? ''),
            'code' => $code,
            'level' => 'error',
            'title' => $storage ? 'Сессия MAX недоступна' : ($database ? 'Хостинг временно не принимает сообщения' : 'MAX отозвал авторизацию'),
            'detail' => $storage
                ? 'EasyChat не может прочитать защищенную сессию. Проверьте права на папку storage.'
                : ($database
                    ? 'Входящие сообщения защищены и будут записаны после восстановления базы данных.'
                    : 'Ранее работавшая сессия больше не принимается. Новые сообщения временно не поступают.'),
            'first_seen_at' => (string)($row['first_seen_at'] ?? ''),
            'last_seen_at' => (string)($row['last_seen_at'] ?? ''),
            'occurrence_count' => max(1, (int)($row['occurrence_count'] ?? 1)),
            'action' => ($storage || $database) ? 'review' : 'reconnect',
        ];
    }

    private function persistFailure(int $channel_id, string $code, int $occurred_at): void {
        $when = $this->clock->fromUnixTimestampSql($occurred_at);
        if ($when === '') {
            $when = $this->clock->nowSql();
        }
        $scope_hash = hash('sha256', 'max-personal-channel:' . $channel_id);
        $table = $this->prefix . 'messenger_provider_incidents';
        $this->db->query(
            "INSERT INTO `{$table}` SET
            `provider` = 'max', `channel_id` = {$channel_id}, `contact_id` = NULL, `chat_id` = NULL,
            `incident_code` = '" . $this->db->escape($code) . "',
            `scope_hash` = '" . $this->db->escape($scope_hash) . "',
            `status` = 'active', `http_code` = 0,
            `first_seen_at` = '" . $this->db->escape($when) . "',
            `last_seen_at` = '" . $this->db->escape($when) . "', `resolved_at` = NULL, `occurrence_count` = 1
         ON DUPLICATE KEY UPDATE `status` = 'active', `last_seen_at` = VALUES(`last_seen_at`),
                `resolved_at` = NULL, `occurrence_count` = `occurrence_count` + 1"
        );
    }

    private function drainSpool(): void {
        if (!$this->spool instanceof MaxOperationalSpool) {
            return;
        }
        $this->spool->drain(function (array $event): bool {
             
             
             
             
            if ($event['category'] === 'authorization'
                && $this->belongsToOlderGeneration((int)$event['channel_id'], (int)$event['generation'])) {
                return true;
            }
            $code = $event['category'] === 'authorization'
                ? self::AUTHORIZATION_REVOKED
                : ($event['category'] === 'storage'
                    ? self::SESSION_STORAGE_UNAVAILABLE
                    : self::DATABASE_UNAVAILABLE);
            $this->persistFailure((int)$event['channel_id'], $code, (int)$event['occurred_at']);
            return true;
        });
    }

    private function belongsToOlderGeneration(int $channel_id, int $event_generation): bool {
        if ($event_generation <= 0) {
            return false;
        }
        $query = $this->db->query(
            "SELECT `settings_json` FROM `{$this->prefix}messenger_channels`
             WHERE `provider` = 'max' AND `channel_id` = " . (int)$channel_id . " LIMIT 1"
        );
        $row = (array)($query->row ?? []);
        $settings = json_decode((string)($row['settings_json'] ?? ''), true);
        $current_generation = is_array($settings)
            ? max(0, (int)($settings['session_generation'] ?? 0))
            : 0;
        return $current_generation > $event_generation;
    }

    private function appendSpool(int $channel_id, int $generation, string $category, int $occurred_at): void {
        if (!$this->spool instanceof MaxOperationalSpool) {
            return;
        }
        try {
            $this->spool->append($channel_id, $generation, $category, $occurred_at);
        } catch (\Throwable $e) {
             
             
        }
    }
}
