<?php

/**
 * Manage Products from catalog Store
 *
 * PHP version 5.6
 *
 * @category  Products
 * @package   Store
 * @author    Radhanatha <radhanatham@riaxe.com>
 * @copyright 2019-2020 Riaxe Systems
 * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
 * @link      http://inkxe-v10.inkxe.io/xetool/admin
 */

namespace App\Modules\Products\Controllers;

// use Illuminate\Database\Capsule\Manager as DB;
use App\Modules\Products\Models\CatalogProductRel;
use App\Modules\Products\Models\PrintProfileDecorationSettingRel;
use App\Modules\Products\Models\PrintProfileProductSettingRel;
use App\Modules\Products\Models\ProductDecorationSetting;
use App\Modules\Products\Models\ProductImageSettingsRel;
use App\Modules\Products\Models\ProductSetting;
use App\Modules\Products\Models\ProductSide;
use App\Modules\Products\Models\ProductImage;
use App\Modules\Products\Models\ProductImageSides;
use App\Modules\Integration\Models\PluginSettings;
use App\Modules\DecorationAreas\Models\PrintArea;
use App\Modules\Orders\Models\CatalogOrderRel;
use ProductStoreSpace\Controllers\StoreProductsController;
use App\Modules\Integration\Controllers\PrintfulController;
use App\Modules\Integration\Controllers\IntegrationController;
use App\Modules\Integration\Controllers\CatalogSupplierController as CatalogSupplier;
use App\Modules\Products\Controllers\CatalogLogController as CatalogLog;
use App\Components\Controllers\Component as ParentController;

/**
 * Products catalog Controller
 *
 * @category Class
 * @package  Product
 * @author   Radhanatha <radhanatham@riaxe.com>
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @link     http://inkxe-v10.inkxe.io/xetool/admin
 */
class ProductsCatalogController extends ParentController
{

    public $logSystem = NULL;
    public $supConfig = [];
    public $catalogSupplier;
    private $storeController = null;
    /**
     * Get the instance of the store product controller
     */
    public function getStore()
    {
        if (!$this->storeController) {
            $this->storeController = new StoreProductsController();
        }
        return $this->storeController;
    }
    /**
     * GET: Get all catalog products from catalog
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function getCatalogProducts($request, $response)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'message' => "No products found"
        ];

        $page = $request->getQueryParam('page');
        $sortBy = $request->getQueryParam('sortby', 'id');
        $order = $request->getQueryParam('order', 'desc');
        $name = $request->getQueryParam('name');
        $perpage = $request->getQueryParam('perpage');
        $categoryId = $request->getQueryParam('category');
        $brandId = $request->getQueryParam('brand');
        $catalogCode = $request->getQueryParam('catalog_code');

        // do not proceed if catalog code is empty
        if (empty($catalogCode)) {
            return response($response, [
                'data' => $jsonResponse,
                'status' => $serverStatusCode
            ]);
        }
        $catalogProductRels = [];
        $params = [
            'name' => $name,
            "catalog_code" => $catalogCode,
            "category" => $categoryId,
            "brand" => $brandId,
            "sortby" => $sortBy,
            "order" => $order,
            "page" => $page,
            "perpage" => $perpage
        ];
        $productData = [];
        if ($catalogCode == 'printful') {
            $printfullObj = new PrintfulController();
            $productList = $printfullObj->printfulGet('products', $params);
            $productData = (array) $productList['data'];
        } else {
            $productList = api_call_by_curl($params, 'products');
            $productData = (array) $productList['data'];
            // get all relation data at a time
            $catalogStyles = array_column($productData, 'style_id');
            $catalogProductRels = (new CatalogProductRel())->whereIn('catalog_product_id', $catalogStyles)
                ->select('product_id', 'catalog_product_id')->pluck('product_id', 'catalog_product_id')->toArray();
        }

        $totalRecords = $productList['total_records'];
        $records = $productList['records'];
        $productArr = [];

        array_walk($productData, function ($product) use ($catalogCode, $catalogProductRels, &$productArr) {
            if ($catalogCode == "printful") {
                $existingProductID = $product['existingProductID'];
            } else {
                $existingProductID = isset($catalogProductRels[$product['style_id']]) ? $catalogProductRels[$product['style_id']] : 0;
            }

            $productArr[] = [
                'id' => $product['id'],
                'style_id' => $product['style_id'],
                'sku' => ($catalogCode == "sns" ? $product['sku'] : $product['style_id']),
                'part_number' => $product['part_number'],
                'description' => $product['description'],
                'title' => preg_replace('@\x{FFFD}@u', '', $product['title']), // invalid character replaced
                'brand_name' => $product['brand_name'],
                'base_category' => $product['base_category'],
                'categories' => $product['categories'],
                'style_image' => $product['style_image'],
                'brand_image' => $product['brand_image'],
                'price' => $product['price'],
                'existingProductID' => $existingProductID,
                'is_imported' => boolVal($existingProductID)
            ];
        });

        if ($productArr) {
            $jsonResponse = [
                'status' => 1,
                'records' => $records,
                'total_records' => $totalRecords,
                'data' => $productArr
            ];
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * GET: Check catalog product already exit or not
     *
     * @param $styleId  Product stylid
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return boolean
     */
    public function checkProductExist($styleId)
    {
        $existingProductID = 0;
        $catlogProductRelSvInit = new CatalogProductRel();
        $catalogProduct = $catlogProductRelSvInit->select('product_id');
        $catalogProduct->where('catalog_product_id', trim($styleId));
        if ($catalogProduct->count() > 0) {
            $existingProductID = $catalogProduct->first()->product_id;
        }
        return $existingProductID;
    }

    /**
     * POST: Product add to store
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     * @param $args     Slim's Arguments
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function importProucts($request, $response, $args)
    {
        ignore_user_abort(true);
        ini_set('memory_limit', '1024M');

        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'records' => 0,
            'data' => [],
            'message' => message('Products', 'not_found')
        ];

        try {
            $msg = 'Some error occurred. Please try again';
            $getStoreDetails = get_store_details($request);
            $storeId = $getStoreDetails['store_id'];
            $predecoDetails = $request->getParsedBody();
            $catalogCode = $predecoDetails['catalog_code'];
            $productData = json_clean_decode($predecoDetails['product_data']);

            // format the input data
            array_walk($productData, function (&$product) use ($catalogCode) {
                if ($catalogCode == 'printful') {
                    // $styleId = base64_decode($product['style_id']);
                    // $product['style_id'] = explode("-", $styleId)[1];
                }
                // find the old product - will be deleted/updated if exists
                if ($product['old_product_id'] == 0) {
                    if (STORE_NAME == 'Shopify') {
                        // multiple product may exists for shopify store
                        $oldProductIds = (new CatalogProductRel())->where('catalog_product_id', $product['style_id'])
                            ->select('product_id')->pluck('product_id')->toArray();
                        $product['old_product_id'] = $oldProductIds;
                    } else {
                        // single product may exist for other stores
                        $product['old_product_id'] = $this->checkProductExist($product['style_id']);
                    }
                }
            });
            // Call Internal Store: add product to store
            $predecoSaveResp = $this->getStore()->addProductToStore($productData, $catalogCode, $predecoDetails, $storeId);
            if (empty($predecoSaveResp)) {
                throw new \Exception('Store resposone error!');
            }
            $productCount = count($predecoSaveResp);
            foreach ($predecoSaveResp as $productInfo) {
                // product breaks into multiple in shopify (max 99 variants)
                // Note: the response has to made same on the other stores
                if (STORE_NAME != 'Shopify') {
                    $productInfo = [$productInfo];
                }
                array_walk($productInfo, function ($v, $indx) use ($predecoDetails, $catalogCode, $storeId) {
                    // do not proceed if product_id is not there
                    if (empty($v['product_id'])) {
                        return false;
                    }

                    // Save Product Settings Data
                    $this->saveCatalogProductSetting($v, $catalogCode, $storeId);

                    $catlogProductRelSvInit = new CatalogProductRel();

                    // deletes one time - all relation with same style (shopify may have multiple relation)
                    if ($indx == 0) {
                        $catlogProductRelSvInit->where(['catalog_product_id' => strval($v['style_id'])])->delete();
                    }

                    $catalogRelData = [
                        'product_id' => $v['product_id'],
                        'catalog_product_id' => strval($v['style_id']),
                        'catalog_code' => $catalogCode,
                        'margin_type' => ($predecoDetails['is_margin'] == 1 ? 'percentage' : 'flat'),
                        'margin_value' => number_format((float) $predecoDetails['product_margin'], 2, '.', ''),
                        'product_stored_type' => SHOPIFY_VARIANTS_PLUGIN == 1 ? 'imprint' : 'store'
                    ];

                    $catlogProductRelSvInit->insert($catalogRelData);

                    if ($catalogRelData['catalog_code'] == 'printful') {
                        $IntegrationObj = new IntegrationController();
                        $pluginId = $IntegrationObj->getPluginId($catalogRelData['catalog_code']);
                        $allPrintfulProduct = $catlogProductRelSvInit->where(['catalog_code' => $pluginId])->get()->toArray();
                        $productIds = array_column($allPrintfulProduct, 'catalog_product_id');
                        $printfullObj = new PrintfulController();
                        $printfullObj->createHookPrintfulProductStock($productIds);
                    }
                });
            }

            $jsonResponse = [
                'status' => 1,
                'message' => message('Catalog Product', 'saved')
            ];
            $msg = $productCount . ' nos of products imported successfully';
        } catch (\Exception $e) {
            $msg = 'Catalog import failed: ' . $e->getMessage();
        }

        $this->mailSendToAdmin($msg);

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }
    /**
     * Internal: Create print profilr for the newly imported product
     */
    private function createDefaultPrintProfile($printProfileIds, $prodSettInsId)
    {
        if (!empty($prodSettInsId) && !empty($printProfileIds)) {
            // create the default print-profile
            $ppProdSettRelData = array_map(function ($printProfileId) use ($prodSettInsId) {
                return [
                    'print_profile_id' => $printProfileId,
                    'product_setting_id' => $prodSettInsId
                ];
            }, $printProfileIds);

            $ppProdSettRelSvInit = new PrintProfileProductSettingRel();
            $ppProdSettRelSvInit->insert($ppProdSettRelData);
            return true;
        }
        return false;
    }

    /**
     * Internal: Get print profile for ImprintNext product
     */
    protected function productPrintAreaFromIN($storeId, $newProduct)
    {
        $thisDecorData = $newProduct['decorData'];
        if (!empty($thisDecorData['product_image_id']) && $thisDecorData['product_image_id'] > 0) {
            $prodIMGTemplate = api_call_by_curl('call To IM server', IMPRINT_CATALOG_API_URL . 'image-sides/' . $thisDecorData['product_image_id']);
            $newProdImgID = $this->saveImprintProductImage($storeId, $prodIMGTemplate['data'][0]);
        }

        $getProdDecoInfo = [
            'product_id' => $newProduct['product_id'],
            'is_crop_mark' => $thisDecorData['crop_value'] > 0 ? 1 : 0,
            'is_safe_zone' => !empty($thisDecorData['is_safe_zone']) ? $thisDecorData['is_safe_zone'] : 0,
            'is_ruler' => !empty($thisDecorData['is_ruler']) ? $thisDecorData['is_ruler'] : 0,
            'crop_value' => !empty($thisDecorData['crop_value']) ? $thisDecorData['crop_value'] : 0.00,
            'safe_value' => !empty($thisDecorData['safe_value']) ? $thisDecorData['safe_value'] : 0.00,
            'is_3d_preview' => !empty($thisDecorData['is_3d_preview']) ? $thisDecorData['is_3d_preview'] : 0,
            '3d_object' => $thisDecorData['3d_object'],
            '3d_object_file_upload' => $thisDecorData['3d_object_file_upload'],
            'scale_unit_id' => !empty($thisDecorData['scale_unit_id']) ? $thisDecorData['scale_unit_id'] : 0,
            'product_image_id' => $newProdImgID,
            'print_profile_ids' => array_column($thisDecorData['print_profile'], 'id'),
            'is_variable_decoration' => $thisDecorData['is_variable_decoration']
        ];

        $sideArr = [];
        foreach ($thisDecorData['sides'] as $side) {
            $productDecoration = [];
            foreach ($side['decoration_settings'] as $boundary) {
                $thisDimension = json_decode($boundary['dimension'], true);
                $boundaryInfo = api_call_by_curl('call To IM server', IMPRINT_CATALOG_API_URL . 'print-areas?id=' . $boundary['print_area_id']);
                $newBoundaryID = $this->saveImprintProductBoundary($storeId, $boundaryInfo['data'][0]);
                $productDecoration[] = [
                    "name" => $boundary['name'],
                    "print_area_id" => $newBoundaryID,
                    "dimension" => [
                        "x" => $thisDimension['x'],
                        "y" => $thisDimension['y'],
                        "width" => $thisDimension['width'],
                        "height" => $thisDimension['height'],
                        "type" => $thisDimension['type'],
                        "path" => $thisDimension['path'],
                        "rotate" => $thisDimension['rotate'],
                        "cx" => $thisDimension['cx'],
                        "cy" => $thisDimension['cy'],
                        "cw" => $thisDimension['cw'],
                        "ch" => $thisDimension['ch'],
                        "sx" => $thisDimension['sx'],
                        "sy" => $thisDimension['sy'],
                        "sw" => $thisDimension['sw'],
                        "sh" => $thisDimension['sh']
                    ],
                    "sub_printarea_type" => "normal_size",
                    "size_variants" => [],
                    "print_profile_ids" => $getProdDecoInfo['print_profile_ids']
                ];
            }
            $sideArr[] = [
                'name' => $side['name'],
                'is_visible' => $side['is_visible'],
                'image_dimension' => '',
                'product_image_side_id' => $side['image']['id'] > 0 ? $side['image']['id'] : 0,
                'product_decoration' => $productDecoration
            ];
        }
        $getProdDecoInfo['sides'] = $sideArr;
        return $getProdDecoInfo;
    }

    /**
     * Internal: Create print area for imprintnext product
     */
    private function saveImprintProductImage($storeId, $imgData)
    {
        if (!empty($imgData)) {
            $productImageData = [
                'name' => $imgData['name'],
                'store_id' => $storeId
            ];

            $saveProductImage = new ProductImage($productImageData);
            $saveProductImage->save();
            $productImageInsertId = $saveProductImage->xe_id;

            foreach ($imgData['sides'] as $sideData) {
                $thisImageContent = fileGetContentsCurl($sideData['file_name']);
                file_put_contents(path('abs', 'product') . $sideData['raw_file_name'], $thisImageContent);
                file_put_contents(path('abs', 'product') . "thumb_" . $sideData['raw_file_name'], $thisImageContent);
                $productImageSides = [
                    'product_image_id' => $productImageInsertId,
                    'side_name' => !empty($sideData['side_name']) ? $sideData['side_name'] : null,
                    'sort_order' => $sideData['sort_order']
                ];
                if (!empty($sideData['raw_file_name'])) {
                    $productImageSides['file_name'] = $sideData['raw_file_name'];
                }
                $saveProductImageSide = new ProductImageSides($productImageSides);
                $saveProductImageSide->save();
            }
            return $productImageInsertId;
        }
    }

    /**
     * Iinternal: Create boumdary for imprintnext product
     */
    private function saveImprintProductBoundary($storeId, $boundaryInfo)
    {
        if ($boundaryInfo['is_user_defined'] === 0) {
            return $boundaryInfo['xe_id'];
        }
        if (!empty($boundaryInfo)) {
            $random = rand();
            $targetFile = path('abs', 'print_area') . $random . ".svg";
            if (!empty($boundaryInfo['file_name'])) {
                $thissvgContent = fileGetContentsCurl($boundaryInfo['file_name']);
                file_put_contents($targetFile, $thissvgContent);
            }
            $newBoundary = [
                'store_id' => $storeId,
                'name' => $boundaryInfo['name'],
                'print_area_type_id' => $boundaryInfo['print_area_type_id'],
                'width' => $boundaryInfo['width'],
                'height' => $boundaryInfo['height'],
                'file_name' => $random . ".svg",
                'is_user_defined' => 1
            ];
            $printAreaInit = new PrintArea($newBoundary);
            if ($printAreaInit->save()) {
                return $printAreaInit->xe_id;
            }
        }

        return false;
    }

    /**
     * Post: save product setting
     *
     * @param $getProdDecoInfo Decoration data
     * @param $storeId         Store id
     *
     * @author radhanatham@riaxe.com
     * @date   05 June 2020
     * @return integer
     */
    protected function saveCatalogProductSetting($productData, $catalogCode, $storeId)
    {
        $getProdDecoInfo = [];
        if ($catalogCode == 'in') {
            $getProdDecoInfo = $this->productPrintAreaFromIN($storeId, $productData);
        } elseif (empty($productData['decorData']['old_product_id']) || STORE_NAME == 'Shopify') {
            $sideArr = array_map(function ($side) {
                return [
                    'name' =>  $side,
                    'is_visible' => 1,
                    'image_dimension' => '',
                    'product_image_side_id' => 0,
                    'product_decoration' => [
                        [
                            "name" => "A4",
                            "print_area_id" => 4,
                            "dimension" => [
                                "x" => 213,
                                "y" => 221,
                                "width" => 175.35,
                                "height" => 124.05,
                                "type" => "rect",
                                "path" => "",
                                "rotate" => false,
                                "cx" => 0,
                                "cy" => 0,
                                "cw" => 0,
                                "ch" => 0,
                                "sx" => 0,
                                "sy" => 0,
                                "sw" => 0,
                                "sh" => 0
                            ],
                            "sub_printarea_type" => "normal_size",
                            "size_variants" => [],
                            "print_profile_ids" => ["1", "2"]
                        ]
                    ]
                ];
            }, $productData['product_side']);

            $getProdDecoInfo = [
                'product_id' => $productData['product_id'],
                'is_crop_mark' => 0,
                'is_safe_zone' => 0,
                'is_ruler' => 0,
                'crop_value' => 0,
                'safe_value' => 0,
                'is_3d_preview' => 0,
                '3d_object' => '',
                '3d_object_file_upload' => '',
                'scale_unit_id' => 1,
                'product_image_id' => 0,
                'print_profile_ids' => ["1", "2"],
                'is_variable_decoration' => 0,
                'sides' => $sideArr
            ];
        }

        $productSettingId = 0;
        // If any file exist then upload
        $objectFileName = do_upload('3d_object_file', path('abs', '3d_object'), [150], 'string');

        // Processing for Table: product_settings
        $productSettData = [
            'store_id' => $storeId,
            'product_id' => (string)$getProdDecoInfo['product_id'],
            'is_crop_mark' => $getProdDecoInfo['is_crop_mark'],
            'is_ruler' => !empty($getProdDecoInfo['is_ruler']) ? $getProdDecoInfo['is_ruler'] : 0,
            'is_safe_zone' => $getProdDecoInfo['is_safe_zone'],
            'crop_value' => $getProdDecoInfo['crop_value'],
            'safe_value' => $getProdDecoInfo['safe_value'],
            'is_3d_preview' => $getProdDecoInfo['is_3d_preview'],
            '3d_object_file' => !empty($objectFileName) ? $objectFileName : null,
            '3d_object' => !empty($getProdDecoInfo['3d_object']) ? $getProdDecoInfo['3d_object'] : null,
            'scale_unit_id' => $getProdDecoInfo['scale_unit_id'],
            'dnt_inventory' => 0,
            'custom_variant' => (SHOPIFY_VARIANTS_PLUGIN == 1) ? 1 : 0,
        ];
        $productSetting = new ProductSetting($productSettData);
        $productSetting->save();
        $productSettingId = $productSetting->xe_id;

        // create print profile for the product
        $this->createDefaultPrintProfile($getProdDecoInfo['print_profile_ids'], $productSettingId);

        // Processing for Table: product_image_settings_rel
        if (!empty($productSettingId) && !empty($getProdDecoInfo['product_image_id'])) {
            $productImageSettings = new ProductImageSettingsRel([
                'product_setting_id' => $productSettingId,
                'product_image_id' => $getProdDecoInfo['product_image_id'],
            ]);
            $productImageSettings->save();
        }

        // Save Decoration Sides
        $this->saveCatalogDecorationSides($getProdDecoInfo, $productSettingId);

        return $productSettingId;
    }

    /**
     * Post: save decoration sides
     *
     * @param $decoration Decoration data
     * @param $settingsId Settings save id
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return boolean
     */
    public function saveCatalogDecorationSides($decoration, $settingsId)
    {
        // Processing for Table: product_sides, product_decoration_settings
        $imageSides = $decoration['sides'];
        if (empty($settingsId) || empty($imageSides)) {
            // do not contunue if mandatory fields are empty
            return false;
        }

        foreach ($imageSides as $productSideData) {
            $productSide = new ProductSide([
                'product_setting_id' => $settingsId,
                'side_name' => $productSideData['name'],
                'product_image_dimension' => $productSideData['image_dimension'],
                'is_visible' => $productSideData['is_visible'],
                'product_image_side_id' => $productSideData['product_image_side_id']
            ]);
            $productSide->save();

            // Product Side Insert Id
            $prodSideInsId = $productSide->xe_id;

            foreach ($productSideData['product_decoration'] as $productDecoSetting) {

                $prodDecoSettRecord = $productDecoSetting;
                $prodDecoSettRecord['product_side_id'] = $prodSideInsId;
                $prodDecoSettRecord['dimension'] = isset($productDecoSetting['dimension']) ? json_encode($productDecoSetting['dimension'], true) : "{}";
                $prodDecoSettRecord['product_setting_id'] = $settingsId;

                $prodDecoSettInit = new ProductDecorationSetting($prodDecoSettRecord);
                $prodDecoSettInit->save();
                $prodDecoSettInsId = $prodDecoSettInit->xe_id;

                if (!empty($prodDecoSettInsId) && !empty($productDecoSetting['print_profile_ids'])) {
                    // Processing for Table: print_profile_decoration_setting_rel
                    $ppDecoSettRelData = array_map(function ($printProfile) use ($prodDecoSettInsId) {
                        return [
                            'print_profile_id' => $printProfile,
                            'decoration_setting_id' => $prodDecoSettInsId
                        ];
                    }, $productDecoSetting['print_profile_ids']);

                    $ppDecoSettInit = new PrintProfileDecorationSettingRel();
                    $ppDecoSettInit->insert($ppDecoSettRelData);
                }
            }
        }
        return true;
    }

    /**
     * GET: mail send to admin
     *
     * @param $msg Total product
     *
     * @author radhanatham@riaxe.com
     * @date  25 Aug 2020
     * @return boolean
     */
    private function mailSendToAdmin($msg)
    {
        $setting = call_curl([], 'settings', 'GET');
        $printShopEmail = $setting['general_settings']['email_address_details']['to_email'];
        $subject = 'Catalog Product Import';
        $fromEmail = $setting['general_settings']['email_address_details']['from_email'];
        $smtpData = $setting['general_settings']['smtp_details'];
        $emailFormat = [
            'from' => [
                'email' => $fromEmail,
                'name' => $fromEmail
            ],
            'recipients' => [
                'to' => [
                    'email' => $printShopEmail,
                    'name' => $printShopEmail
                ],
                'reply_to' => [
                    'email' => '',
                    'name' => ''
                ],
                'cc' => [
                    'email' => '',
                    'name' => ''
                ],
                'bcc' => [
                    'email' => '',
                    'name' => ''
                ]
            ],
            'attachments' => ['', ''],
            'subject' => $subject,
            'body' => '<html>
                    <body>
                    <table width="400" border="0" cellspacing="0" cellpadding="0">
                    <tr>
                    <td>
                    <div align="center">' . $msg . '.</div>
                    </td>
                    </tr>
                    </table>
                    </body>
                    </html>',
            'smptData' => $smtpData
        ];
        if (!empty($smtpData['smtp_host']) && !empty($smtpData['smtp_user']) && !empty($smtpData['smtp_pass'])) {
            email($emailFormat);
        }
    }

    /**
     * Create name and number CSV file
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     * @param $args     Slim's Arguments
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return json
     */
    public function createProductCsvSample($request, $response, $args)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'data' => [],
            'message' => message('Product Import CSV Sample', 'error')
        ];

        $predecoDetails = $request->getParsedBody();
        $productData = json_clean_decode($predecoDetails['product_data']);

        if (!empty($productData)) {
            $csvFilename = $this->getStore()->createProductImportCSV($request, $response, $args);
            $jsonResponse = [
                'status' => 1,
                'data' => $csvFilename,
                'message' => message('Catalog CSV Sample', 'saved')
            ];
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * Download name and number csv sample file
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     * @param $args     Slim's Arguments
     *
     * @author radhanatham@riaxe.com
     * @date   05 June 2020
     * @return json
     */
    public function downloadProductCsvSample($request, $response, $args)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'message' => message('Catalog Sample', 'error')
        ];

        $assetsPath = ASSETS_PATH_W;
        $catalogAssetsPath = $assetsPath . 'catalog/';

        $fileName = $request->getQueryParam('filename');

        if (!empty($fileName)) {
            $downloadFilepath = $catalogAssetsPath . $fileName;
            if (file_exists($downloadFilepath)) {
                file_download($downloadFilepath);
                $jsonResponse = [
                    'status' => 1,
                    'message' => 'Catalog Sample File Downloaded Successful'
                ];
            } else {
                $jsonResponse['message'] = 'Error In Catalog Sample File Download';
            }
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * GET: Get all catalogs
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function getAllCatalog($request, $response)
    {
        $serverStatusCode = OPERATION_OKAY;
        $params = [];
        $catalogList = api_call_by_curl($params, '');
        return response($response, [
            'data' => $catalogList,
            'status' => $serverStatusCode
        ]);
    }
    /**
     * GET: Get all catalogs category
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function getCatalogCategory($request, $response)
    {
        $serverStatusCode = OPERATION_OKAY;
        $catalogCode = $request->getQueryParam('catalog_code');
        $params = ['catalog_code' => $catalogCode];
        $catalogCategoryList = api_call_by_curl($params, 'categories');
        return response($response, [
            'data' => $catalogCategoryList,
            'status' => $serverStatusCode
        ]);
    }
    /**
     * GET: Get all catalogs brands
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function getCatalogBrand($request, $response)
    {
        $serverStatusCode = OPERATION_OKAY;
        $catalogCode = $request->getQueryParam('catalog_code');
        $params = ['catalog_code' => $catalogCode];
        $catalogBrandList = api_call_by_curl($params, 'brand');
        return response($response, [
            'data' => $catalogBrandList,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * GET: Get product details
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author radhanatham@riaxe.com
     * @date  05 June 2020
     * @return array json
     */
    public function getProductDetails($request, $response)
    {
        $serverStatusCode = OPERATION_OKAY;
        $catalogCode = $request->getQueryParam('catalog_code');
        $styleId = $request->getQueryParam('style_id');
        $params = ['catalog_code' => $catalogCode, 'style_id' => $styleId];

        if ($catalogCode == 'printful') {
            $printfullObj = new PrintfulController();
            $productDetails = $printfullObj->getPrintfulProductDetail($styleId, 'all');
        } else {
            $productDetails = api_call_by_curl($params, 'product');
        }

        if (empty($productDetails['data']['color_data']) || empty($productDetails['data']['size_data'])) {
            foreach ($productDetails['data']['attributes'] as $attribute) {
                if (strtolower($attribute['name']) == "color" || strtolower($attribute['name']) == "colour") {
                    $productDetails['data']['color_data'] = $attribute['options'];
                }
                if (strtolower($attribute['name']) == "size") {
                    $productDetails['data']['size_data'] = $attribute['options'];
                }
            }
        }

        // remove invalid charaters from the result's field'
        $productDetails['data']['name'] = preg_replace('@\x{FFFD}@u', '', $productDetails['data']['name']);
        array_walk($productDetails['data']['variations'], function (&$variation) {
            $variation['style_name'] = preg_replace('@\x{FFFD}@u', '', $variation['style_name']);
        });

        return response($response, [
            'data' => $productDetails,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * GET: Get status of the import product
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     *
     * @author mukeshp@riaxe.com
     * @date  09 Oct 2020
     * @return array json
     */
    public function getImportProductStatus($request, $response, $args)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 1,
            'message' => "Processing"
        ];

        if (isset($args['id'])) {
            $catProductId = $args['id'];
            // check if the product id exists
            $productId = $this->checkProductExist($catProductId);
            if ($productId) {
                $jsonResponse = [
                    'status' => 1,
                    'message' => 'Completed'
                ];
            }
        }
        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode,
        ]);
    }

    /**
     * Note: This method is for printful products only.
     * Please refer 'updateCentralCatalog' method - for other providers
     * ---------------------------------------------------------
     *
     * Performs  Catalog product stock update
     *
     * Accepts the productIds  to perform the request on
     *
     * @param $args array
     * @return string
     */

    public function deleteProductCatlog($request, $response, $args)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'message' => message('product catlog ', 'error'),
        ];

        if (!empty($args['id'])) {
            $productId = $args['id'];
            $prodcatInit = new CatalogProductRel();
            $count = $prodcatInit->where('product_id', $productId)->count();
            if ($count > 0) {
                //delete product_id
                CatalogProductRel::where('product_id', $productId)->delete();

                $jsonResponse = [
                    'status' => 1,
                    'message' => 'Product catalog deleted successfully!',
                ];
            }
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }






    public function catalogsStockUpdate($request, $response, $args)
    {
        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'message' => "Error in Update"
        ];
        // get the post type
        $postType = $request->getParsedBodyParam('type');
        switch ($postType) {
            case 'stock_updated':
                $jsonResponse = $this->printfulStockUpdate($request, $response);
                break;
            case 'package_shipped':
                $allValues = $request->getParsedBody();
                $storeOrderId =  $allValues['order']['external_id'];
                $shipping = json_encode($allValues['shipment'], true);
                $catalogOrderRelObj = new CatalogOrderRel();
                $catalogOrderRelObj->where('order_id', $storeOrderId)->update(["shipping" => $shipping]);
                $jsonResponse = [
                    'status' => 1,
                    'message' => 'Shiiping Detail Updated'
                ];
                break;
            default:
                break;
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * Internal: Printful stock update
     */
    private function printfulStockUpdate($request, $response)
    {
        $jsonResponse = [
            'status' => 0,
            'message' => "Error in stock update"
        ];
        $getStoreDetails = get_store_details($request);
        $storeId = $getStoreDetails['store_id'];
        $allValues = $request->getParsedBody();
        // get the printful plugin ID
        $settingObj = new IntegrationController();
        $pluginId = $settingObj->getPluginId('printful');

        $productId = $allValues['product_id'];
        // the variants which are out of stock
        $variantIds = array_merge($allValues['variant_stock']['out'], $allValues['variant_stock']['discontinued']);
        // get the printful catalog product from the relation table
        $catlogProductRelSvInit = new CatalogProductRel();
        $catalogProductRel = $catlogProductRelSvInit->where([
            ['catalog_product_id', '=', $productId],
            ['catalog_code', '=', $pluginId]
        ])->first();

        // keeps all the updated variant ids
        $selectedVarIdMsg = '';
        if (!empty($catalogProductRel)) {
            $storeProductId = $catalogProductRel->product_id;
            $productArray = [];
            // get all the variants from the store
            $result = $this->getStore()->productVariants($storeProductId, $storeId);
            if ($result['status'] == 1) {
                $catalogProductVariants = $result['variants'];
                // make a list of variants which stock will be 0
                foreach ($catalogProductVariants as $variantValue) {
                    $vid = base64_decode($variantValue['sku']);
                    $vid = explode("-", $vid)[2];
                    if (in_array($vid, $variantIds)) {
                        $selectedVarIdMsg .= "#" . $variantValue['id'];
                        $productArray[] = [
                            "productId" => $storeProductId,
                            "variantId" => $variantValue['id'],
                            "stock" => 0
                        ];
                    }
                }
                // call to store to update the stocks
                $storeResponse = $this->getStore()->stockPriceUpdate($productArray);

                $storeResponse = $storeResponse['status'];
                if ($storeResponse == 1) {
                    $message = "printful product stock Updated Successful";
                } else {
                    $message = "Error in printful product stock Update";
                }
                create_log('activity', 'info', [
                    'message' => $message,
                    'extra' => [
                        'pid = ' . $storeProductId . '- Variant Ids ' . $selectedVarIdMsg
                    ]
                ]);
            } else {
                // delete the relation, since product is no more
                $catalogProductRel->delete();
                create_log('activity', 'info', [
                    'message' => 'Product not found on the store',
                    'extra' => [
                        'pid = ' . $storeProductId
                    ]
                ]);
            }
            $jsonResponse = [
                'status' => $storeResponse,
                'message' => $message
            ];
        }
        return $jsonResponse;
    }

    /** Note: due to dependent changes from Log-System - this frunction is not optimized
     * Get: Update the catalog product inventory from the API (CRON JOB ENDPOINT)
     *
     * @param $request  Slim's Request object
     * @param $response Slim's Response object
     * @param $args     Slim's Argument parameters
     *
     * @author dan@imprintnext.com
     * @date   06 oct 2022
     * @return json
     */
    public function updateCentralCatalog($request, $response, $args)
    {
        ignore_user_abort(true);
        ini_set('max_execution_time', '3000');

        $serverStatusCode = OPERATION_OKAY;
        $jsonResponse = [
            'status' => 0,
            'message' => 'Could not update the catalog'
        ];
        try {
            $getStoreDetails = get_store_details($request);
            $storeId = $getStoreDetails['store_id'];
            // at this time provider = sanmar
            $this->logSystem = new CatalogLog();

            $this->catalogSupplier = new CatalogSupplier();

            // get supplier data from one place (backward compartibility code added there - will be removed)
            $getSuppliersData = $this->catalogSupplier->getSuppliersData();

            $allSupplierConfig = $getSuppliersData->slug_data;

            // config contains - margin_type, margin_value
            $this->supConfig = $allSupplierConfig['config'];
            unset($allSupplierConfig['config']);

            // decrypt the necessary fields
            $allSupplierConfig = array_filter($allSupplierConfig, function ($supConfig, $provider) {
                if (!$supConfig['auto_sync']) {
                    return false;
                }
                return $this->catalogSupplier->checkApiAuth($supConfig, $provider);
            }, ARRAY_FILTER_USE_BOTH);

            $validProviders = array_keys($allSupplierConfig);

            // get all the catalog product
            $catlogProductRelsInit = new CatalogProductRel();
            $catalogProducts = $catlogProductRelsInit->select('*')->whereIn('catalog_code', $validProviders)
                ->where(function ($query) {
                    // get 1 day old updated
                    return $query->where('last_updated', '<', date('Y-m-d H:i:s', strtotime("-1 days")))
                        ->orWhereNull('last_updated');
                });
            $catalogProducts = $catalogProducts->orderBy('product_id', 'asc')->take(7)->get();
            if ($catalogProducts->count() > 0) {
                foreach ($catalogProducts as $catalogProduct) {
                    $this->updateCatalogStock($catalogProduct, $storeId);
                    // update the last update time for the product
                    $catlogProductRelsInit->where('product_id', $catalogProduct->product_id)->update(["last_updated" => date('Y-m-d H:i:s')]);
                }
                $jsonResponse = [
                    'status' => 1,
                    'message' => 'Catalog updated successfully.'
                ];
            }
        } catch (\Exception $e) {
            $serverStatusCode = EXCEPTION_OCCURED;
            $jsonResponse['message'] =  $e->getMessage();
        }

        return response($response, [
            'data' => $jsonResponse,
            'status' => $serverStatusCode
        ]);
    }

    /**
     * Internal - catalog supplier product stock, price update
     */
    private function updateCatalogStock($catalogProduct, $storeId)
    {
        $this->logSystem->setCommonData([
            'style_id' => $catalogProduct->catalog_product_id,
            'product_id' => $catalogProduct->product_id,
            'provider' => $catalogProduct->catalog_code
        ]);

        $failedVariants = [];
        $productNewStocks = [];
        $catalogColorCode = [];
        $provider = $catalogProduct->catalog_code;
        // get the product variants
        $result = $this->getcatalogVariants($catalogProduct->product_id, $storeId);
        // if product variants not found, product does not exists, delete from relation
        if ($result['status'] == 0) {
            // *** do not delete relation - since store method needs amend to detect if product exists
            // $catlogProductRelsInit = new CatalogProductRel();
            // $catlogProductRelsInit->where('product_id', $catalogProduct->product_id)->delete();
            // $this->logSystem->createLog('product not found on the store, relationship deleted', [
            //     'tags' => ['notfound', 'store']
            // ]);
            return false;
        }
        $productVariants = $result['variants'];
        if ($provider == 'sanmar') {
            $catalogColorCode = $this->getCatalogColors($catalogProduct->catalog_product_id);
            if (!$catalogColorCode) {
                $this->logSystem->createLog('catalog color code not found on the server', [
                    'tags' => ['color', 'api', 'central', 'status'],
                    'inventory_status' => ['success' => [], 'failed' => ['all']],
                    'price_status' => ['success' => [], 'failed' => ['all']]
                ]);
                return false;
            }
        }
        array_walk($productVariants, function ($productVariant) use ($catalogProduct, $provider, $catalogColorCode, &$productNewStocks, &$failedVariants) {
            $supConfig = $this->supConfig;
            // set values for single product
            $singleProductData = [
                "productId" => $catalogProduct->product_id,
                "variantId" => $productVariant['id']
            ];

            // api parameters for the catalog
            $apiParams = [
                'type' => 'inventory',
                'product_id' =>  $catalogProduct->catalog_product_id,
                'color' => ucwords($productVariant['color']),
                'size' => $productVariant['size'],
                'sku' => $productVariant['sku']
            ];
            if ($provider == 'sanmar') {
                // color code is received from the central server (for sanmar)
                // skip the product if catalog color code(mandatory) is not found
                if (!isset($catalogColorCode[$apiParams['color']])) {
                    $this->logSystem->createLog('catalog color code not matched', [
                        'variant_id' => $productVariant['id'],
                        'sku' => $productVariant['sku'],
                        'color_name' =>  $apiParams['color'],
                        'tags' => ['color']
                    ]);
                    $failedVariants[] = strval($productVariant['id']);
                    return false;
                }
                $apiParams['color'] = $catalogColorCode[$apiParams['color']];
            }

            // get the inventory detail from the catalog server
            $getCatalogInv = $this->catalogSupplier->getApiData($provider, $apiParams);
            // for SNS stock price in one API
            if ($provider == 'sns') {
                $getCatalogPrice = $getCatalogInv;
            }
            if ($getCatalogInv['status'] == 1) {
                // sum all inventory quantity
                $singleProductData['stock'] = $getCatalogInv['stock'];
            } else {
                $singleProductData['stock'] = 0;
                $this->logSystem->createLog('Inventory could not be fetched', [
                    'variant_id' => $productVariant['id'],
                    'sku' => $productVariant['sku'],
                    'api_params' => $apiParams,
                    'tags' => ['inventory', 'api', $provider]
                ]);
                $failedVariants[] = strval($productVariant['id']);
            }

            if ($singleProductData['stock'] == 0) {
                $this->logSystem->createLog('Price update skipped, stock is 0', [
                    'variant_id' => $productVariant['id'],
                    'sku' => $productVariant['sku'],
                    'api_params' => $apiParams,
                    'tags' => ['price']
                ]);

                $productNewStocks[] = $singleProductData;
                // do not proceed with price update, since no stock is there
                return false;
            }

            // get the pricing detail from the catalog server
            $apiParams['type'] = 'price';
            if ($provider != 'sns') {
                $getCatalogPrice = $this->catalogSupplier->getApiData($provider, $apiParams);
            }
            if ($getCatalogPrice['status'] == 1) {
                // add profit margin to the product price
                $newPrice = $getCatalogPrice['price'];

                // get the product base margin
                $marginType = $catalogProduct->margin_type;
                $marginValue = floatval($catalogProduct->margin_value);
                // get the global margin if not set at product level
                if ((empty($marginType) || empty($marginValue)) && !empty($supConfig['margin_type'])) {
                    $marginType = $supConfig['margin_type'];
                    $marginValue = floatval($supConfig['margin_value']);
                }

                if ($marginType) {
                    $newPrice += floatval(
                        $marginType == 'flat' ? $marginValue : $newPrice * $marginValue * 0.01
                    );
                    $newPrice =  number_format((float) $newPrice, 2, '.', '');
                }

                // price update at the store
                $singleProductData['price'] = $newPrice;
            } else {
                $this->logSystem->createLog("Pricing data could not be fetched from $provider", [
                    'api_params' => $apiParams,
                    'tags' => ['price', 'api', $provider],
                    'variant_id' => $productVariant['id'],
                    'sku' => $productVariant['sku']
                ]);
            }
            $productNewStocks[] = $singleProductData;
        });

        $pVariants = array_column($productVariants, 'sku', 'id');

        // update the inventory/price for all variant at a time
        $result = $this->getStore()->stockPriceUpdate($productNewStocks);
        if ($result['status'] == 0) {
            $successStockSkus = $successPriceSkus = [];
            $failedStockSkus = $failedPriceSkus = array_values($pVariants);
        } else {
            $failedStockSkus = array_values(array_intersect_key($pVariants, array_flip($failedVariants)));
            $successStockSkus = array_values(array_diff_key($pVariants, array_flip($failedStockSkus)));

            // we do not have store status for price, so queued vids for price are treated as success
            $queuedPriceVids = array_keys(array_column($productNewStocks, 'price', 'variantId'));
            $successPriceSkus = array_values(array_intersect_key($pVariants, array_flip($queuedPriceVids)));
            $failedPriceSkus = array_values(array_diff($pVariants, $successPriceSkus));
        }
        $stockStatus = ['success' => $successStockSkus, 'failed' => $failedStockSkus];
        $priceStatus = ['success' => $successPriceSkus, 'failed' => $failedPriceSkus];

        $this->logSystem->createLog('Catalog update completed successfully for the product: ' . $catalogProduct->catalog_product_id, [
            'tags' => ['store', 'status'],
            'inventory_status' => $stockStatus,
            'price_status' => $priceStatus
        ]);
    }
    /**
     * get the product variants from cache or save cache
     */
    private function getcatalogVariants($productId, $storeId)
    {
        $cacheDir = ASSETS_PATH_W . 'catalog/';

        $productVariants = [];
        $cacheVariantsFile = $cacheDir . $productId . '.json';
        $cacheForStore = (STORE_NAME == 'Shopify' || STORE_NAME == 'Bigcommerce');

        if ($cacheForStore && file_exists($cacheVariantsFile)) {
            return json_decode(file_get_contents($cacheVariantsFile), true);
        }

        // get the product variant list from the store
        $productVariants = $this->getStore()->productVariants($productId, $storeId);
        $variantData = json_encode($productVariants);

        if ($cacheForStore) {
            // save in to the cache dir
            file_put_contents($cacheVariantsFile, $variantData);
        }

        return $productVariants;
    }

    /**
     * Internal: Get catalg color from the central server for the style id
     */
    private function getCatalogColors($styleId)
    {
        // get the catalog color name/code from the central server(not present on the product data)
        $centralColorUrl = CATALOG_API_URL . 'catalog/services/api/v1/catalogs/get-color-codes';

        // get the color data from the central server
        $params = ['style_id' => $styleId];

        $centralResponse = fileGetContentsCurl($centralColorUrl . '?' . http_build_query($params));
        $centralResponse = !empty($centralResponse) ? json_decode($centralResponse, true) : [];
        if (empty($centralResponse['status'])) {
            return false;
        }

        return $centralResponse['data'];
    }
}
