<?php
namespace EasyChat\Library\Commerce;

final class OpenCartCommerceContextProvider extends AbstractCommerceContextProvider
{
    public function capabilities(): array
    {
        return array('customer' => true, 'orders' => true, 'order_items' => true, 'read_only' => true);
    }

    public function resolveCustomer(array $lookup): array
    {
        $emails = $this->emails($lookup);
        $phones = $this->phones($lookup);
        if (!$emails && !$phones) {
            return $this->unresolved('not_found');
        }
        $conditions = array();
        if ($emails) {
            $conditions[] = "`email` IN (" . $this->quoted($emails) . ")";
        }
        if ($phones) {
            $conditions[] = "`telephone` IN (" . $this->quoted($phones) . ")";
        }
        try {
            $rows = $this->rows($this->db->query(
                "SELECT `customer_id`,`email`,`telephone` FROM `{$this->prefix}customer`
                 WHERE " . implode(' OR ', $conditions) . " ORDER BY `customer_id` DESC LIMIT 3"
            ));
            $ids = array_values(array_unique(array_filter(array_map(function ($row) {
                return (int)($row['customer_id'] ?? 0);
            }, $rows))));
            if (count($ids) === 1) {
                return $this->matched((string)$ids[0], 'customer_id', $this->evidence($rows[0] ?? array(), $emails, $phones));
            }
            if (count($ids) > 1) {
                return $this->unresolved('ambiguous');
            }

             
             
            $order_conditions = array();
            if ($emails) {
                $order_conditions[] = "`email` IN (" . $this->quoted($emails) . ")";
            }
            if ($phones) {
                $order_conditions[] = "`telephone` IN (" . $this->quoted($phones) . ")";
            }
            $guest = $this->rows($this->db->query(
                "SELECT `email`,`telephone` FROM `{$this->prefix}order`
                 WHERE `customer_id` = 0 AND `order_status_id` > 0 AND `store_id` = " . $this->storeId() . "
                   AND (" . implode(' OR ', $order_conditions) . ")
                 ORDER BY `order_id` DESC LIMIT 3"
            ));
            if (!$guest) {
                return $this->unresolved('not_found');
            }
            $evidence = $this->evidence($guest[0], $emails, $phones);
            $value = $evidence === 'email' ? mb_strtolower((string)$guest[0]['email'], 'UTF-8') : (string)$guest[0]['telephone'];
            return $this->matched(hash('sha256', $evidence . ':' . $value), 'guest_contact', $evidence, array(
                'contact_type' => $evidence,
                'contact_value' => $value,
            ));
        } catch (\Throwable $e) {
            return $this->unresolved('unavailable');
        }
    }

    public function findOrders(array $reference, int $limit = 3): array
    {
        $limit = $this->limit($limit);
        $ownership = $this->ownershipSql($reference);
        if ($ownership === '') {
            return array();
        }
        $where = 'o.`store_id` = ' . $this->storeId() . ' AND (' . $ownership . ')';
        if (!empty($reference['order_id'])) {
            $where .= " AND o.`order_id` = " . (int)$reference['order_id'];
        }
        try {
            $language_id = $this->languageId();
            $shipping_code = $this->hasOrderColumn('shipping_code') ? 'o.`shipping_code`' : "'' AS `shipping_code`";
            $orders = $this->rows($this->db->query(
                "SELECT o.`order_id`,o.`date_added`,o.`date_modified`,o.`total`,o.`currency_code`,o.`currency_value`,
                        o.`payment_method`,o.`shipping_method`,o.`tracking`,{$shipping_code},o.`order_status_id`,
                        COALESCE(os.`name`, '') AS `status_name`
                 FROM `{$this->prefix}order` o
                 LEFT JOIN `{$this->prefix}order_status` os
                   ON os.`order_status_id` = o.`order_status_id` AND os.`language_id` = {$language_id}
                 WHERE o.`order_status_id` > 0 AND {$where}
                 ORDER BY o.`date_added` DESC,o.`order_id` DESC LIMIT {$limit}"
            ));
            if (!$orders) {
                return array();
            }
            $ids = array_map(function ($row) { return (int)$row['order_id']; }, $orders);
            $items = $this->groupItems($this->rows($this->db->query(
                "SELECT `order_id`,`product_id`,`name`,`quantity` FROM `{$this->prefix}order_product`
                 WHERE `order_id` IN (" . implode(',', $ids) . ") ORDER BY `order_product_id` ASC"
            )));
            $shipments = $this->apiShipShipmentsForOrders($ids);
            return array_map(function ($row) use ($items, $shipments) {
                $id = (string)$row['order_id'];
                $value = (float)($row['currency_value'] ?? 1);
                if ($value <= 0) $value = 1;
                return array(
                    'id' => $id,
                    'number' => $id,
                    'date' => (string)$row['date_added'],
                    'updated_at' => (string)($row['date_modified'] ?? $row['date_added']),
                    'status' => $this->boundedText($row['status_name'] ?: ('Статус ' . $row['order_status_id']), 80),
                    'total' => $this->money((float)$row['total'] * $value, (string)$row['currency_code']),
                    'payment' => $this->methodLabel($row['payment_method'] ?? ''),
                    'delivery' => $this->methodLabel($row['shipping_method'] ?? ''),
                    'payment_status' => 'unknown',
                    'refund_status' => 'unknown',
                    'fulfillment_status' => $this->fulfillmentStatus((string)($row['status_name'] ?? '')),
                    'shipments' => $this->mergeNativeShipment($row, $shipments[$id] ?? array()),
                    'items' => array_slice($items[$id] ?? array(), 0, 20),
                );
            }, $orders);
        } catch (\Throwable $e) {
            return array();
        }
    }

    private function hasOrderColumn(string $column): bool
    {
        try {
            return (bool)$this->row($this->db->query("SHOW COLUMNS FROM `{$this->prefix}order` LIKE '" . $this->db->escape($column) . "'"));
        } catch (\Throwable $e) { return false; }
    }

    private function mergeNativeShipment(array $row, array $confirmed): array
    {
        if ($confirmed) return $confirmed;
        $method = $row['shipping_method'] ?? '';
        $code = $row['shipping_code'] ?? '';
        if (is_string($method) && strlen($method) > 1 && ($method[0] === '{' || $method[0] === '[')) {
            $decoded = json_decode($method, true);
            if (is_array($decoded)) {
                $method = $decoded['name'] ?? $decoded['title'] ?? '';
                $code = $decoded['code'] ?? $code;
            }
        }
        $resolver = new OpenCartShippingResolver();
        $settings_mapping = $resolver->mappingFromSettings($this->settings['opencart_shipping_mapping'] ?? array());
        $resolved = $resolver->resolve($code, $method, $settings_mapping);
        $tracking = trim((string)($row['tracking'] ?? ''));
        if ($tracking === '') return array();
         
         
        if (($resolved['evidence'] ?? '') === 'admin_ignored') return array();
         
         
        if (($resolved['provider'] ?? '') === '' || ($resolved['confidence'] ?? 0) < 0.95) {
            return array(array(
                'carrier' => '', 'carrier_code' => '', 'tracking_number' => $tracking,
                'status' => 'unknown', 'status_label' => '',
                'updated_at' => (string)($row['date_modified'] ?? $row['date_added'] ?? ''),
                'source' => 'opencart_order', 'source_evidence' => 'unresolved_shipping_method',
            ));
        }
        return array(array(
            'carrier' => $resolved['carrier'], 'carrier_code' => $resolved['provider'],
            'tracking_number' => $tracking, 'status' => 'unknown', 'status_label' => '',
            'updated_at' => (string)($row['date_modified'] ?? $row['date_added'] ?? ''),
            'source' => 'opencart_order', 'source_evidence' => $resolved['evidence'],
        ));
    }

    





    private function apiShipShipmentsForOrders(array $ids): array
    {
        if (!$ids) return array();
        try {
            $table = $this->prefix . 'apiship_order';
            if (!$this->row($this->db->query("SHOW TABLES LIKE '" . $this->db->escape($table) . "'"))) return array();
            $prefix_row = $this->row($this->db->query(
                "SELECT `value` FROM `{$this->prefix}setting` WHERE `key`='shipping_apiship_prefix' AND `store_id`=" . $this->storeId() . " ORDER BY `setting_id` DESC LIMIT 1"
            ));
            $client_prefix = (string)($prefix_row['value'] ?? '');
            if (!preg_match('/^[A-Za-z0-9_.:-]{0,64}$/', $client_prefix)) return array();
            $rows = $this->rows($this->db->query(
                "SELECT `oc_order_id`,`apiship_order_id` FROM `{$table}` WHERE `oc_order_id` IN (" . implode(',', array_map('intval', $ids)) . ")"
            ));
            $result = array();
            foreach ($rows as $row) {
                $order_id = (string)(int)($row['oc_order_id'] ?? 0);
                $shipment = ApiShipShipment::fromOfficialModule($client_prefix . $order_id, (string)($row['apiship_order_id'] ?? ''), '', 'apiship_opencart');
                if ($shipment) $result[$order_id][] = $shipment;
            }
            return $result;
        } catch (\Throwable $e) {
            return array();
        }
    }

    public function health(): array
    {
        return array(
            'ready' => true,
            'schema' => 'opencart_1_5_to_4',
            'index_policy' => 'native_or_diagnostic',
            'recommended' => array('customer.email', 'order.customer_id', 'order.email', 'order_product.order_id'),
        );
    }

    private function ownershipSql(array $reference): string
    {
        if (($reference['kind'] ?? '') === 'customer_id' && (int)($reference['id'] ?? 0) > 0) {
            return "o.`customer_id` = " . (int)$reference['id'];
        }
        if (($reference['kind'] ?? '') === 'guest_contact') {
            $column = ($reference['contact_type'] ?? '') === 'email' ? 'email' : 'telephone';
            $value = trim((string)($reference['contact_value'] ?? ''));
            if ($value !== '') {
                return "o.`customer_id` = 0 AND o.`{$column}` = '" . $this->db->escape($column === 'email' ? mb_strtolower($value, 'UTF-8') : $value) . "'";
            }
        }
        return '';
    }

    private function evidence(array $row, array $emails, array $phones): string
    {
        return in_array(mb_strtolower(trim((string)($row['email'] ?? '')), 'UTF-8'), $emails, true) ? 'email' : 'phone';
    }

    private function languageId(): int
    {
        try {
            $row = $this->row($this->db->query("SELECT `language_id` FROM `{$this->prefix}language` WHERE `status`=1 ORDER BY `sort_order`,`language_id` LIMIT 1"));
            return max(1, (int)($row['language_id'] ?? 1));
        } catch (\Throwable $e) {
            return 1;
        }
    }

    private function storeId(): int
    {
        $host = strtolower(preg_replace('/:\d+$/', '', (string)(\EasyChat\Library\RequestContext::server()['HTTP_HOST'] ?? '')));
        if ($host === '') return 0;
        try {
            $rows = $this->rows($this->db->query("SELECT `store_id`,`url`,`ssl` FROM `{$this->prefix}store` ORDER BY `store_id`"));
            foreach ($rows as $row) {
                foreach (array('url','ssl') as $key) {
                    if (strtolower((string)parse_url((string)($row[$key] ?? ''), PHP_URL_HOST)) === $host) return (int)$row['store_id'];
                }
            }
        } catch (\Throwable $e) {}
        return 0;
    }

    private function methodLabel($value): string
    {
        if (is_string($value) && strlen($value) > 1 && ($value[0] === '{' || $value[0] === '[')) {
            $decoded = json_decode($value, true);
            if (is_array($decoded)) {
                $value = $decoded['name'] ?? $decoded['title'] ?? $decoded['code'] ?? '';
            }
        }
        return $this->boundedText($value, 100);
    }

    private function fulfillmentStatus(string $status): string
    {
        $status = mb_strtolower($status, 'UTF-8');
        if (preg_match('/достав|выполн|заверш/u', $status)) return 'delivered';
        if (preg_match('/отправ|передан.*достав/u', $status)) return 'shipped';
        if (preg_match('/готов/u', $status)) return 'ready';
        if (preg_match('/отмен|возврат/u', $status)) return 'cancelled';
        if (preg_match('/обработ|сбор/u', $status)) return 'processing';
        return 'unknown';
    }
}
