<?php

namespace Modules\PrintSuppliers\Controllers;

use Exception;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Config;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\PrintSuppliers\Models\Order;
use Modules\PrintSuppliers\Models\OrderItem;
use Modules\PrintSuppliers\Models\Shipper;
use Modules\PrintSuppliers\Models\OrderMeta;
use Modules\PrintSuppliers\Models\MerchizeProduct;
use Modules\PrintSuppliers\Models\MerchizeProductVariant;

class MerchizeController extends Controller
{
    private $apiUrl;

    public function __construct()
    {
        $this->apiUrl = Config::get('print-suppliers::connection.merchize.api_url', 'https://bo-5mjd36b.merchize.com/bo-api');
    }

    public function autoFulfill(Request $request)
    {
        $responseData = [
            'status' => self::STATUS_FAIL,
            'msg' => 'order_id is required!'
        ];

        $orderId = $request->input('order_id');

        if ($orderId) {

            try {

                $order = $this->findOrderDetail($orderId);

                if ($order && (!isset($order['resource_id']) || !$order['resource_id'])) {

                    $lineItems = [];
                    $itemIds = [];
                    foreach ($order['items'] as $orderItem) {
                        $itemIds[] = $orderItem['id'];
                        $item = $this->buildLineItem($orderItem);
                        if ($item) {
                            $lineItems[] = $item;
                        }
                    }

                    if (count($order['items']) == count($lineItems)) {

                        $orderSchema = $this->getFulfillOrderSchema($order);
                        $orderSchema['items'] = $lineItems;

                        $fulfillResponse = $this->fulfill($order, $orderSchema);

                        $responseData = [
                            'result' => $fulfillResponse
                        ];

                        if ($fulfillResponse !== false) {

                            if (isset($fulfillResponse['data']['_id'])) {

                                $responseData['status'] = self::STATUS_SUCCESSFUL;

                                OrderItem::query()->whereIn('id', $itemIds)
                                    ->update([
                                        'printing_code' => 'merchize'
                                    ]);

                                // update resource_id
                                $objectOrder = Order::query()->find($order['id']);
                                $objectOrder->resource_id = $fulfillResponse['data']['_id'];
                                $objectOrder->save();

                            }
                        }

                    }  else {
                        $responseData = [
                            'status' => self::STATUS_FAIL,
                            'msg' => 'Không build được đủ line items'
                        ];
                    }

                } else {
                    $msg = 'Không tìm thấy chi tiết đơn Printerval hoặc đơn đã được fulfill, kiểm tra resource_id trong đơn hàng!';
                    if (isset($order['resource_id']) && $order['resource_id']) {
                        $msg = 'Đơn fullfill trùng lặp!';
                    } elseif (!$order) {
                        $msg = 'Không tìm thấy chi tiết đơn Printerval';
                    }

                    $responseData = [
                        'status' => self::STATUS_FAIL,
                        'msg' => $msg
                    ];
                }

            } catch (Exception $ex) {
                $errorMsg = 'Error on line '. $ex->getLine().' in '.$ex->getFile() .': '.$ex->getMessage();
                $responseData = [
                    'status' => self::STATUS_FAIL,
                    'msg' => $errorMsg
                ];
            }
        }

        return $responseData;
    }

    public function fulfillOrder(Request $request)
    {
        $orderSchema = $request->input('merchize_order_data');

        list($shopId, $apiToken) = $this->getCredential('printerval', 'merchize');

        if ($orderSchema && $shopId && $apiToken) {
            
            $client = new Client([
                'headers' => [
                    'x-api-key' => $apiToken,
                    'Content-Type' => 'application/json'
                ]
            ]);

            $httpResponse = $client->post('https://bo-5mjd36b.merchize.com/bo-api/order/external/orders', [
                'body' => json_encode($orderSchema, JSON_UNESCAPED_UNICODE)
            ]);

            $merchizeRes = json_decode($httpResponse->getBody(), true);

            if ($merchizeRes) return $merchizeRes;
        }

        return [
            'status' => 'fail'
        ];
    }

    public function manualUpdateOrderTracking(Request $request)
    {
        $orderCode = $request->input('code');
        $order = Order::query()->where('code', $orderCode)->first(); 

        if ($order) {

            $trackingData = $this->getMerchizeOrderTracking($order->code, 'external_number');

            if ($trackingData && count($trackingData)) {
                $shipments = $this->updateShipments($trackingData, $order);
                return $shipments;
            }
        }   
    }

    public function merchizeWebhook(Request $request)
    {
        $webhookPayload = $request->all();
        $retVal['status'] = 'fail';

        if (isset($webhookPayload['resource']['external_number'])) {

            $sysOrderCode = $webhookPayload['resource']['external_number'];
            $statusMapping = [
                "in_production_pending" => "ISSUED",
                "in_production_done" => "ISSUED",
                "shipment_started_pending" => "READY_TO_SHIP",
                "shipment_started_done" => "DELIVERING",
                "delivered_done" => "FINISHED",
            ];
            
            // in_production, shipment_started, delivered
            $newStatus = null;
            if (isset($webhookPayload['resource']['order_progress'])) {
                $merchizeOrderProcess = $webhookPayload['resource']['order_progress'];
                foreach ($merchizeOrderProcess as $process) {
                    $statusKey = $process['event'] . '_' . $process['status'];
                    if (isset($statusMapping[$statusKey])) {
                        $newStatus = $statusMapping[$statusKey];
                    }
                }
            }

            if (substr($sysOrderCode, 0, 4) === "API_") {
                $sysOrderCode = substr($sysOrderCode, 4);
            } else if (substr($sysOrderCode, 0, 3) === "API") {
                $sysOrderCode = substr($sysOrderCode, 3);
            }
            
            $originOrder = Order::query()
                ->where('code', '=', $sysOrderCode)
                ->whereNotIn('status', ['CANCELED', 'REQUEST_RETURN', 'RETURNED'])
                ->where('payment_status', '=', 'PAID')
                ->orderBy('id', 'DESC')
                ->first();

            if ($originOrder) {

                $this->_updateOrder($originOrder, $webhookPayload, $newStatus);

                $retVal['status'] = 'successful';
                $retVal['mapped_status'] = $newStatus;

            } else {
                $retVal['status'] = 'fail';
                $retVal['message'] = 'Order Not Found.';
            }

        } else {
            $retVal['status'] = 'fail';
            $retVal['message'] = 'Empty merchize ex ID & DATA.';
        }

        return response()->json($retVal, 200, [], JSON_NUMERIC_CHECK);

    }

    protected function _updateOrder($order, $webhookPayload, $newStatus = null)
    {
        // update status and tracking code
        $dataUpdate = [];

        if ($newStatus) {
            $dataUpdate['status'] = $newStatus;        
        }

        // get tracking data
        if (isset($webhookPayload['resource']['code'])) {

            $trackingData = $this->getMerchizeOrderTracking($webhookPayload['resource']['code']);

            if ($trackingData && count($trackingData)) {
                $shipments = $this->updateShipments($trackingData, $order);
                if (!empty($shipments )) {
                    $dataUpdate['shipments'] = $shipments;
                }
            }
        }

        if (!empty($dataUpdate)) {
            $this->_sendUpdateOrder($order->id, $dataUpdate, [ "Referal: Webhook" ]);
        }
    }

    protected function _sendUpdateOrder($orderId, $dataUpdate, $headers = []) {
        $updateUrl = Config::get('sa.api_url') . '/api/update-order/' . $orderId;
        array_push($headers, 'Content-Type:application/json');
        return $this->triggerSyncRequest($updateUrl, 'POST', $dataUpdate, $headers);
    }

    protected function updateShipments($trackingData, $order)
    {
        $checkShipments = [];
        $shipments = [];

        foreach ($trackingData as $shipment)
        {
            $tempShipment = [];
            if (!empty($shipment['tracking_company'])) 
            {
                $shipper = Shipper::firstOrCreate([
                    'name' => $shipment['tracking_company']
                ], 
                [
                    'name' => $shipment['tracking_company'], 
                    'meta_data' => 'Created by Webhook'
                ]);

                $tempShipment['id'] = $shipper->id;
                $tempShipment['code'] = $shipment['tracking_number'];

                OrderMeta::updateOrCreate([ 'order_id' => $order->id, 'key' => 'shipment' ], [ 'value' => $shipper->id ]);

                if (!empty($shipment['tracking_url'])) {
                    $tempShipment['url'] = $shipment['tracking_url'];
                }

                $existShipment = $tempShipment['id'] . '-' . $tempShipment['code'];
                if (!in_array($existShipment, $checkShipments)) {
                    array_push($shipments, $tempShipment);
                    array_push($checkShipments, $existShipment);
                }
            }

        }

        if (!empty($shipments)) {
            OrderMeta::updateOrCreate([ 'order_id' => $order->id, 'key' => 'shipment_data' ], [ 'value' => json_encode($shipments) ]);
        }

        return $shipments;
    }

    protected function getMerchizeOrderTracking($orderCode, $type = 'code')
    {
        list($shopId, $apiToken) = $this->getCredential('printerval', 'merchize');
        $carriers = [];

        if ($orderCode && $shopId && $apiToken) {
            
            $client = new Client([
                'headers' => [
                    'x-api-key' => $apiToken,
                    'Content-Type' => 'application/json'
                ]
            ]);

            try {
                $httpResponse = $client->get($this->apiUrl . '/order/external/orders/tracking?' . $type . '=' . $orderCode);
                $merchizeRes = json_decode($httpResponse->getBody(), true);
                if (isset($merchizeRes['success'], $merchizeRes['data']) && $merchizeRes['success'] && count($merchizeRes['data'])) {
                    $carriers = $merchizeRes['data'];
                }
            } catch (\Exception $ex) {
                $carriers = [];
            }
        }

        return $carriers;
    }

    /**
     * @param $order
     * @param $orderSchema
     * @return false|mixed
     */
    protected function fulfill($order, $orderSchema)
    {
        list($shopId, $apiToken) = $this->getCredential('printerval', 'merchize');

        if ($shopId && $apiToken) {
            
            $client = new Client([
                'headers' => [
                    'x-api-key' => $apiToken,
                    'Content-Type' => 'application/json'
                ]
            ]);

            $httpResponse = $client->post('https://bo-5mjd36b.merchize.com/bo-api/order/external/orders', [
                'body' => json_encode($orderSchema, JSON_UNESCAPED_UNICODE)
            ]);

            $merchizeRes = json_decode($httpResponse->getBody(), true);

            $this->logFulfill([
                'order_id' => $order['id'],
                'data' => $orderSchema
            ]);

            if ($merchizeRes) return $merchizeRes;
        }

        return false;
    }

    /**
     * @throws Exception
     */
    private function buildLineItem($orderItem)
    {
        $lineItem = null;

        // anh Quân viết chung hàm bên này nên phải gọi sang như vậy
        // $printifyController = new PrintifyController();
        $printifyController = app(PrintifyController::class);

        $template = $printifyController->getTemplate($orderItem['product_sku_id']);
        $template = (isset($template['template'])) ? $template['template'] : null;

        if (!$template) {
            throw new Exception('Template not found: ' . $orderItem['product_sku_id']);
        }

        $printableAreas = ['front', 'back'];
        // check print configuration
        $printAreas = $this->getPrintLocations($orderItem);

        if (!count(array_diff($printAreas, $printableAreas))) {

            $designs = [];
            $getDesignParams = [];

            foreach ($printAreas as $area) {
                $resolution = $template->resolution;
                $design = $this->getFulfillDesign($orderItem['product_sku_id'], $resolution, $orderItem['product_id'], null, $area);
                $getDesignParams[] = [
                    'prams' => [$orderItem['product_sku_id'], $resolution, $orderItem['product_id'], null, $area],
                    'result' => $design
                ];

                if ($design) {
                    $designs[$area] = $design;
                }
            }

            if (count($designs) == count($printAreas)) {

                $designs = array_map(function($d) {
                    return $d->image_url;
                }, $designs);

                $lineItem = $this->getLineItemByTemplate($template, $orderItem, $designs);

            } else {

                $detailDesigns = [
                    'received_design' => $designs,
                    'expected_areas' => $printAreas,
                    'get_design_params_detail' => $getDesignParams
                ];

                throw new Exception('Design missing: ' . $orderItem['product_sku_id'] . '. Detail: ' . json_encode($detailDesigns));

            }

        }  else {
            throw new Exception('Printable Areas not found: ' . $orderItem['product_sku_id']);
        }

        return $lineItem;
    }

    /**
     * return line item by fulfill template
     * @return array
     * */
    protected function getLineItemByTemplate($template, $item, $designs)
    {
        $lineItem = [
            "name" => $item['name'],
            "product_id" => $item['id'],
            "sku" => $item['product_sku'],
            "quantity" => intval($item['quantity']),
            "price" => 0,
            "currency" => 'USD',
            "image" => $item['image_url'],
            "design_front" => '',
            "design_back" => '',
            "design_sleeve" => '',
            "design_hood" => '',
            "attributes" => $template->attributes
        ];

        foreach ($designs as $area => $url) {
            if ($area == 'front') $lineItem['design_front'] = $url;
            if ($area == 'back') $lineItem['design_back'] = $url;
        }

        return $lineItem;
    }

    /**
     * @param $orderDetail
     * @return array
     */
    protected function getFulfillOrderSchema($orderDetail)
    {
        $fullname = $orderDetail['customer']['full_name'];
        $phone = $orderDetail['customer']['phone'];

        if (isset($orderDetail['attributes']['customer_info'])) {
            $fullname = $orderDetail['attributes']['customer_info']['full_name'];
            $phone = $orderDetail['attributes']['customer_info']['phone'];
        }

        if (isset($orderDetail['attributes']['gift_info']['name']) && $orderDetail['attributes']['gift_info']['name']) {
            $fullname = $orderDetail['attributes']['gift_info']['name'];
            $phone = $orderDetail['attributes']['gift_info']['phone'];
        }

        $customerName = getCustomerName($fullname);
        
        $lastName = $customerName['last_name'];
        $firstName = $customerName['first_name'];

        return [
            "order_id" => "API" . $orderDetail['code'],
            "identifier" => 'autofulfill',
            "shipping_info" => [
                "full_name" => $firstName . ' ' . $lastName,
                "address_1" => $orderDetail['delivery_address'],
                "address_2" => (isset($orderDetail['optional_address']) && $orderDetail['optional_address']) ? $orderDetail['optional_address'] : ' ',
                "city" => $orderDetail['city_name'],
                "state" => $orderDetail['state_name'],
                "postcode" => $orderDetail['zip_code'],
                "country" => $orderDetail['country']['iso'],
                "email" => 'info@megaads.vn',
                "phone" => $phone,
            ],
            "items" => []
        ];
    }

    protected function logFulfill($logData)
    {
        DB::table('log')->insert([
            'actor_email' => 'system',
            'target_id' => $logData['order_id'],
            'target_type' => 'ORDER',
            'event_type' => 'MERCHIZE_AUTO_FULFILL',
            'data' => json_encode($logData['data']),
            'created_at' => date('Y-m-d H:i:s')
        ]);
    }

    public function getMerchizeProducts()
    {
        $products = DB::table('print_supplier_merchize_product')
            ->select(['id', 'product_title'])
            ->get();

        return $products;
    }

    public function getMerchizeVariants(Request $request)
    {
        $productId = $request->input('product_id');
        $variants = [
            'colors' => [],
            'sizes' => []
        ];
        
        if ($productId)
        {
            $rawVariants = DB::table('print_supplier_merchize_variant')
                ->whereIn('type', ['size', 'color'])
                ->where('mc_product_id', $productId)
                ->get();

            foreach ($rawVariants as $value) {
                if ($value->type == 'size') $variants['sizes'][] = $value;
                if ($value->type == 'color') $variants['colors'][] = $value;
            }
        }

        return $variants;
    }

    public function updateProducts() // cần chuẩn bị bảng print_supplier_merchize_import trước
    {
       // Đường dẫn đến file CSV
        $csvFile = public_path('MRC.csv');

        // Hàm để đọc CSV và chuyển đổi thành mảng
        function readCSV($csvFile){
            $file_handle = fopen($csvFile, 'r');
            $data = [];

            // Bỏ qua hàng đầu tiên (tiêu đề cột)
            fgetcsv($file_handle, 1024, ',');

            // Đọc từng dòng của file CSV
            while (($row = fgetcsv($file_handle, 1024, ',')) !== FALSE) {
                $prod = [
                    'id' => $row[0],
                    'product_type' => $row[1],
                    'color' => explode(', ', $row[2]),
                    'size' => explode(', ', $row[3]),
                    'link' => $row[4]
                ];

                $mcProductId = DB::table('print_supplier_merchize_product')->insertGetId([
                    'product_title' => $prod['product_type'],
                    'link' => $prod['link'],
                    'type' => null,
                ]);

                if (count($prod['color'])) {
                    foreach ($prod['color'] as $value) {
                        DB::table('print_supplier_merchize_variant')->insert([
                            'type' => 'color',
                            'value' => $value,
                            'mc_product_id' => $mcProductId
                        ]);
                    }
                }

                if (count($prod['size'])) {
                    foreach ($prod['size'] as $value) {
                        DB::table('print_supplier_merchize_variant')->insert([
                            'type' => 'size',
                            'value' => $value,
                            'mc_product_id' => $mcProductId
                        ]);
                    }
                }

                $data[] = $prod;
            }

            fclose($file_handle);
            return $data;
        }

        // Đọc dữ liệu từ file CSV
        $products = readCSV($csvFile);

        // In ra mảng dữ liệu
        return $products;
    }

    public function crawlMerchize(Request $request) {
        // var links = [''];
        // while (links.length != 0) {
        //     var url = links.pop();
        //     var xhr = new XMLHttpRequest();
        //     xhr.open('GET', 'https://printerval.com/print-suppliers/merchize/crawl-products?url=' + url.trim(), false);
        //     xhr.onreadystatechange = function() {
        //         if (xhr.readyState == 4 && xhr.status == 200) {
        //             console.log(url, xhr.responseText);
        //         }
        //     }
        //     xhr.send();
        // }
    
        $retVal = ['status' => 'error', 'message' => 'Crawl failed!'];
        if ($request->has('url')) {
            $mappingVariantTypes = [
                'SIZES' => 'size',
                'TIERS' => 'tier',
                'COLORS' => 'color',
                'OPTIONS' => 'option',
                'TYPES' => 'type'
            ];
            $sourcePage = $this->_getSourceMerchizeProducts(trim($request->input('url')));
            preg_match_all('/const product = ({(.*)})/', $sourcePage, $matches);
            if (!empty($matches[1][0])) {
                $product = json_decode($matches[1][0], true);
                $merchize = MerchizeProduct::updateOrCreate([
                    'link' => $request->input('url')
                ], [
                    'product_title' => $product['title'],
                    'type' => $product['sku']
                ]);
                if ($merchize->id && !empty($product['options'])) {
                    foreach ($product['options'] as $option) {
                        if (empty($option['values']) || !isset($mappingVariantTypes[$option['name']])) continue;
                        
                        foreach ($option['values'] as $value) {
                            MerchizeProductVariant::updateOrCreate([
                                'mc_product_id' => $merchize->id,
                                'type' => $mappingVariantTypes[$option['name']],
                                'value' => $value
                            ]);
                        }
                    }
                }
                $retVal = ['status' => 'success', 'message' => 'Crawl successful!'];
            }
        }
        return response()->json($retVal);
    }

    private function _getSourceMerchizeProducts($url) {
        $command = "curl '$url' " .
           "-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' " .
           "-H 'accept-language: en-US,en;q=0.9,vi;q=0.8' " .
           "-H 'cache-control: no-cache' " .
           "-H 'cookie: _gcl_au=1.1.1332401207.1724121914; _gid=GA1.2.855835872.1724121915; _fbp=fb.1.1724121915749.462602694329664406; _ga=GA1.1.1407203832.1724121915; _ga_90CCF1FEYX=GS1.1.1724125509.2.1.1724126120.60.0.0; _ga_9DJ2LVFW6G=GS1.1.1724125509.2.1.1724126120.60.0.0' " .
           "-H 'pragma: no-cache' " .
           "-H 'priority: u=0, i' " .
           "-H 'referer: https://www.google.com/' " .
           "-H 'sec-ch-ua: \"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\"' " .
           "-H 'sec-ch-ua-mobile: ?0' " .
           "-H 'sec-ch-ua-platform: \"macOS\"' " .
           "-H 'sec-fetch-dest: document' " .
           "-H 'sec-fetch-mode: navigate' " .
           "-H 'sec-fetch-site: cross-site' " .
           "-H 'sec-fetch-user: ?1' " .
           "-H 'upgrade-insecure-requests: 1' " .
           "-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'";

        $output = shell_exec($command);
        return $output; 
    }
}