<?php
namespace EasyChat\Library\Crm;

use EasyChat\Library\Http\ExternalHttpClient;
use EasyChat\Library\Http\PublicUrlGuard;

final class RetailCrmProvider implements CrmProviderInterface {
    private const MAX_RESPONSE_BYTES = 1048576;
    private $http;
    private $guard;

    public function __construct($registry, ?ExternalHttpClient $http = null, ?PublicUrlGuard $guard = null) {
        $this->http = $http ?: new ExternalHttpClient($registry);
        $this->guard = $guard ?: new PublicUrlGuard();
    }

    public function code(): string { return 'retailcrm'; }

    public function capabilities(): array {
        return array(
            'entity_modes'=>array('customer','order'),
            'pipelines'=>false,
            'prepare_fields'=>false,
            'native_chat'=>false,
            'auth_type'=>'api_key',
        );
    }

    public function testConnection(array $connection): array {
        $credentials = $this->call($connection, 'GET', '/api/credentials');
        if (empty($credentials['success'])) return $credentials;
        $sites = $this->call($connection, 'GET', '/api/v5/reference/sites');
        if (empty($sites['success'])) return $sites;
        $available = array();
        foreach ((array)($sites['result']['sites'] ?? array()) as $code => $site) {
            if (!is_array($site)) continue;
            $safe_code = preg_replace('/[^A-Za-z0-9_.-]/', '', (string)($site['code'] ?? $code));
            if ($safe_code === '') continue;
            $available[] = array('code'=>$safe_code, 'name'=>mb_substr((string)($site['name'] ?? $safe_code), 0, 120));
        }
        return array(
            'success'=>true,
            'portal'=>RetailCrmDomain::normalize((string)($connection['base_domain'] ?? '')),
            'sites'=>$available,
        );
    }

    public function syncConversation(array $connection, array $conversation): array {
         
         
         
        if (($conversation['delivery_state'] ?? '') === 'uncertain') {
            return array('success'=>false,'error'=>'retailcrm_delivery_uncertain','retryable'=>false,'outcome_unknown'=>true);
        }
        $site = $this->site((string)($connection['site'] ?? ''));
        if ($site === '') return array('success'=>false,'error'=>'retailcrm_site_required','retryable'=>false);
        $customer_external_id = $this->externalId('EC-CUSTOMER-', (string)($conversation['origin_key'] ?? ''));
        $customer = $this->ensureCustomer($connection, $conversation, $customer_external_id, $site);
        if (empty($customer['success'])) return $customer;
        $customer_id = (int)($customer['customer_id'] ?? 0);

        $mode = ($connection['entity_mode'] ?? 'customer') === 'order' ? 'order' : 'customer';
        $external_id = $customer_id;
        $external_url = 'https://' . RetailCrmDomain::normalize((string)$connection['base_domain']) . '/customers/' . $customer_id . '/edit';
        if ($mode === 'order') {
            $order_external_id = $this->externalId('EC-ORDER-', (string)($conversation['origin_key'] ?? ''));
            $order = $this->ensureOrder($connection, $conversation, $order_external_id, $customer_external_id, $site);
            if (empty($order['success'])) return $order;
            $external_id = (int)($order['order_id'] ?? 0);
            $external_url = 'https://' . RetailCrmDomain::normalize((string)$connection['base_domain']) . '/orders/' . $external_id . '/edit';
        }

        $note = $this->appendCustomerNote($connection, $conversation, $customer_id, $site);
        if (empty($note['success'])) return $note;
        return array(
            'success'=>true,
            'external_id'=>(string)$external_id,
            'external_contact_id'=>(string)$customer_id,
            'external_operation_id'=>(string)($note['note_id'] ?? ''),
            'entity_type'=>$mode,
            'external_url'=>$external_url,
        );
    }

    private function ensureCustomer(array $connection, array $conversation, string $external_id, string $site): array {
        $found = $this->call($connection, 'GET', '/api/v5/customers/' . rawurlencode($external_id), array('by'=>'externalId','site'=>$site));
        if (!empty($found['success']) && !empty($found['result']['customer']['id'])) {
            return array('success'=>true,'customer_id'=>(int)$found['result']['customer']['id']);
        }
        if (($found['http_status'] ?? 0) !== 404) return $found;
        $customer = array(
            'externalId'=>$external_id,
            'firstName'=>mb_substr((string)($conversation['customer_name'] ?? 'Клиент EasyChat'), 0, 100),
            'tags'=>array('EasyChat'),
        );
        $phone = trim((string)($conversation['phone'] ?? ''));
        $email = trim((string)($conversation['email'] ?? ''));
        if ($phone !== '') $customer['phones'] = array(array('number'=>mb_substr($phone, 0, 32)));
        if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) $customer['email'] = mb_substr(strtolower($email), 0, 255);
        if (!empty($connection['responsible_id'])) $customer['managerId'] = (int)$connection['responsible_id'];
        $created = $this->call($connection, 'POST', '/api/v5/customers/create', array('site'=>$site,'customer'=>json_encode($customer, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
        if (empty($created['success'])) return $created;
        $id = (int)($created['result']['id'] ?? 0);
        return $id > 0 ? array('success'=>true,'customer_id'=>$id) : array('success'=>false,'error'=>'retailcrm_invalid_customer_response','retryable'=>false);
    }

    private function ensureOrder(array $connection, array $conversation, string $external_id, string $customer_external_id, string $site): array {
        $found = $this->call($connection, 'GET', '/api/v5/orders/' . rawurlencode($external_id), array('by'=>'externalId','site'=>$site));
        if (!empty($found['success']) && !empty($found['result']['order']['id'])) return array('success'=>true,'order_id'=>(int)$found['result']['order']['id']);
        if (($found['http_status'] ?? 0) !== 404) return $found;
        $summary = trim((string)($conversation['summary'] ?? ''));
        $order = array(
            'externalId'=>$external_id,
            'firstName'=>mb_substr((string)($conversation['customer_name'] ?? 'Клиент EasyChat'), 0, 100),
            'customer'=>array('externalId'=>$customer_external_id),
            'managerComment'=>mb_substr('Обращение из EasyChat · ' . (string)($conversation['source_label'] ?? 'Сайт') . "\n" . $summary . "\n" . (string)($conversation['chat_url'] ?? ''), 0, 4000),
        );
        foreach (array('order_method'=>'orderMethod','order_type'=>'orderType','order_status'=>'status') as $setting => $field) {
            $value = $this->safeCode((string)($connection[$setting] ?? ''));
            if ($value !== '') $order[$field] = $value;
        }
        if (!empty($connection['responsible_id'])) $order['managerId'] = (int)$connection['responsible_id'];
        $created = $this->call($connection, 'POST', '/api/v5/orders/create', array('site'=>$site,'order'=>json_encode($order, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
        if (empty($created['success'])) return $created;
        $id = (int)($created['result']['id'] ?? 0);
        return $id > 0 ? array('success'=>true,'order_id'=>$id) : array('success'=>false,'error'=>'retailcrm_invalid_order_response','retryable'=>false);
    }

    private function appendCustomerNote(array $connection, array $conversation, int $customer_id, string $site): array {
        if ($customer_id <= 0) return array('success'=>false,'error'=>'retailcrm_customer_missing','retryable'=>false);
        $text = trim((string)($conversation['summary'] ?? '')) ?: 'Новое сообщение доступно в EasyChat.';
        $marker = (string)($conversation['message_marker'] ?? '');
        $note = array(
            'text'=>mb_substr((string)($conversation['customer_name'] ?? 'Клиент') . ' · ' . (string)($conversation['source_label'] ?? 'EasyChat') . ":\n" . $text . "\n\nДиалог EasyChat:\n" . (string)($conversation['chat_url'] ?? '') . ($marker !== '' ? "\n\n" . $marker : ''), 0, 10000),
            'customer'=>array('id'=>$customer_id),
        );
        if (!empty($connection['responsible_id'])) $note['managerId'] = (int)$connection['responsible_id'];
        $created = $this->call($connection, 'POST', '/api/v5/customers/notes/create', array('site'=>$site,'note'=>json_encode($note, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
        if (empty($created['success'])) return $created;
        return array('success'=>true,'note_id'=>(string)($created['result']['id'] ?? ''));
    }

    private function call(array $connection, string $method, string $path, ?array $params = null): array {
        $domain = RetailCrmDomain::normalize((string)($connection['base_domain'] ?? ''));
        $api_key = trim((string)($connection['api_key'] ?? ''));
        if ($domain === '' || strlen($api_key) < 16 || strlen($api_key) > 512 || !preg_match('#^/api/[A-Za-z0-9_/?=&%.-]+$#', $path)) return array('success'=>false,'error'=>'retailcrm_credentials_invalid','retryable'=>false);
        $url = 'https://' . $domain . $path;
        if ($method === 'GET' && $params) $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
        $prepared = $this->guard->prepare($url, array('retailcrm.ru'));
        if (empty($prepared['success'])) return array('success'=>false,'error'=>'crm_remote_host_denied','retryable'=>false);
        $buffer = ''; $overflow = false;
        $request = array(
            'headers'=>array('Accept: application/json','X-API-KEY: ' . $api_key,'Content-Type: application/x-www-form-urlencoded'),
            'connect_timeout'=>4,'timeout'=>15,'follow_location'=>false,'trust_profile'=>'retailcrm',
            'curl_options'=>(array)$prepared['curl_options'],
            'write_function'=>function ($ch, string $chunk) use (&$buffer, &$overflow): int { if (strlen($buffer)+strlen($chunk)>self::MAX_RESPONSE_BYTES) { $overflow=true; return 0; } $buffer.=$chunk; return strlen($chunk); },
        );
        if ($method === 'POST') $request['body'] = http_build_query($params ?: array(), '', '&', PHP_QUERY_RFC3986);
        $result = $this->http->request($method, $url, $request);
        $http = (int)($result['http_code'] ?? 0);
        $json = !$overflow && $buffer !== '' ? json_decode($buffer, true) : array();
        $json = is_array($json) ? $json : array();
        if (!$overflow && !empty($result['transport_ok']) && $http >= 200 && $http < 300 && !empty($json['success'])) return array('success'=>true,'result'=>$json,'http_status'=>$http);
        $code = preg_replace('/[^A-Za-z0-9_.-]/', '', (string)($json['errorMsg'] ?? $json['error'] ?? ''));
        return array('success'=>false,'error'=>$overflow?'crm_response_too_large':($code?:'retailcrm_http_'.$http),'retryable'=>$http===0||$http===429||$http>=500,'outcome_unknown'=>$method==='POST'&&($http===0||$http>=500),'http_status'=>$http);
    }

    private function externalId(string $prefix, string $origin): string { return $prefix . strtoupper(substr(hash('sha256', $origin), 0, 24)); }
    private function site(string $value): string { return $this->safeCode($value); }
    private function safeCode(string $value): string { return preg_match('/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/', $value) ? $value : ''; }
}
