<?php
namespace EasyChat\Library\Messenger\Marketplace;

use EasyChat\Library\AppClock;










final class WildberriesHistoryRepairService {
    private $registry;
    private $db;
    private $prefix;

    public function __construct($registry) {
        $this->registry = $registry;
        $this->db = $registry->get('db');
        $this->prefix = function_exists('easychat_db_prefix') ? easychat_db_prefix() : (defined('DB_PREFIX') ? DB_PREFIX : '');
    }

    public function run(string $received_from, string $received_to, int $channel_id = 0, bool $apply = false): array {
        if (!$this->validSqlDate($received_from) || !$this->validSqlDate($received_to) || strcmp($received_from, $received_to) > 0) {
            return ['success' => false, 'error' => 'repair_window_invalid'];
        }
        $from = $this->db->escape($received_from);
        $to = $this->db->escape($received_to);
        $channel_filter = $channel_id > 0 ? ' AND c.`channel_id` = ' . $channel_id : '';
        $query = $this->db->query(
            "SELECT m.`message_id`, m.`chat_id`, m.`external_message_id`,
                    m.`date_added` AS `message_date`,
                    d.`delivery_id`, d.`date_added` AS `received_at`,
                    t.`thread_id`, t.`conversation_kind`, t.`external_thread_id`,
                    t.`snapshot_json`
             FROM `{$this->prefix}messages` m
             INNER JOIN `{$this->prefix}messenger_deliveries` d ON d.`message_id` = m.`message_id`
             INNER JOIN `{$this->prefix}messenger_channels` c ON c.`channel_id` = d.`channel_id`
             INNER JOIN `{$this->prefix}marketplace_threads` t
                ON t.`channel_id` = c.`channel_id` AND t.`chat_id` = m.`chat_id`
             WHERE c.`provider` = 'wildberries'
               AND m.`source_provider` = 'wildberries'
               AND m.`author` = 1
               AND d.`date_added` BETWEEN '{$from}' AND '{$to}'
               {$channel_filter}
             ORDER BY m.`message_id` ASC"
        );

        $summary = [
            'success' => true,
            'mode' => $apply ? 'apply' : 'dry-run',
            'scanned' => 0,
            'eligible' => 0,
            'changed' => 0,
            'unchanged' => 0,
            'skipped' => [],
            'affected_chats' => 0,
            'oldest_provider_event' => '',
            'newest_provider_event' => '',
        ];
        $changes = [];
        foreach (($query->rows ?: []) as $row) {
            $summary['scanned']++;
            $candidate = $this->candidate($row);
            if (empty($candidate['success'])) {
                $reason = (string)($candidate['reason'] ?? 'not_eligible');
                $summary['skipped'][$reason] = (int)($summary['skipped'][$reason] ?? 0) + 1;
                continue;
            }
            $summary['eligible']++;
            if ((string)$row['message_date'] === (string)$candidate['date_sql']) {
                $summary['unchanged']++;
                continue;
            }
            $changes[] = $candidate + [
                'message_id' => (int)$row['message_id'],
                'delivery_id' => (int)$row['delivery_id'],
                'thread_id' => (int)$row['thread_id'],
                'chat_id' => (int)$row['chat_id'],
            ];
            $event_date = (string)$candidate['date_sql'];
            if ($summary['oldest_provider_event'] === '' || strcmp($event_date, $summary['oldest_provider_event']) < 0) {
                $summary['oldest_provider_event'] = $event_date;
            }
            if ($summary['newest_provider_event'] === '' || strcmp($event_date, $summary['newest_provider_event']) > 0) {
                $summary['newest_provider_event'] = $event_date;
            }
        }
        $summary['changed'] = count($changes);

        if (!$apply || !$changes) {
            $summary['affected_chats'] = count(array_unique(array_column($changes, 'chat_id')));
            return $summary;
        }

        $chat_ids = [];
        $thread_dates = [];
        $this->db->query('START TRANSACTION');
        try {
            foreach ($changes as $change) {
                $date = $this->db->escape((string)$change['date_sql']);
                $this->db->query(
                    "UPDATE `{$this->prefix}messages`
                     SET `date_added` = '{$date}', `status_admin` = 1
                     WHERE `message_id` = " . (int)$change['message_id'] . "
                       AND `source_provider` = 'wildberries' AND `author` = 1"
                );
                $this->db->query(
                    "UPDATE `{$this->prefix}messenger_deliveries`
                     SET `provider_occurred_at` = '{$date}',
                         `ingest_mode` = 'reconciliation',
                         `date_modified` = NOW()
                     WHERE `delivery_id` = " . (int)$change['delivery_id']
                );
                $chat_ids[(int)$change['chat_id']] = (int)$change['chat_id'];
                $thread_id = (int)$change['thread_id'];
                if (!isset($thread_dates[$thread_id]) || strcmp((string)$change['date_sql'], $thread_dates[$thread_id]) > 0) {
                    $thread_dates[$thread_id] = (string)$change['date_sql'];
                }
            }
            foreach ($thread_dates as $thread_id => $thread_date) {
                $this->db->query(
                    "UPDATE `{$this->prefix}marketplace_threads`
                     SET `last_external_at` = '" . $this->db->escape($thread_date) . "',
                         `date_modified` = NOW()
                     WHERE `thread_id` = " . (int)$thread_id
                );
            }
            foreach ($chat_ids as $chat_id) {
                $this->db->query(
                    "UPDATE `{$this->prefix}sessions`
                     SET `date_modified` = COALESCE(
                            (SELECT MAX(m.`date_added`) FROM `{$this->prefix}messages` m WHERE m.`chat_id` = {$chat_id}),
                            `date_modified`
                         )
                     WHERE `chat_id` = {$chat_id}"
                );
            }
            $this->db->query('COMMIT');
        } catch (\Throwable $error) {
            $this->db->query('ROLLBACK');
            return ['success' => false, 'error' => 'repair_transaction_failed'];
        }

        $summary['affected_chats'] = count($chat_ids);
        return $summary;
    }

    private function candidate(array $row): array {
        $snapshot = json_decode((string)($row['snapshot_json'] ?? ''), true);
        $payload = is_array($snapshot['provider_payload'] ?? null) ? $snapshot['provider_payload'] : [];
        if (!$payload) return ['success' => false, 'reason' => 'snapshot_missing'];

        $kind = (string)($row['conversation_kind'] ?? '');
        $external_thread_id = (string)($row['external_thread_id'] ?? '');
        $external_message_id = (string)($row['external_message_id'] ?? '');
        $raw_date = '';

        if ($kind === MarketplaceStream::QUESTION
            && hash_equals('question:' . $external_thread_id, $external_message_id)
            && hash_equals($external_thread_id, (string)($payload['id'] ?? ''))) {
            $raw_date = (string)($payload['createdDate'] ?? '');
        } elseif ($kind === MarketplaceStream::REVIEW
            && hash_equals('review:' . $external_thread_id, $external_message_id)
            && hash_equals($external_thread_id, (string)($payload['id'] ?? ''))) {
            $raw_date = (string)($payload['createdDate'] ?? '');
        } elseif ($kind === MarketplaceStream::RETURN_CLAIM
            && hash_equals($external_thread_id, (string)($payload['id'] ?? ''))
            && strpos($external_message_id, 'return:' . $external_thread_id . ':') === 0) {
            $raw_date = (string)($payload['dt_update'] ?? $payload['dt'] ?? '');
        } elseif ($kind === MarketplaceStream::BUYER_CHAT
            && hash_equals($external_message_id, (string)($payload['eventID'] ?? ''))) {
            $raw_date = (string)($payload['addTimestamp'] ?? $payload['addTime'] ?? '');
        } else {
            return ['success' => false, 'reason' => 'identity_mismatch'];
        }

        $unix = ProviderTimestamp::unix($raw_date);
        if ($unix <= 0) return ['success' => false, 'reason' => 'provider_date_invalid'];
        $clock = new AppClock($this->registry);
        $message_unix = $clock->sqlToTimestamp((string)($row['message_date'] ?? ''));
        $received_unix = $clock->sqlToTimestamp((string)($row['received_at'] ?? ''));
        if ($message_unix === false || $received_unix === false || abs($message_unix - $received_unix) > 900) {
            return ['success' => false, 'reason' => 'not_bug_signature'];
        }
        if ($unix > ((int)$received_unix + 300)) {
            return ['success' => false, 'reason' => 'provider_date_in_future'];
        }
        $date_sql = $clock->fromUnixTimestampSql($unix);
        return $date_sql !== ''
            ? ['success' => true, 'date_sql' => $date_sql]
            : ['success' => false, 'reason' => 'provider_date_invalid'];
    }

    private function validSqlDate(string $value): bool {
        return preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value) === 1;
    }
}
