<?php

namespace Modules\PrintSuppliers\Controllers;

use App\Customer;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Route;
use Modules\PrintSuppliers\Controllers\Controller;
use Modules\PrintSuppliers\Models\Order;
use Modules\PrintSuppliers\Models\OrderItem;
use Modules\PrintSuppliers\Models\OrderMeta;
use Modules\PrintSuppliers\Models\OrderMatching;
use Modules\PrintSuppliers\Models\PrintifyVariant;
use Modules\PrintSuppliers\Models\Shipper;
use Modules\PrintSuppliers\Models\Resource;
use Modules\PrintSuppliers\Models\Template;
use Modules\PrintSuppliers\Models\Color;
use GuzzleHttp\Client;
use Modules\PrintSuppliers\Controllers\Services\PrintifyExpressService;

class PrintifyController extends Controller
{

    private $printingName = 'printify';
    private $printifyApiUrl;
    private $printifyExpressSerivce;
    private $shopId;
    private $apiToken;

    public function __construct(PrintifyExpressService $printifyExpressService)
    {
        $this->printifyApiUrl = Config::get('print-suppliers::connection.printify.api_url', 'https://api.printify.com');
        $this->printifyExpressSerivce = $printifyExpressService;

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

        $this->shopId = $shopId;
        $this->apiToken = $apiToken;

    }

    public function test () {
        $items = DB::table('order_item')->select('product_sku_id')->take(2000)->get();
        $total = 0;
        $count = 0;
        foreach ($items as $key => $value) {
            \Log::info($value->product_sku_id);
            $template = $this->getTemplate($value->product_sku_id);
            $total++;
            if ($template) {
                \Log::info($template);
                $count++;
            } else {
                \Log::info("No template");
            }
        }
        \Log::info($count);
        \Log::info($total);
    }

    public function getTemplates($orderId) {

        $retval = [
            'status' => 'successful',
            'msg' => ''
        ];
        $routeParameters = Route::current()->parameters();
        $orderId = !empty($routeParameters['orderId'])  ? $routeParameters['orderId'] : $orderId;
        try {
            $order = (object) $this->findOrderDetail($orderId);
            $retval['order'] = $order;
            if ($order) {
                $retval['order'] = $order;
            } else {
                throw new \Exception("Order not found", 1);
            }
            if ($order->country_id != 226) {
                throw new \Exception("US Only", 226);
            }
            $items = $order->items;
            $inputs = [];
            $onlyWomenClassicTshirt = true;
            $hasWomenClassicTshirt = false;
            $checkBlueprintMRC = false;
            $forcePrintProviderTrademarks = [];
            foreach ($items as $key => $item) {
                $input = $this->getTemplateInput($item['product_sku_id']);
                $input[3] = $this->mapType($input[3]);
                $input[4] = $this->mapStyle($input[4]);
                if ($input[3] == 'Women' && in_array($input[4], [null, 'Classic T-shirt'])) {
                    $hasWomenClassicTshirt = true;
                } else {
                    $onlyWomenClassicTshirt = false;
                }
                if ($input[0] == null) {
                    throw new \Exception("Can not get item input, item id :  $item->id", 3);
                }
                $input[5] = request()->has('position') ? request()->input('position') : 'front';
                $input['quantity'] = $item['quantity'];
                $input['id'] = $item['id'];
                if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'Heavyweight T-shirt') {
                    $checkBlueprintMRC =  true;
                } else if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'Comfort Colors Tee') {
                    $checkBlueprintMRC =  true;
                } else if (in_array($input[3], ['Women']) && $input['4'] == 'Classic T-shirt') {
                    $checkBlueprintMRC =  true;
                } else if (in_array($input[3], ['Youth']) && $input['4'] == 'Heavyweight T-shirt') {
                    $checkBlueprintMRC =  true;
                } else if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'Classic Tank') {
                    $checkBlueprintMRC =  true;
                }
                if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'Classic T-shirt') {
                    if (in_array($input['2'], ['S', 'M', 'L', 'XL'])) {
                        $forcePrintProviderTrademarks[] = 61;
                    } else if (in_array($input['2'], ['2XL', '3XL', '4XL', '5XL'])) {
                        $forcePrintProviderTrademarks[] = 3;
                    }
                }
                if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'V-neck T-shirt') {
                    $forcePrintProviderTrademarks[] = 3;
                }
                if (in_array($input[3], ['Men', 'Unisex']) && $input['4'] == 'Classic Hoodie') {
                    if (in_array($input['2'], ['S', 'M', 'L', 'XL'])) {
                        $forcePrintProviderTrademarks[] = 61;
                    } else if (in_array($input['2'], ['2XL', '3XL', '4XL', '5XL'])) {
                        $checkBlueprintMRC =  true;
                    }

                }
                $inputs[$key] = $input;
            }
            $forcePrintProviderTrademarks = array_unique($forcePrintProviderTrademarks);
//            if ($order->shipping_type != 'express' && $notMenWomanClassicTshirt && $womanClassicTshirt) {
//                throw new \Exception("Order has merchize & printify", 15);
//            }
            $order->only_women_classic_tshirt = $onlyWomenClassicTshirt;
            $order->has_women_classic_tshirt = $hasWomenClassicTshirt;
            if (!empty($order->attributes['ignore_trademark'])) {
                $order->force_print_provider_ids  = [];
            } else {
                $order->force_print_provider_ids = $this->getForcePrintProviderIds($items);
            }
            $hasTrademark = false;
            if ($order->force_print_provider_ids) {
                $hasTrademark = true;
            }

            if ($hasTrademark && count($forcePrintProviderTrademarks) > 1) {
                throw new \Exception("Trademark multiple print provider");
            }
            $order->printing_service = 'printify';
            $template = new PrintifyTemplateV2;
//            if ($order->shipping_type == 'express' || $notMenWomanClassicTshirt || ($womanClassicTshirt && $menClassicTshirt)) {
//                $order->printing_service = 'printify';
//                $template = new PrintifyTemplateV2;
//            } else if ($womanClassicTshirt && !$menClassicTshirt && !$notMenWomanClassicTshirt) {
//                $order->printing_service = 'merchize';
//                $template = new MerchizeTemplate;
//            }
            if ($hasTrademark && $checkBlueprintMRC) {
                $order->printing_service = 'merchize';
                $template = new MerchizeTemplate;
            }
            if ($hasTrademark && !$checkBlueprintMRC && $forcePrintProviderTrademarks) {
                $order->force_print_provider_ids = $forcePrintProviderTrademarks;
            }

            $outputs = $template->getTemplates($inputs, $order);
            foreach ($outputs as $key => $value) {
                $items[$key]['template'] = $value;
            }
            if (count($items)) {
                $retval['items'] = $items;
            } else {
                throw new \Exception("Items not found", 2);
            }
        } catch (\Exception $e) {
            $retval['status'] = 'fail';
            $retval['msg'] = $e->getMessage() . '. File: ' . $e->getFile() . '. Line: ' . $e->getLine();
        }
       

        return $retval;
    }


    private function getForcePrintProviderIds ($items) {
        $retVal = [];
        $listText = ["swift", "taylor", "rolling", "Nintendo", "Disney", "Kitty", "lego", "21 SAVAGE","AEROSMITH","AMY WINEHOUSE","ANITTA","ARIANA GRANDE","AVRIL LAVIGNE","BEACH BOYS","BEASTIE BOYS","BENEE","BILLIE EILISH","BLACK SABBATH","BLACK VEIL BRIDES","BLACKPINK","BLEACHERS","BOB MARLEY","BON JOVI","BOYZONE","BRANDY","BRITNEY SPEARS","BROCKHAMPTON","BROS","BRYAN FERRY","BRYSON TILLER","CLAIRE ROSINKRANZ","CONAN GRAY","DAVID BOWIE","DEF LEPPARD","DEMI LOVATO","DJ KHALED","DR. DRE","ELTON JOHN","FLETCHER","FREDDIE MERCURY","GARY BARLOW","GRETA VAN FLEET","GUNS N’ ROSES","HARDY","HOPE TALA","ILLENIUM","IMAGINE DRAGONS","INXS","JACOB COLLIER","JAMIROQUAI","JANET JACKSON","JAZMIN BEAN","JEREMY ZUCKER","JUSTIN BIEBER","JUSTIN TIMBERLAKE","KASABIAN","KESHI","KID CUDI","KIM PETRAS","KISS","LADY GAGA","LAUREN DAIGLE","LIL WAYNE","LORDE","MACHINE GUN KELLY","MARY J. BLIGE","MEG DONNELLY","METRO BOOMIN","MIDLAND","MONTY PYTHON","MORGAN WALLEN","MYKE TOWERS","NAS","NAV","NEIL DIAMOND","NICKI MINAJ","OLIVIA RODRIGO","PANTERA","PARIS JACKSON","PAUL WELLER","PIERRE BOURNE","PIL","PLAYBOI CARTI","POP SMOKE","POST MALONE","PUBLIC ENEMY","PUSHA T","QUEEN","QUEENS OF THE STONE AGE","RAE SREMMURD","RENEÉ RAPP","ROBBIE WILLIAMS","ROD STEWART","RONAN KEATING","RONNIE WOOD","ROXY MUSIC","RUN DMC","SAM SMITH","SCORPIONS","SELENA GOMEZ","SEX PISTOLS","SHANIA TWAIN","SHAWN MENDES","SHECK WES","SIGRID","SKEPTA","SKIP MARLEY","SLIPKNOT","SOCIAL DISTORTION","SPANDAU BALLET","SPICE GIRLS","STEPS","STYLE COUNCIL","TAJ MAHAL","TAKE THAT","TAYLOR SWIFT","THE 1975","THE BEATLES","THE BLACK CROWES","THE JAM","THE KILLERS","THE ROLLING STONES","THE WANTED","THE WAR ON DRUGS","THE WEEKND","THE WHO","TRAVIS SCOTT","TUPAC SHAKUR","WET LEG","WILLIE NELSON","XXXTENTACION","YUNGBLUD"];
        foreach ($items as $item) {
            foreach ($listText as $itemText) {
                $itemText = strtolower($itemText);
                $name = strtolower($item['product_name']);
                if (preg_match('/\b' . $itemText . '\b/', $name)) {
                    if (\Illuminate\Support\Facades\Input::get('debug') == 2) {
                        echo $itemText;
                    }
                    $retVal = [61, 217, 3, 50];
                    break;
                    break;
                }
            }
        }
        return $retVal;
    }

    /**
     * [getTemplate Lay ra cac thuoc tinh can cho viec auto fulfill]
     * @param  [int] $productSkuId               [ma bien the]
     * @return [array] $retval             [$blueprint_id, $print_provider_id, $variant_id]
     */
    public function getTemplate($productSkuId) {

        if (!$productSkuId || !is_numeric($productSkuId)) {
            $routeParameters = Route::current()->parameters();
            $productSkuId = $routeParameters['productSkuId'];
        }

        if (!$productSkuId) {
            return [
                'status' => 'fail',
                'msg' => 'Product SKU ID require'
            ];
        }
        $input = $this->getTemplateInput($productSkuId);
        $input[5] = request()->has('position') ? request()->input('position') : 'front';
        $printifyTemplate = new PrintifyTemplate;
        $output = $printifyTemplate->getTemplate($input);
        $template = new Template;
        $template->id = null;

        $template->category_id =  $input[0];
        $template->color = $input[1];
        $template->size = $input[2];
        $template->type = $input[3];
        $template->style = $input[4];
        $template->position = $input[5];
        $template->printing_service = "printify";
        $template->created_at = null;
        $template->updated_at = null;
        $template->enable = 1;
        $template->blueprint_id = $output[0];
        $template->print_provider_id = $output[1];
        $template->variant_id = $output[2];
        $template->count = 0;
        $template->resolution = $output[3];
        $template->note = 'Generated from Auto Printify Template, you can not edit this template.';

        if ($template && strtolower($template->type) == 'women' && strtolower($template->style) == 'classic t-shirt') {
            return $this->getMerchizeTemplate($input);
        }
        if (!$template->variant_id) {
            $template = $this->getTemplateOutput($input);
        }

        if ($template && $template->blueprint_id != 6) {
            return [
                'status' => 'fail',
                'template' => null,
                'msg' => 'No template available',
                'input' => $input,
                'template' => $template
            ];
        }
        if ($template && $template->variant_id) {
            return [
                'status' => 'successful',
                'template' => $template,
                'input' => $input
            ];

        } else {
            return [
                'status' => 'fail',
                'template' => null,
                'msg' => 'No template available',
                'input' => $input
            ];
        }
    }

    public function getMerchizeTemplate ($input) {
        $merchizeTemplate = new MerchizeTemplate;
        $output = $merchizeTemplate->getTemplate($input);

        if ($output) {
            $template = new Template;
            $template->id = null;

            $template->category_id =  $input[0];
            $template->color = $input[1];
            $template->size = $input[2];
            $template->type = $input[3];
            $template->style = $input[4];
            $template->position = $input[5];
            $template->attributes = $output['attributes'];
            $template->resolution = '4200x4800';
            $template->enable = 1;
            $template->created_at = null;
            $template->updated_at = null;
            $template->printing_service = "merchize";
            $template->note = 'Generated from Auto Merchize Template, you can not edit this template.';
            return [
                'status' => 'successful',
                'template' => $template,
                'msg' => 'Do not auto fulfill classic women tshirt',
                'input' => $input
            ];
        } else {
            return [
                'status' => 'fail',
                'template' => null,
                'msg' => 'Do not auto fulfill classic women tshirt',
                'input' => $input
            ];
        }
    }

    public function getTemplateGelato($productSkuId) {

        if (!$productSkuId || !is_numeric($productSkuId)) {
            $routeParameters = Route::current()->parameters();
            $productSkuId = $routeParameters['productSkuId'];
        }

        if (!$productSkuId) {
            return [
                'status' => 'fail',
                'msg' => 'Product SKU ID require'
            ];
        }
        $input = $this->getTemplateInput($productSkuId);
        $input[5] = request()->has('position') ? request()->input('position') : 'front';
        $printifyTemplate = new GelatoTemplate;
        $output = $printifyTemplate->getTemplate($input);
        $template = new Template;
        $template->id = null;

        $template->category_id =  $input[0];
        $template->color = $input[1];
        $template->size = $input[2];
        $template->type = $input[3];
        $template->style = $input[4];
        $template->position = $input[5];
        $template->printing_service = "gelato";
        $template->created_at = null;
        $template->updated_at = null;
        $template->enable = 1;
        $template->product_uid = $output[0];
        $template->resolution = $output[1];
        $template->note = 'Generated from Auto Gelato Template, you can not edit this template.';


        if ($template && $template->product_uid) {
            return [
                'status' => 'successful',
                'template' => $template,
                'input' => $input
            ];

        } else {
            return [
                'status' => 'fail',
                'template' => $template,
                'msg' => 'No template available',
                'input' => $input
            ];
        }
    }


    public function getResolution($productSkuId) {
        $retval = [
            'status' => 'fail',
        ];
        if (!$productSkuId) {
            return [
                'status' => 'fail',
                'msg' => 'Product SKU ID require'
            ];
        }
        $input = $this->getTemplateInput($productSkuId);
        $output = $this->getTemplateOutput($input);
        // anh quan bao the
        if (true) {
            if ($input && isset($input[0])) {
                $retval['status'] = 'successful';
                $template = new Template;
                switch ($input[0]) {
                    case '7': // T Shirt
                        $template->resolution = '4500x5100';
                        break;
                    case '8': // Hoodie
                        $template->resolution = '4500x3000';
                        break;
                    case '33': // Custom T Shirt
                        $template->resolution = '4500x5100';
                        break;
                    case '34': // Hat
                        $template->resolution = '1200x720';
                        break;
                    case '48': // Tank Top
                        $template->resolution = '3000x3600';
                        break;

                    case '54': // Sweatshirts
                        $template->resolution = '4500x5100';
                        break;
                    case '57': // Face masks
                        $template->resolution = '2558x1898';
                        break;
                    case '59':
                        $template->resolution = '3300x4400';
                        break;
                    case '68':
                        $template->resolution = '4200x5100';
                        break;
                    case '151':
                        $template->resolution = '4500x3000';
                        break;

                    default:
                        $retval['status'] = 'fail';
                        break;
                }
                $retval['template'] = $template;
            }

        } else {
            $retval = [
                'status' => 'successful',
                'template' => $output
            ];
        }
        return $retval;
    }

    /**
     * [getTemplateInput Lay ra cac input de lay ra template]
     * @param  [int] $productSkuId               [ma bien the]
     * @return [array] $retval                   [$category_id, $color, $size, $type, $style]
     */
    public function getTemplateInput($productSkuId) {
        $locale = env('APP_LOCALE', 'us');
        if (!$locale) {
            $locale = 'us';
        }
        return ($this->triggerSyncRequest("https://printerval.com/" . $locale . "/print-suppliers/get-fulfill-sku-attrs?sku_id=$productSkuId"));
        // return $this->getFulfillSkuAttrs($productSkuId);
    }


    /**
     * [getTemplateOutput]
     * @param  [array] $input              [$category_id, $color, $size, $type, $style]
     * @return [array] $output             [$blueprint_id, $print_provider_id, $variant_id]
     */
    public function getTemplateOutput ($input) {
        for ($i=0; $i < 5; $i++) {
            $input[$i] = $input[$i] ? $input[$i] : '';
        }
        $output = Template::where('category_id', $input[0])
                          ->where('color', $input[1])
                          ->where('size', $input[2])
                          ->where('type', $input[3])
                          ->where('style', $input[4])
                          ->where('enable', 1)
                          ->first();
        return $output;
    }

    public function fetchBlueprint(Request $request)
    {
        $search = $request->input('search');
        $blueprintId = $request->input('blueprint_id');

        /* $blueprints = DB::table('printify_order_import_data')
            ->select(['id', 'blueprint_id', 'blueprint_title as display', 'print_provider_id'])
            ->groupBy('blueprint_id'); */

        $blueprints = DB::table('print_suppliers_printify_blueprint');

        if ($search) {
            $blueprints = $blueprints->where('search', 'like', '%' . $search . '%');
        }

        if ($blueprintId) {
            $blueprints = $blueprints->where('blueprint_id', $blueprintId);
        }

        $blueprints->limit(100);
        $blueprints = $blueprints->get();

        return [
            "status" => "successful",
            "result" => $blueprints
        ];
    }

    public function fetchPrintProvider(Request $request)
    {
        $search = $request->input('search');
        $blueprintId = $request->input('blueprint_id');

        $printProdivers = DB::table('printify_order_import_data')
            ->where('blueprint_id', $blueprintId)
            ->select(['id', 'blueprint_id', 'print_provider_name as display', 'print_provider_id'])
            ->groupBy('print_provider_id');

        if ($search) {
            $printProdivers = $printProdivers->where('print_provider_name', 'like', '%' . $search . '%');
        }

        $printProdivers->limit(20);
        $printProdivers = $printProdivers->get();

        return [
            "status" => "successful",
            "result" => $printProdivers
        ];
    }

    public function fetchVariant(Request $request)
    {
        $search = $request->input('search');
        $blueprintId = $request->input('blueprint_id');
        $printProviderId = $request->input('print_provider_id');

        $variants = DB::table('printify_order_import_data')
            ->where('blueprint_id', $blueprintId)
            ->where('print_provider_id', $printProviderId)
            ->select(['id', 'blueprint_id', 'variant_title as display', 'print_provider_id', 'variant_id', 'printable_areas']);

        if ($search) {
            $variants = $variants->where('variant_title', 'like', '%' . $search . '%');
        }

        $variants->limit(20);
        $variants = $variants->get();

        return [
            "status" => "successful",
            "result" => $variants
        ];
    }

    private function _printifyOrder($shopId, $resourceId = null, $params = []) {
        $accounts = Config::get('print-suppliers::connection.printify.accounts');
        $apiUrl = Config::get('print-suppliers::connection.printify.api_url');

        if (empty($accounts[$shopId])) return;

        if (!empty($resourceId)) {
            $endPoint = $apiUrl . "/v1/shops/$shopId/orders/$resourceId.json";
        } else {
            $endPoint = $apiUrl . "/v1/shops/$shopId/orders.json";
        }
        if (!empty($params)) {
            $endPoint .= '?' . http_build_query($params);
        }

        $printifyOrder = $this->triggerSyncRequest($endPoint, 'GET', [], [
            'Authorization: Bearer ' . $accounts[$shopId]['api_token']
        ]);
        return $printifyOrder;
    }

    private 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);
    }

    private function _updateOrder($order, $shopId, $printifyOrder, $refer = null) 
    {
        $mapStatus = Config::get('print-suppliers::statuses.printify');

        if (!empty($refer)) {
            $dataUpdate = [];
            $checkShipments = [];
            $delivered = false;

            if (!empty($printifyOrder['shipments'])) {

                $delivered = true;
                $shipments = [];
                $shipmentKey = 'shipment';

                foreach ($printifyOrder['shipments'] as $shipment) {
                    $tempShipment = [];
                    if (!empty($shipment['carrier'])) {
                        $shipper = Shipper::firstOrCreate(['name' => $shipment['carrier']], ['name' => $shipment['carrier'], 'meta_data' => 'Created by Webhook']);
                        $tempShipment['id'] = $shipper->id;
                        $tempShipment['code'] = $shipment['number'];

                        OrderMeta::updateOrCreate([ 'order_id' => $order->id, 'key' => $shipmentKey ], [ 'value' => $shipper->id ]);
                        if (!empty($shipment['url'])) {
                            $tempShipment['url'] = $shipment['url'];
                        }

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

                    if (!isset($shipment['delivered_at']) || !$shipment['delivered_at']) {
                        $delivered = false;
                    }

                }
            }

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

            if (!empty($mapStatus[$printifyOrder['status']])) {
                $dataUpdate['status'] = $mapStatus[$printifyOrder['status']];
                if ($printifyOrder['status'] == 'payment-not-received') {
                    OrderMeta::updateOrCreate([ 'order_id' => $order->id, 'key' => 'is_auto_fulfill' ], [ 'value' => 2 ]);
                    OrderMeta::updateOrCreate([ 'order_id' => $order->id, 'key' => 'reason_auto_fulfill_fail' ], [ 'value' => 'request_auto_fulfill' ]);
                }
            }

            if ($delivered == true) {
                $dataUpdate['status'] = 'FINISHED';
            }

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

        $orderUpdate = [
            'cost' => 0,
            'tax_cost' => 0,
            'shipping_cost' => 0
        ];
        if (!empty($printifyOrder['line_items'])) {
            foreach ($printifyOrder['line_items'] as $lineItem) {
                if (isset($lineItem['status']) && $lineItem['status'] == 'canceled') {
                    continue;
                }
                if (!empty($lineItem['cost'])) {
                    $orderUpdate['cost'] += $lineItem['cost'] / 100;
                }
                if (!empty($lineItem['shipping_cost'])) {
                    $orderUpdate['shipping_cost'] += $lineItem['shipping_cost'] / 100;
                }
            }
        }
        if (isset($printifyOrder['total_tax'])) {
            $orderUpdate['tax_cost'] = $printifyOrder['total_tax'] / 100;
        }
        if (isset($printifyOrder['status']) && $printifyOrder['status'] == 'canceled') {
            $orderUpdate['shipping_cost'] = 0;
            $orderUpdate['tax_cost'] = 0;
            $orderUpdate['cost'] = 0;
        }

        if (!empty($orderUpdate)) {
            try {
                OrderMatching::updateOrCreate([ 'order_code' => $order->code, 'printing_code' => $this->printingName ], $orderUpdate);
            } catch (Exception $ex) {
                OrderMatching::query()
                            ->where('order_code', '=', $order->code)
                            ->where('printing_code', '=', $this->printingName)
                            ->update($orderUpdate);
            }
        }

        /** tính tổng lại giá vốn, vì 1 đơn hàng có thể in ở nhiều nhà in */
        $totalOrderMatching = OrderMatching::where('order_code', '=', $order->code)
                                ->select(DB::raw('SUM(cost) AS cost'), DB::raw('SUM(shipping_cost) AS shipping_cost'), DB::raw('SUM(tax_cost) AS tax_cost'))
                                ->groupBy('order_code')
                                ->first();

        if (!empty($totalOrderMatching->cost)) {
            $orderUpdate['cost'] = $totalOrderMatching->cost;
        }
        if (!empty($totalOrderMatching->shipping_cost)) {
            $orderUpdate['shipping_cost'] = $totalOrderMatching->shipping_cost;
        }
        if (!empty($totalOrderMatching->tax_cost)) {
            $orderUpdate['tax_cost'] = $totalOrderMatching->tax_cost;
        }

        $orderUpdate['resource_id'] = $printifyOrder['id'];
        Order::where('id', '=', $order->id)->update($orderUpdate);

        if (isset($orderUpdate['cost'])) {
            DB::table('log')->insert([
                'actor_email' => 'system',
                'target_id' => $order->id,
                'target_type' => 'printifyWebhookV2',
                'event_type' => 'PRINTIFY_WEBHOOK_UPDATE_COST',
                'data' => json_encode($orderUpdate),
                'created_at' => date('Y-m-d H:i:s')
            ]);
        }

        //update order item
        if (!empty($printifyOrder['line_items'])) {
            foreach ($printifyOrder['line_items'] as $lineItem) {
                $itemUpdate = [];
                if (!empty($lineItem['shipping_cost'])) {
                    $itemUpdate['shipping_cost'] = $lineItem['shipping_cost'] / 100;
                }
                if (!empty($lineItem['cost'])) {
                    $itemUpdate['cost'] = $lineItem['cost'] / 100;
                }

                $itemUpdate['printing_code'] = $this->printingName;
                $itemUpdate['resource_item_id'] = $lineItem['variant_id'];
                $itemUpdate['resource_id'] = $printifyOrder['id'];
                $itemUpdate['store_id'] = $shopId;

                $orderItems = OrderItem::where('order_id', '=', $order->id)->get();

                $oldData = null;
                if (count($orderItems) == 1) {
                    $orderItems[0]->update($itemUpdate);
                    $oldData = clone $orderItems[0];
                } else if (!empty($lineItem['variant_id'])) {
                    $oldData = OrderItem::where('order_id', '=', $order->id)
                        ->where('resource_item_id', '=', $lineItem['variant_id'])
                        ->first();
                    OrderItem::where('order_id', '=', $order->id)
                                ->where('resource_item_id', '=', $lineItem['variant_id'])
                                ->update($itemUpdate);
                }
                if (isset($oldData->printing_code) && $oldData->printing_code == 'gelato') {
                    \DB::table("order_matching_item")->insert([
                        "order_id" => $order->id,
                        "order_item_id" => $oldData->id,
                        "resource_id" => $oldData->resource_id,
                        "resource_item_id" => $oldData->resource_item_id,
                        "cost" => $oldData->cost,
                        "shipping_cost" => $oldData->shipping_cost,
                        "printing_code" => $oldData->printing_code,
                        "status" => "PENDING",
                        "created_at" => new \Datetime(),
                        "updated_at" => new \Datetime(),
                    ]);
                }
            }
        }
    }

    public function updatePrintifyOrderByGlt($id, $order) {
        $accounts = Config::get('print-suppliers::connection.printify.accounts');
        foreach ($accounts as $key => $account) {
            $printifyOrder = $this->_printifyOrder($key, $id);
            \Log::info("Printify order " . json_encode($printifyOrder));
            if (isset($printifyOrder['line_items'])) {
                $orderUpdate = [];
                if (isset($printifyOrder['total_price'])) {
                    $orderUpdate['cost'] = $printifyOrder['total_price'] / 100;
                }
                if (isset($printifyOrder['total_shipping'])) {
                    $orderUpdate['shipping_cost'] = $printifyOrder['total_shipping'] / 100;
                }
                if (isset($printifyOrder['total_tax'])) {
                    $orderUpdate['tax_cost'] = $printifyOrder['total_tax'] / 100;
                }
                if (isset($printifyOrder['status']) && $printifyOrder['status'] == 'canceled') {
                    $orderUpdate['shipping_cost'] = 0;
                    $orderUpdate['tax_cost'] = 0;
                    $orderUpdate['cost'] = 0;
                }
                $printingUpdateData = $orderUpdate;
                if (!empty($orderUpdate)) {
                    try {
                        OrderMatching::updateOrCreate([ 'order_code' => $order->code, 'printing_code' => $this->printingName ], $orderUpdate);
                    } catch (Exception $ex) {
                        OrderMatching::query()
                                    ->where('order_code', '=', $order->code)
                                    ->where('printing_code', '=', $this->printingName)
                                    ->update($orderUpdate);
                    }
                }
        
                /** tính tổng lại giá vốn, vì 1 đơn hàng có thể in ở nhiều nhà in */
                $totalOrderMatching = OrderMatching::where('order_code', '=', $order->code)
                                        ->select(DB::raw('SUM(cost) AS cost'), DB::raw('SUM(shipping_cost) AS shipping_cost'), DB::raw('SUM(tax_cost) AS tax_cost'))
                                        ->groupBy('order_code')
                                        ->first();
        
                if (!empty($totalOrderMatching->cost)) {
                    $orderUpdate['cost'] = $totalOrderMatching->cost;
                }
                if (!empty($totalOrderMatching->shipping_cost)) {
                    $orderUpdate['shipping_cost'] = $totalOrderMatching->shipping_cost;
                }
                if (!empty($totalOrderMatching->tax_cost)) {
                    $orderUpdate['tax_cost'] = $totalOrderMatching->tax_cost;
                }
        
                $orderUpdate['resource_id'] = $printifyOrder['id'];
                Order::where('id', '=', $order->id)->update($orderUpdate);
            }
        }
        return $printingUpdateData;
    }

    public function printifyWebhook(Request $request) {
        return $this->printifyWebhookV2($request);
    }

    public function printifyWebhookV2(Request $request) {
        $mapStatus = Config::get('print-suppliers::statuses.printify');
        $input = $request->all();
        $retVal['status'] = 'fail';

        if (!empty($input['resource']['id']) && !empty($input['resource']['data'])) {
            $shopId = $input['resource']['data']['shop_id'];
            $printifyOrder = $this->_printifyOrder($shopId, $input['resource']['id']);

            if (isset($printifyOrder['status']) && !empty($mapStatus[$printifyOrder['status']])) { // allow statuses
                if (empty($printifyOrder['id']) || empty($printifyOrder['status'])) {
                    $orderMeta = OrderMeta::query()
                        ->where('key', '=', 'printify_order_id')
                        ->where('value', '=', $input['resource']['id'])
                        ->first();
                    if (
                        !$request->has('retry') &&
                        !empty($orderMeta->order_id) &&
                        !empty($input['resource']['data']['status']) &&
                        !empty($mapStatus[$input['resource']['data']['status']])
                    ) {
                        $dataUpdate = [
                            'status' => $mapStatus[$input['resource']['data']['status']]
                        ];
                        $this->_sendUpdateOrder($orderMeta->id, $dataUpdate, [ 'Referal:Webhook' ]);
                    }
    
                    return response()->json(['status' => 'fail', 'message' => $printifyOrder], 500, [], JSON_NUMERIC_CHECK);
                }

                if (!empty($printifyOrder['metadata']['shop_order_label'])) {
                    $printifyOrderLabel = str_replace('API', '', $printifyOrder['metadata']['shop_order_label']);
                    if (substr($printifyOrder['metadata']['shop_order_label'], 0, 4) === "Auto") {
                        $printifyOrderLabel = str_replace('Auto', '', $printifyOrder['metadata']['shop_order_label']);
                    }

                    $originOrder = Order::query()
                        ->where('code', '=', $printifyOrderLabel)
                        ->whereNotIn('status', ['CANCELED', 'REQUEST_RETURN', 'RETURNED'])
                        ->where('payment_status', '=', 'PAID')
                        ->orderBy('id', 'DESC')
                        ->first();

                    if (!$originOrder || empty($originOrder->id)) {

                        $originOrder = $this->findOrderByResourceId($input['resource']['id']);
                        if (!$originOrder) {
                            $retVal['message'] = 'Order Not Found.';
                            return response()->json($retVal, 200, [], JSON_NUMERIC_CHECK);
                        }
                    }

                    $this->_updateOrder($originOrder, $shopId, $printifyOrder, 'Webhook');
                    $retVal['status'] = 'successful';
                    $retVal['mapped_status'] = $mapStatus[$printifyOrder['status']];
                }

            } else {
                $retVal['message'] = 'Status not match from Config.';
            }

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

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

    protected function findOrderByResourceId($resourceId)
    {
        $order = Order::query()
            ->join("order_item", "order_item.order_id", "=", "order.id")
            ->where('order_item.resource_id', '=', $resourceId)
            ->whereNotIn('status', ['CANCELED', 'REQUEST_RETURN', 'RETURNED'])
            ->where('payment_status', '=', 'PAID')
            ->orderBy('order.id', 'DESC')
            ->first(['order.*']);
        if (!$order) {
            $order = Order::query()
                ->where('resource_id', '=', $resourceId)
                ->whereNotIn('status', ['CANCELED', 'REQUEST_RETURN', 'RETURNED'])
                ->where('payment_status', '=', 'PAID')
                ->orderBy('id', 'DESC')
                ->first();
        }
        return $order;
    }

    /**
     * Save data after fulfill complete
     * */
    public function storeFulfillTemplate(Request $request)
    {
        $templates = [];
        $profiles = $request->input();

        $apiUrl = Config::get('print-suppliers::connection.printify.api_url');
        list($shopId, $apiToken) = $this->getCredential();

        foreach ($profiles as $profile) {

            $skuAttr = $this->getFulfillSkuAttrs($profile['product_sku_id']);

            if (isset($skuAttr[0], $skuAttr[1], $skuAttr[2], $skuAttr[3]) && $skuAttr[0] && $skuAttr[1] && $skuAttr[2] && $skuAttr[3]) {

                $endPoint = $apiUrl . "/v1/catalog/blueprints/{$profile['blueprint_id']}/print_providers/{$profile['print_provider_id']}/variants.json";
                $response = $this->triggerSyncRequest($endPoint, 'GET', [], [
                    'Authorization: Bearer ' . $apiToken
                ]);

                $variants = (isset($response['variants'])) ? $response['variants'] : [];
                $targetVariant = array_filter($variants, function($v) use($profile) {
                    return $v['id'] == $profile['variant_id'];
                });

                if (count($targetVariant)) $targetVariant = end($targetVariant);

                $resolution = null;
                if (isset($targetVariant['placeholders'])) {
                    foreach ($targetVariant['placeholders'] as $placeholder) {
                        if ($placeholder['position'] == 'front') {
                            $resolution = $placeholder['width'] . 'x' . $placeholder['height'];
                            break;
                        }
                    }
                }

                $enable = 1;
                if ($skuAttr[0] == 33) $enable = 0;

                $templates[] = [
                    'category_id' => $skuAttr[0],
                    'color' => $skuAttr[1],
                    'size' => $skuAttr[2],
                    'type' => $skuAttr[3],
                    'style' => $skuAttr[4],
                    'enable' => $enable,
                    'printing_service' => 'printify',
                    'blueprint_id' => $profile['blueprint_id'],
                    'print_provider_id' => $profile['print_provider_id'],
                    'variant_id' => $profile['variant_id'],
                    'resolution' => $resolution
                ];

            }

        }

        if (count($templates)) {
            foreach ($templates as $template) {

                $existTemplate = Template::query()
                    ->where('printing_service', 'printify')
                    ->where('category_id', $template['category_id'])
                    ->where('color', $template['color'])
                    ->where('size', $template['size'])
                    ->where('style', $template['style'])
                    ->where('resolution', $template['resolution'])
                    ->where('print_provider_id', $template['print_provider_id'])
                    ->first();

                if (!$existTemplate) {
                    $existTemplate = new Template();
                }

                $existTemplate->fill($template);
                $existTemplate->save();

            }
        }

        return $templates;
    }

    /**
     * Request send order Printify to production
     * */
    public function submitProduction($orderId = null)
    {
        $responseData = [
            'status' => self::STATUS_FAIL
        ];

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

        if (!$orderId) {
            $orderId = request()->input('order_id');
        }

        if ($orderId) {
            // /v1/shops/{shop_id}/orders/{order_id}/send_to_production.json
            $endPoint = "https://api.printify.com/v1/shops/{$shopId}/orders/$orderId/send_to_production.json";
            $response = $this->triggerSyncRequest($endPoint, 'POST', [], [
                'Authorization: Bearer ' . $apiToken
            ]);
            if (isset($response['id'])) {

                $systemOrderId = request()->input('id');
                if ($systemOrderId) {
                    DB::table('order_meta')->insert([
                        'key' => 'in_production_time',
                        'order_id' => $systemOrderId,
                        'value' => date('Y-m-d H:i:s')
                    ]);
                }

                $responseData['status'] = self::STATUS_SUCCESSFUL;
            }
            $responseData['result'] = $response;
        }

        return $responseData;
    }

    public function getFulfillData($blueprintId = null, $printProviderId = null, $variantId = null, $productId = null)
    {
        if (!$blueprintId) {
            $blueprintId = request()->input('blueprint_id');
        }
        if (!$printProviderId) {
            $printProviderId = request()->input('print_provider_id');
        }
        if (!$variantId) {
            $variantId = request()->input('variant_id');
        }
        if (!$productId) {
            $productId = request()->input('product_id');
        }

        if (!$blueprintId || !$printProviderId || !$variantId) {
            return null;
        }

        $printifyRes = $this->triggerSyncRequest("https://printify.com/api/v1/blueprints/$blueprintId/$printProviderId",
            'GET', [], []);

        if (isset($printifyRes['print_provider']) && isset($printifyRes['print_provider']['options'])) {

            $variants = $printifyRes['print_provider']['variants'];
            $options = $printifyRes['print_provider']['options'];
            $targetVariant = null;

            foreach ($variants as $variant) {
                if ($variant['id'] == $variantId) {
                    $targetVariant = $variant;
                    break;
                }
            }

            if ($targetVariant) {

                $sizes = [];
                foreach ($options as $option) {
                    if (isset($option['type']) && $option['type'] == 'size') {
                        $sizes = $option['items'];
                        break;
                    }
                }

                $sizeIds = [];
                for ($i = 0; $i < count($sizes); $i ++) {
                    $sizeIds[] = $sizes[$i]['id'];
                }

                $selectedSize = null;
                $sizeId = array_intersect($targetVariant['options'], $sizeIds);
                if ($sizeId && count($sizeId)) {
                    $sizeId = array_pop($sizeId);
                    foreach ($sizes as $sizeObject) {
                        if ($sizeObject['id'] == $sizeId) {
                            $selectedSize = $sizeObject;
                            break;
                        }
                    }
                }

                $placeholders = [];
                if (isset($selectedSize['sizes'])) {
                    foreach ($selectedSize['sizes'] as $size) {
                        $dimensions = [
                            'width_mm' => 0,
                            'height_mm' => 0,
                            'width' => 0,
                            'height' => 0,
                            'dpi' => 0
                        ];

                        $dimensions['width_mm'] = $size['width'];
                        $dimensions['height_mm'] = $size['height'];

                        $dimensions['width'] = round($size['width'] / 25.4 * $size['recommended_dpi']);
                        $dimensions['height'] = round($size['height'] / 25.4 * $size['recommended_dpi']);
                        $dimensions['dpi'] = $size['recommended_dpi'];

                        $placeholders[$size['placeholder_id']] = $dimensions;
                    }
                }

                $designs = [];
                if ($productId) {
                    $designs = DB::table('product_n_design')
                        ->where('product_id', $productId)
                        ->join('design', 'design.id', '=', 'product_n_design.design_id')
                        ->select(['image_url'])
                        ->get()
                        ->pluck('image_url');
                }

                return [
                    'blueprint_id' => $blueprintId,
                    'print_provider_id' => $printProviderId,
                    'variant_id' => $variantId,
                    'placeholders' => $placeholders,
                    'designs' => $designs
                ];

            }
        }
    }

    public function getAutofulfillData ($order) 
    {
        $orderSchema = $this->getFulfillOrderSchema($order);

        $templates = $this->getTemplates($order['id']);

        $lineItems = [];

        if ($templates['status'] != 'successful') {
            throw new Exception("Can not get templates", 1);
        }

        $canExpressFulfill = true;

        foreach ($templates['items'] as $orderItem) 
        {
            $orderItem['canExpressFulfill'] = false;
            $lineItems[] = $this->buildLineItemv2($orderItem, $orderItem['canExpressFulfill']);

            if (!$orderItem['canExpressFulfill']) $canExpressFulfill = false;
        }

        if ($canExpressFulfill === true) {
            $orderSchema['shipping_method'] = 3; // represent for Printify Express delivery
        }

        if ($canExpressFulfill || (isset($_GET['create_product']) && $_GET['create_product'] == 1)) {
            // autoff will use default shopid 
            // call $this->printifyExpressSerivce->setShopId() for change shop credential if necessary
            $lineItems = $this->printifyExpressSerivce->getExpressLineItems($lineItems);
        }
 
        $orderSchema['line_items'] = $lineItems;

        return [
            "schema" => $orderSchema,
            "expressFulfill" => $canExpressFulfill
        ];
    }

    protected function buildLineItemv2 ($orderItem, &$isExpressFulFill)
    {   
        $template = $orderItem['template'];
        if (is_array($template)) {
            $template = (object) $template;
        }
        $printAreas = [];

        try {
            $areas = $this->getPrintLocations($orderItem);
            foreach ($areas as $area) {
                $printAreas[$area] = $this->getFulfillDesignV2($orderItem['product_sku_id'], $template->resolution, $orderItem['product_id'], null, $area);
            }
        } catch (\Exception $ex) {
            throw new Exception('buildLineItemv2 Template error: ' . json_encode($template) . ' || ' .  $ex->getMessage() . '. Line: ' . $ex->getLine());
        }

        $isExpressFulFill = $template->is_express_shipping_type == 1 && (count($printAreas) === 1 && isset($printAreas['front']));

        return [
            "print_provider_id" => intval($template->print_provider_id),
            "blueprint_id" => intval($template->blueprint_id),
            "variant_id"=> intval($template->variant_id),
            "print_areas"=> $printAreas,
            "quantity" => intval($orderItem['quantity'])
        ];
    }

    public function autoFulfillOrder ($orderId) {

        $retval = [
            'status' => self::STATUS_FAIL,
            'msg' => "Unknown error"
        ];
        $order = $this->findOrderDetail($orderId);
        if(!$order) {
            $retval['msg'] = "Order not found";
            return $retval;
        }

        $productionMode = !config('app.debug');
        if (request()->input('debug') == 'true') {
            $productionMode = false;
        }

        if ($order['resource_id'] && $productionMode) {
            $retval['msg'] = "Order already auto fulfill";
            return $retval;
        }

        $data = $this->getAutofulfillData($order);
        $retval['data'] = $data['schema'];

        if ($productionMode) {
            $retval['printify'] = $this->fulfill($order, $retval['data']);
            $retval['msg'] = "Debug off, auto fulfill printify.";
            $objectOrder = Order::query()->find($order['id']);
            $objectOrder->resource_id = $fulfillResponse['id'];
            $objectOrder->save();
        } else {
            $retval['msg'] = "Debug on, do not auto fulfill.";
        }

        $retval['status'] = 'successful';
        return $retval;
    }

    public function testAutoFulfill (Request $request ) {
        $retval = [
            'status' => self::STATUS_FAIL,
            'msg' => 'Unknown errors',
            'errors' => [],
        ];

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

        if (!$orderId) {  
            $retval['errors'][] = "Không có orderId";
            return $retval;
        }

        try {
            $retval['order'] =  $this->findOrderDetail($orderId);
            
            $retval['status'] = 'successful';
            
            $retval['data'] = $this->getAutofulfillData($retval['order']);
            
            // $retval['items']  = $retval['order']['items'];
            $retval['items'] = [];
            foreach ($retval['order']['items'] as $item) {
                $item['inputs'] = $this->getTemplateInput($item['product_sku_id']);
                $retval['items'][] = $item;
            }
            unset($retval['msg']);
            unset($retval['errors']);

        } catch (\Exception $ex) {
            $errorsMsg = 'errors on line '. $ex->getLine().' in '.$ex->getFile() .': '.$ex->getMessage();
            $retval = [
                'status' => self::STATUS_FAIL,
                'msg' => $errorsMsg,
            ];
        }
        
        return $retval;
    }

    /**
     * Auto fulfill endpoint push order to Printify
     * */
    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 ($request->input('type') && $request->input('type') == 'retry') {
                    $order['resource_id'] = null;
                }

                if ($order && (!isset($order['resource_id']) || !$order['resource_id'])) {
                    $lineItems = [];
                    $itemIds = [];

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

                        $data = $this->getAutofulfillData($order);

                        $orderSchema = $data['schema'];
                        $isExpressFulfill = $data['expressFulfill'];

                        if ($request->input('type') && $request->input('type') == 'retry') {
                            $orderSchema['external_id'] = $orderSchema['external_id'] . '_' . time();
                        }

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

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

                        if ($fulfillResponse !== false) {

                            $isExpressFulfillResponse = ( $isExpressFulfill === true && isset($fulfillResponse['data']));

                            if (isset($fulfillResponse['id']) || $isExpressFulfillResponse) {

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

                                if ($isExpressFulfillResponse) {
                                    $fulfillResponse = end($fulfillResponse['data']);
                                }

                                // update printing code
                                OrderItem::query()->whereIn('id', $itemIds)
                                    ->update([
                                        'printing_code' => 'printify',
                                        'store_id' => $this->shopId
                                    ]);

                                // check profit
                                $this->checkProfit($order, $fulfillResponse['id']);

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

                                // update printing profiles
                                // $responseData['updated_profiles'] = $this->updatePrintProfiles($order);
                                // update order status (Mr Diem update after)

                            }
                        }

                    } 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;
    }

    protected function checkProfit($order, $printifyId)
    {
        $profit = $this->getOrderProfit($order, $printifyId);

        if (!$profit || $profit <= 0) { // đơn bị lỗ
            $this->cancelOrder($printifyId);
        }

        if ($profit) {

            $orderMeta = DB::table('order_meta')->where('order_id', $order['id'])
                ->where('key', 'profit')
                ->first();

            if (!$orderMeta) {
                DB::table('order_meta')->insert([
                    'order_id' => $order['id'],
                    'key' => 'profit',
                    'value' => number_format($profit, 2, '.', '')
                ]);
            } else {
                DB::table('order_meta')->where('order_id', $order['id'])
                    ->where('key', 'profit')
                    ->update(['value' => number_format($profit, 2, '.', '')]);
            }

        }

        return $profit;
    }

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

        if ($shopId && $apiToken) {

            if ($isExpressFulFill === true) {
                $printifyRes = $this->printifyExpressSerivce->fulfill($orderSchema);
            } else {
                $printifyRes = $this->triggerSyncRequest('https://api.printify.com/v1/shops/' . $shopId . '/orders.json',
                    'POST', $orderSchema, [
                        'Authorization: Bearer ' . $apiToken
                    ]);
            }

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

            if ($printifyRes) return $printifyRes;
        }

        return false;
    }

    /**
     * Build and return line item
     * @param $orderItem
     * @return array|null
     * @throws Exception
     */
    protected function buildLineItem($orderItem)
    {
        $lineItem = null;

        /*
         * $profile = null;
         * $profile = $this->findProfile($orderItem['product_sku_id']);
         * if (false) { // line item by profile ($profile && false)
            $lineItem = $this->getLineItemByProfile($profile, $orderItem['quantity']);
        } else { // không có profile => template }
        */

        $template = $this->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;

                if ($area != 'front') {
                    $resolution = $this->getVariantResolution($template->variant_id, $template->print_provider_id, $area);
                    if (isset($resolution['height'], $resolution['width'])) {
                        $resolution = $resolution['width'] . 'x' . $resolution['height'];
                    }
                }

                $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)) { // check stock + print_aria front

                $printProviderData = $this->getPrintProviderData($template->blueprint_id, $template->print_provider_id);
                $variants = (isset($printProviderData['print_provider']['variants'])) ? $printProviderData['print_provider']['variants'] : [];

                if (gettype($variants) == 'array' && count($variants)) {

                    $targetVariant = array_filter($variants, function($v) use($template) {
                        return $v['id'] == $template->variant_id;
                    });

                    if (count($targetVariant)) $targetVariant = end($targetVariant);

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

                    if ($targetVariant['status'] == 'in-stock') {
                        $lineItem = $this->getlineItemByTemplate($template, $designs, $orderItem['quantity']);
                    } else {
                        throw new Exception('Out of stock: ' . $orderItem['product_sku_id']);
                    }
                }

            } 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));
            }// end check stock + design variant

        } 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, $printAreas, $quantity)
    {
        return [
            "print_provider_id" => intval($template->print_provider_id),
            "blueprint_id" => intval($template->blueprint_id),
            "variant_id"=> intval($template->variant_id),
            "print_areas"=> $printAreas,
            "quantity" => intval($quantity)
        ];
    }

    /**
     * get line item by profile
     * @param $profile
     * @param $quantity
     * @return array
     */
    protected function getLineItemByProfile($profile, $quantity)
    {
        $lineItem = [
            "blueprint_id" => $profile['blueprint_id'],
            "print_provider_id" => $profile['print_provider_id'],
            "variant_id" => $profile['variant_id'],
            "print_areas" => $profile['print_areas'],
            "quantity" => $quantity
        ];

        $printAreas = $profile['print_areas'];
        if (isset($profile['position']['position'])) {
            foreach ($profile['position']['position'] as $prArea => $value) {
                $urlDesign = $profile['print_areas'][$prArea];
                if (isset($profile['print_areas'][$prArea])) {
                    $printAreas[$prArea] = [array_merge(['src' => $urlDesign], $value)];
                }
            }
        }

        $lineItem['print_areas'] = $printAreas;

        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 [
            "external_id" => "printerval-" . $orderDetail['code'] . '-atff',
            "label" => "API" . $orderDetail['code'],
            "line_items" => [],
            "shipping_method" => ($orderDetail['shipping_type'] == 'express' ? 2 : 1),
            "send_shipping_notification" => false,
            "address_to" => [
                "first_name" => $firstName,
                "last_name" => $lastName,
                "email" => 'info@megaads.vn',
                "phone" => $phone,
                "country" => $orderDetail['country']['iso'],
                "region" => $orderDetail['state_name'],
                "address1" => $orderDetail['delivery_address'],
                "address2" => (isset($orderDetail['optional_address']) && $orderDetail['optional_address']) ? $orderDetail['optional_address'] : ' ',
                "city" => $orderDetail['city_name'],
                "zip" => $orderDetail['zip_code']
            ]
        ];
    }

    /**
     * get printable areas by blueprint + provider + variant
     * @param $blueprintId
     * @param $printProviderId
     * @param $variantId
     * @return array|string[]
     */
    protected function printableAreas($blueprintId, $printProviderId, $variantId)
    {
        $printifyData = DB::table('printify_order_import_data')
            ->where('print_provider_id', $printProviderId)
            ->where('blueprint_id', $blueprintId)
            ->where('variant_id', $variantId)
            ->first();

        if ($printifyData && isset($printifyData->printable_areas)) {
            return array_map(function($area) {
                return trim($area);
            }, explode(',', $printifyData->printable_areas));
        }

        return [];
    }

    /**
     * call return and cache print provider from printify
     * */
    private function getPrintProviderData($blueprintId, $printProviderId)
    {
        $dataCacheKey = 'PRINTIFY_PROVIDER_' . $blueprintId . '_' . $printProviderId;
        if (Cache::has($dataCacheKey)) {

            $printifyRes = Cache::get($dataCacheKey, []);

        } else {

            $printifyRes = $this->triggerSyncRequest("https://printify.com/api/v1/blueprints/{$blueprintId}/{$printProviderId}",'GET');

            Cache::put($dataCacheKey, $printifyRes, 86400); // cache for 24h

        }

        return $printifyRes;
    }

    /**
     * just update profile
     * */
    private function updatePrintProfiles($order)
    {
        $profileController = new ProfileController();
        $updatedProfiles = [];

        foreach ($order['items'] as $orderItem) {
            $profile = $this->buildProfile($orderItem, $order['id']);
            $saveRes = $profileController->_saveItem($profile);
            if (isset($saveRes['status'], $saveRes['data']) && $saveRes['status'] == self::STATUS_SUCCESSFUL) {
                $updatedProfiles[] = $saveRes['data'];
            }
        }

        return $updatedProfiles;
    }

    /**
     * build profile to store
     * */
    protected function buildProfile($orderItem, $orderId)
    {
        $profile = [
          'product_sku_id' => $orderItem['product_sku_id'],
          'order_id' => $orderId,
          'order_item_id' => $orderItem['id'],
          'options' => [],
        ];

        $lineItem = $this->buildLineItem($orderItem);

        if (isset($lineItem['quantity'])) unset($lineItem['quantity']);

        $printProvider = $this->getPrintProviderData($lineItem['blueprint_id'], $lineItem['print_provider_id']);

        $variants = (isset($printProvider['print_provider']['variants'])) ? $printProvider['print_provider']['variants'] : [];
        $options = (isset($printProvider['print_provider']['options'])) ? $printProvider['print_provider']['options'] : [];

        if (gettype($variants) == 'array' && count($variants)) {

            $targetVariant = array_filter($variants, function ($v) use ($lineItem) {
                return $v['id'] == $lineItem['variant_id'];
            });

            if (count($targetVariant)) $targetVariant = end($targetVariant);

            $optionIds = [
                'size' => [],
                'color' => []
            ];

            if ($options && count($options)) {
                foreach ($options as $option) {
                    $optionIds[$option['type']] = array_map(function($o) {
                        return $o['id'];
                    }, $option['items']);
                }
            }

            $optionProfileValues = [
                'color_id' => null,
                'size_id' => null
            ];

            foreach ($targetVariant['options'] as $option) {
                foreach ($optionIds as $key => $optionIdValues) {
                    if (in_array($option, $optionIdValues)) {
                        $optionProfileValues[$key . '_id'] = $option;
                        break;
                    }
                }
            }

            $profile['options'] = $optionProfileValues;

        }

        return array_merge($profile, $lineItem);
    }

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

    public function getOrderProfit($order = null, $printifyOrderId = null)
    {
        if (!$printifyOrderId) { $printifyOrderId = request()->input('order_id'); }

        if (!$order) { $order = request()->input('printerval_order_id'); }

        if ($order && is_numeric($order)) {

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

        }

        if ($printifyOrderId && $order) {

            $orderDetail = $this->getPrintifyOrderDetail($printifyOrderId);

            if (isset($orderDetail['total_price']) && isset($orderDetail['total_shipping']) && isset($order['amount'])) {

                $subTotal = doubleval($orderDetail['total_price']) + doubleval($orderDetail['total_shipping']);
                if (isset($orderDetail['total_tax'])) {
                    $subTotal = $subTotal + $orderDetail['total_tax'];
                }

                $subTotal = $subTotal / 100; // cent to usd

                return doubleval($order['amount']) - $subTotal;

            }

        }

        return false;
    }

    public function getPrintifyOrderDetail($printifyOrderId = null)
    {
        if (!$printifyOrderId) { $printifyOrderId = request()->input('order_id'); }

        if ($printifyOrderId) {

            $shopId = request()->input('shop_id');

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

            if ($shopId && $apiToken) {

                return $this->triggerSyncRequest("https://api.printify.com/v1/shops/{$shopId}/orders/{$printifyOrderId}.json",
                'GET', [], [
                    'Authorization: Bearer ' . $apiToken
                ]);

            }

        }

        return [];
    }

    public function createPrintifyProduct(Request $request)
    {
        $shopId = $request->input('shop_id');
        $data = $request->input('data');    

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

        if ($shopId && $apiToken) {

            $client = new Client([
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiToken,
                    'Content-Type' => 'application/json'
                ]
            ]);

            try {

                $httpResponse = $client->post("https://api.printify.com/v1/shops/{$shopId}/products.json", [
                    'json' => $data
                ]);

                return json_decode($httpResponse->getBody(), true);

            } catch (\Throwable $th) {}

        }

        return [];
    }

    public function cancelOrder($printifyOrderId = null)
    {
        if (!$printifyOrderId) { $printifyOrderId = request()->input('order_id'); }

        if ($printifyOrderId) {

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

            if ($shopId && $apiToken) {

                return $this->triggerSyncRequest("https://api.printify.com/v1/shops/{$shopId}/orders/{$printifyOrderId}/cancel.json",
                'POST', [], [
                    'Authorization: Bearer ' . $apiToken
                ]);

            }

        }

    }

    public function getVariantResolution($variantId = null, $providerId = null, $position = null)
    {
        if (!$variantId || !$position || !$providerId) {
            $variantId = request()->input('variant_id');
            $position = request()->input('position');
            $providerId = request()->input('provider_id');
        }

        $printifyVariant = PrintifyVariant::query()
            ->where('variant_id', $variantId)
            ->where('print_provider_id', $providerId)
            ->first();

        $placeholders = [];

        if ($printifyVariant) {

            $placeholders = json_decode($printifyVariant->placeholders, true);

            if ($position) {
                $placeholders = array_filter($placeholders, function ($placeholder) use($position) {
                    return $placeholder['position'] == $position;
                });
                $placeholders = end($placeholders);
            }

        }

        return $placeholders;
    }

    public function callPrintifyApi(Request $request)
    {
        set_time_limit(300);

        $endPoint = $request->input('end_point');
        $method = $request->input('method', 'GET');
        $params = $request->input('params', []);
        $baseApi = $request->input('base_api');
        $shopId = $request->input('shop_id');

        return $this->internalCallApi($endPoint, $method, $params, $baseApi, $shopId);
    }

    public function internalCallApi($endPoint, $method = 'GET', $params = [], $baseApi = null, $shopId = null)
    {
        if ($baseApi) {
            $this->printifyApiUrl = $baseApi;
        }
        list($shopId, $apiToken) = $this->getCredential($shopId);

        $endPoint = urldecode($endPoint);

        return $this->triggerSyncRequest($this->printifyApiUrl . $endPoint, $method, $params, [
            'Authorization: Bearer ' . $apiToken
        ]);
    }

    public function proxyAPI(Request $request) {
        $url = $request->input('url');
        return $this->triggerSyncRequest($url, 'GET', [], []);
    }

    public function proxyPOSTAPI(Request $request)
    {
        $url = $request->input('url');
        $postData = $request->input('data');

        $curl = curl_init();

        curl_setopt_array($curl, array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 0,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => 'POST',
        CURLOPT_POSTFIELDS => json_encode($postData),
        CURLOPT_HTTPHEADER => array(
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
            'Content-Type: application/json',
            'Cookie: laravel_session=eyJpdiI6Ikx3RzJQWkowclVQa2VQTHk0MndiTFE9PSIsInZhbHVlIjoiUFwvM0IxR3E0a1dMcDcwbGhibzdkVkR6dEFKN2JqM3FLY05aYkI2NkhOc0FUcDRKa3ZlenNCZGtyeUZ1aFhXaEh0dVR0U0RjZU50U0srSDVrMUFsWjRRPT0iLCJtYWMiOiIzOGQ5NWNmNDAxM2ViYzcxMGMxNzI4ZjNmNjEyY2MyMjc2MGQ2YmYwYmEyMjlhMDMwZjkwNDExOWJmMTE3ZjliIn0%3D'
        ),
        ));

        $response = curl_exec($curl);

        curl_close($curl);
        echo $response;

    }

    protected function customTriggerSyncRequest($url, $method = 'GET', $params = [], $headers = []) {
        $ch = curl_init();
        $timeout = 200;
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        if ($headers) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        if ($method != 'GET') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
            if (!empty($params)) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
                curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
            }
        }

        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        $data = curl_exec($ch);
        curl_close($ch);
        return json_decode($data, true);
    }

    public function deleteProductPrintify() {
        set_time_limit(3600);
        $endTime = strtotime("-120 days");
        $endTime = date("Y-m-d h:i:s", $endTime);
        $startTime = strtotime("-200 days");
        $startTime = date("Y-m-d h:i:s", $startTime);
        $accounts = Config::get('print-suppliers::connection.printify.accounts');
        $orders = Order::join("order_meta", function($q) {
            $q->on("order_meta.order_id", "=", "order.id");
            $q->where("key", "printify_product_ids");
        })
            ->where("created_at", ">", $startTime)
            ->where("created_at", "<", $endTime)
            ->get(["order.id", "value"]);
        foreach ($orders as $key => $order) {
            $productIds = explode(",", $order->value);
            foreach ($productIds as $productId) {
                foreach ($accounts as $shopId => $account) {
                    $this->deletePrintifyProduct($productId, $shopId, $account["api_token"]);
                }
            }
        }
        return response()->json(["status" => "successful"]);
    }

    public function deletePrintifyProduct($productPrintifyId, $shopId, $token) {
        set_time_limit(3600);

        $result = $this->customTriggerSyncRequest("https://api.printify.com/v1/shops/{$shopId}/products/{$productPrintifyId}.json",
        "DELETE", [], [
            'Authorization: Bearer ' . $token
        ]);
    }

    public function findColorCode(Request $request)
    {
        $colorName = $request->input('name');
        $slug = str_slug($colorName);

        $color = DB::table('printify_color')
            ->where('slug', $slug)
            ->whereOr('name', 'like', '% ' . $colorName . ' %')
            ->first();

        return [
            'result' => $color
        ];
    }

    public function triggerWebhookByOrder(Request $request)
    {
        $orderId = $request->input('order_id');
        $resourceIds = [];

        if ($orderId) {

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

            if ($order['items'] && count($order['items'])) {
                foreach ($order['items'] as $key => $item) {
                    if ($item['printing_code'] == 'printify' && $item['resource_id']) {
                        $resourceIds[] = $item['resource_id'];
                    }
                }
            }

            $resourceIds = array_unique($resourceIds);

        }

        if (count($resourceIds)) {

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

            foreach ($resourceIds as $resourceId) {
                // $detail = $this->getPrintifyOrderDetail($resourceId);

                $data = [
                    'resource' => [
                        "id" => $resourceId,
                        "type" => "order",
                        "data" => [
                            "shop_id" => $shopId
                        ]
                    ]
                ];
                
                $newRequest = new Request();
                $newRequest->merge($data);

                $this->printifyWebhookV2($newRequest);

            }
        }

        return 'OK';
    }


    private function mapStyle ($style) {
        $list = [
            'T-shirt premium à col en V unisexe' => 'V-neck T-shirt',
            'V-Auschnitt T-Shirt' => 'V-neck T-shirt',
            'T-shirt unisexe à sonnerie' => 'Ringer T-shirt',
            'Sweatshirt classique' => 'Classic Sweatshirt',
            'Classic Pullover' => 'Classic Sweatshirt',
            'Sweatshirt premium' => 'Premium Sweatshirt',
            'Premium Pullover' => 'Premium Sweatshirt',
            'Sweat à capuche premium' => 'Premium Hoodie',
            'T-shirt à col rond triblend unisexe' => 'Tri-blend T-shirt',
            'Sweat à capuche classique' => 'Classic Hoodie',
            'T-shirt Classique' => 'Classic T-shirt',
            'T-shirt Épais' => 'Heavyweight T-shirt',
            'Schweres T-Shirt' => 'Heavyweight T-shirt',
            'Comfort Colors T-Shirt' => 'Comfort Colors Tee',
        ];
        if (isset($list[$style])) {
            $style = $list[$style];
        }
        return $style;
    }

    private function mapType ($type) {
        $list = [
            'Unisexe' => 'Unisex',
            'Damen' => 'Women',
            'Herren' => 'Men',
            'Kinder' => 'Youth',
            'Femmes' => 'Women',
            'Hommes' => 'Men',
            'Enfant' => 'Youth',
        ];
        if (isset($list[$type])) {
            $type = $list[$type];
        }
        return $type;
    }
}
