<?php

namespace Modules\PrintSuppliers\Controllers;

use App\Utils\SkuTemplateHelper;
use Illuminate\Http\Request;
use Modules\PrintSuppliers\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Modules\PrintSuppliers\Models\Country;
use Modules\PrintSuppliers\Models\Profile;
use Modules\PrintSuppliers\Models\Design;
use Modules\PrintSuppliers\Models\Order;
use Modules\PrintSuppliers\Models\OrderMeta;
use Modules\PrintSuppliers\Models\OrderItem;
use Modules\PrintSuppliers\Models\Zipcode;
use Illuminate\Support\Facades\Log;

class ProfileController extends Controller {

    const STATUS_SUCCESSFUL = 'successful';
    const STATUS_FAIL = 'fail';

    public function find(Request $request)
    {
        $type = $request->input('type', 'printify');
        $designs = [];
        $profiles = [];
        $productTemplateIds = [];

        $retVal = [
            'status' => self::STATUS_FAIL
        ];

        if ($request->has('order_items')) {

            $orderItems = $request->input('order_items');
            foreach($orderItems as $item) {
                $profile = null;

                $isTemplate = false;
//                if (class_exists('App\Utils\SkuTemplateHelper')) {
//                    $isTemplate = SkuTemplateHelper::isExists($item['product_id']);
//                }
                // get design by product_id
                $itemsDesigns = $this->getDesignByProductId($item['product_id']);
                if ($isTemplate) {
                    $profile = $this->getProfile($item, $type, true);
                    $productTemplateIds[] = $item['product_id'];
                } else {
                    // get design by sku_id
//                    $itemsDesigns = $this->getDesignBySkuId($item['product_sku_id']);
                    $profile = $this->getProfile($item, $type, false);
                }

                if ($profile) $profiles[] = $profile;
                if (count($itemsDesigns)) {
                    $tempItemDesigns = [];
                    foreach ($itemsDesigns as $designItem) {
                        $duplicateCheck = array_filter($designs, function($d) use ($designItem) {
                            return $d->url == $designItem->url;
                        });
                        if (count($duplicateCheck)) {
                            continue;
                        }
                        $designs[] = $designItem;
                        $tempItemDesigns[] = $designItem;
                    }
                }
            }

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

            $retVal['product_template_ids'] = array_values(array_unique($productTemplateIds));
            $retVal['profiles'] = $profiles;
            $retVal['designs'] = $designs;

        } else {
            $retVal['message'] = 'skuIds missing param!';
        }

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

    public function findByProductIds($request)
    {

        $retVal = [
            'status' => self::STATUS_FAIL
        ];
        if ($request->has('productIds')) {
            $productIds = explode('-', $request->get('productIds', null));

            $productSkus = DB::table('product_sku')
                ->join('product_n_design', 'product_sku.product_id', '=', 'product_n_design.product_id')
                ->join('design', 'product_n_design.design_id', '=', 'design.id')
                ->whereIn('product_sku.product_id', $productIds)
                ->select('design.image_url', 'product_n_design.is_primary', 'product_sku.id AS sku_id', 'design.resolution', 'design.source')
                ->get()
                ->toArray();
            if (count($productSkus) == 0) {
                $oldProductSku = DB::table('old_product_sku_value')
                    ->whereIn('old_product_sku_value.product_id', $productIds)
                    ->join('product_n_design', 'old_product_sku_value.product_id', '=', 'product_n_design.product_id')
                    ->join('design', 'product_n_design.design_id', '=', 'design.id')
                    ->groupBy('old_product_sku_value.sku_id')
                    ->select('design.image_url', 'product_n_design.is_primary', 'old_product_sku_value.sku_id')
                    ->get()
                    ->toArray();
                $productSkus = array_merge($productSkus, $oldProductSku);
            }
            $designs = [];
            $skuIds = [];
            foreach ($productSkus as $item) {
                if (empty($designs[$item->sku_id])) {
                    $designs[$item->sku_id] = [];
                }
                if ($item->is_primary) {
                    array_unshift($designs[$item->sku_id], [
                        'url' => $item->image_url
                    ]);
                } else {
                    array_push($designs[$item->sku_id], [
                        'url' => $item->image_url
                    ]);
                }
                $skuIds[] = $item->sku_id;
            }

            $retVal['status'] = self::STATUS_SUCCESSFUL;
            $retVal['data'] = Profile::whereIn('product_sku_id', $skuIds)
                ->where('type', '=', $request->input('type', 'printify'))
                ->orderBy('id', 'ASC')->get();
            $retVal['designs'] = $designs;
        } else {
            $retVal['message'] = 'productIds missing param!';
        }

        return $retVal;

    }

    protected function getProfile($item, $type, $isTemplate) {
        if (!$isTemplate) {
            return Profile::where('product_sku_id', $item['product_sku_id'])
                ->where('product_id', $item['product_id'])
                ->where('type', '=', $type)
                ->where('created_at', '>=', '2024-08-07 12:00:00')
                ->where(function($q) {
                    $q->whereNull('is_template_product');
                    $q->orWhere('is_template_product', '!=', 1);
                })
                ->first();
        } else {
            return Profile::where('product_id', $item['product_id'])
                ->where('type', '=', $type)
                ->where('created_at', '>=', '2024-08-06 12:00:00')
                ->where('is_template_product', 1)
                ->first();
        }
    }

    protected function getDesignBySkuId($skuId)
    {
        $productSku = DB::table('product_sku')
            ->join('product_n_design', 'product_sku.product_id', '=', 'product_n_design.product_id')
            ->join('design', 'product_n_design.design_id', '=', 'design.id')
            ->where('product_sku.id', $skuId)
            ->select('design.image_url as url', 'product_n_design.is_primary', 'product_sku.id AS sku_id', 'product_n_design.product_id')
            ->orderBy('product_n_design.is_primary')
            ->get()
            ->toArray();

        if (!$productSku) {
            $productSku = DB::table('old_product_sku_value')
                ->where('sku_id', $skuId)
                ->join('product_n_design', 'old_product_sku_value.product_id', '=', 'product_n_design.product_id')
                ->join('design', 'product_n_design.design_id', '=', 'design.id')
                ->groupBy('old_product_sku_value.sku_id')
                ->select('design.image_url as url', 'product_n_design.is_primary', 'old_product_sku_value.sku_id', 'product_n_design.product_id')
                ->orderBy('product_n_design.is_primary')
                ->get()
                ->toArray();
        }

        return array_map(function($element) {
            $element->type = 'design_sku';
            return $element;
        }, $productSku);
    }

    protected function getDesignByProductId($productId)
    {
        $productSku = DB::table('product_n_design')
            ->join('design', 'product_n_design.design_id', '=', 'design.id')
            ->where('product_n_design.product_id', $productId)
            ->select(['design.id as design_id', 'design.image_url as url', 'design.resolution', 'design.source', 'product_n_design.is_primary', 'product_n_design.product_id'])
            ->orderBy('product_n_design.is_primary')
            ->get()
            ->toArray();

        $productSkuComfirm = DB::table('product_n_design')
            ->join('design', 'product_n_design.design_id', '=', 'design.id')
            ->join('pod_design_meta', 'pod_design_meta.design_id', '=', 'design.id')
            ->where('product_n_design.product_id', $productId)
            ->where('pod_design_meta.key', 'is_confirmation')
            ->where('pod_design_meta.value', 1)
            ->select(['design.id as design_id'])
            ->orderBy('product_n_design.is_primary')
            ->get()
            ->toArray();

        foreach ($productSku as &$item) {
            $isComfirm = array_filter($productSkuComfirm, function($prodItem) use ($item) {
                return $prodItem->design_id  == $item->design_id;
            });

            $item->is_design_comfirm = false;
            if ($isComfirm && count($isComfirm)) {
                $item->is_design_comfirm = true;
            }
        }

        return array_map(function($element) {
            $element->type = 'design_template';
            return $element;
        }, $productSku);
    }

    public function save(Request $request)
    {
        $retVal = [
            'status' => self::STATUS_FAIL
        ];
        $inputs = $request->all();

        if (is_array($inputs) && count($inputs) > 0) {
            try
            {
                $results = [];
                foreach ($inputs as $input) {
                    $input['is_template_product'] = 0;
                    $result = $this->_saveItem($input);
                    if (!empty($result['data'])) {
                        $results[] = $result['data'];
                    }
                }
                $retVal['status'] = self::STATUS_SUCCESSFUL;
                $retVal['data'] = $results;

            } catch (\Exception $exception) {

                $exLocation = $exception->getFile() . ': line ' . $exception->getLine();
                $exMessage = $exception->getMessage();

                $retVal['exception'] = [
                    'file' => $exLocation,
                    'message' => $exMessage,
                    'input' => $input,
                ];

                if (isset($inputs[0]['order_id'])) {
                    DB::table('log')->insert([
                        'actor_email' => 'system',
                        'target_id' => $inputs[0]['order_id'],
                        'target_type' => 'ORDER',
                        'event_type' => 'ERROR_SAVE_PROFILE',
                        'data' => json_encode($retVal),
                        'created_at' => date('Y-m-d H:i:s')
                    ]);
                }

                $retVal['message'] = $exMessage . ' | ' . $exLocation;
            }

        } else {
            $retVal['message'] = 'product_sku_id missing param!';
        }

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

    public function logFulfill(Request $request) {

        $eventType = $request->input('event_type', 'PRINTIFY-FULFILLMENT');
        $targetType = $request->input('target_type', 'ORDER');

        $insert = DB::table('log')->insert([
            'actor_email' => $request->input('actor_email'),
            'target_id' => $request->input('target_id'),
            'target_type' => $targetType,
            'event_type' => $eventType,
            'data' => json_encode($request->input('data')),
            'created_at' => date('Y-m-d H:i:s')
        ]);

        if ($insert) {
            return $request->all();
        }
    }

    public function attachOrderMeta(Request $request)
    {
        $orderId = $request->input('order_id');
        $shopId = $request->input('shop_id');
        $key = $request->input('key');
        $value = $request->input('value');

        if (Order::query()->where('id', $orderId)->exists()) {

            if ($request->input('resource_id')) {
                Order::query()->where('id', $orderId)->update([
                    'resource_id' => $request->input('resource_id')
                ]);
            }

            if ($key && $value) {
                OrderMeta::query()->insert([
                    'order_id' => $orderId,
                    'key' => $key,
                    'value' => $value
                ]);
            }

            $printProviders = $request->input('print_provider');
            if ($printProviders && count($printProviders)) 
            {
                foreach ($printProviders as $orderItemId => $printProvider) {
                    DB::table('order_item')->where('id', $orderItemId)
                        ->update([
                            'print_provider' => $printProvider
                        ]);
                }
            }

            if ($shopId) {
                DB::table('order_item')->where('order_id', $orderId)
                ->update([
                    'store_id' => $shopId
                ]);
            }

            DB::table('log')->insert([
                'target_id' => $orderId,
                'target_type' => 'ORDER',
                'data' => json_encode([
                    'meta' => [
                        'order_id' => $orderId,
                        'key' => $key,
                        'value' => $value
                    ],
                    'print_provider' => $printProviders
                ]),
                'event_type' => 'attachOrderMeta',
                'created_at' => new \DateTime()
            ]);

            return [
                'status' => 'successful',
                'order_id' => $orderId,
            ];
        }
    }

    public function _saveItem($input)
    {
        $retVal = [];


        // save profiles
        $profileType = (isset($input['type']) && $input['type']) ? $input['type'] : 'printify';
        $profileQuery = Profile::query()
            ->where('type', '=', $profileType);

        $isTemplate = isset($input['is_template_product']) && $input['is_template_product'];

        $profileQuery->where('product_id', '=', $input['product_id']);
        $profileQuery->where('product_sku_id', '=', $input['product_sku_id']);
//        if ($isTemplate) {
//            $profileQuery->where('product_id', '=', $input['product_id']);
//            $profileQuery->where('is_template_product', 1);
//        } else {
//            $profileQuery->where('product_sku_id', '=', $input['product_sku_id']);
//        }

        try {

            // check dub
            $q = clone $profileQuery;
            $countProfiles = $q->get()->count();
            if ($countProfiles > 1) {
                $q = clone $profileQuery;
                $q->delete();
            }

            $profile = $profileQuery->first();

            if (empty($profile)) {
                $profile = new Profile;
            }

            $profile = $this->saveProfileData($input, $profile, $isTemplate);

        } catch (\Exception $e) {

            DB::table('log')->insert([
                'target_id' => $input['order_id'],
                'target_type' => 'PROFILE_ITEM',
                'data' => json_encode([
                    'error' => $e->getMessage(),
                    'input' => $input
                ]),
                'event_type' => 'ERROR_SAVE_PROFILE',
                'created_at' => new \DateTime()
            ]);

            $profile = $profileQuery->first();
            if ($profile) {
                $profile = $this->saveProfileData($input, $profile, $isTemplate);
            }

        }

        // save order items
        if (!empty($input['variant_id'])) { // printify
            $updateData = ['resource_item_id' => $input['variant_id']];
        } else if (!empty($input['options']['productUid'])) { // gelato
            $updateData = ['resource_item_id' => md5($input['options']['productUid'])];
        } else {
            $updateData = [ 'resource_item_id' => $input['order_item_id'] ];
        }

        DB::table('order_item')
            ->where('id', '=', $input['order_item_id'])
            ->update($updateData);

        $this->logProfile([
            'updateData' => $updateData,
            'order_id' => $input['order_id'],
            'input' => $input
        ]);

        // save item design
        if ($profile) {
            $retValProfile = Profile::find($profile->id);
            if (!empty($retValProfile->print_areas)) {
                $this->_saveDesign($retValProfile->print_areas, $retValProfile->product_sku_id);
            }
    
            $retVal['status'] = self::STATUS_SUCCESSFUL;
            $retVal['data'] = $retValProfile;
        }

        return $retVal;
    }

    private function saveProfileData($input, $profile, $isTemplate = false)
    {
        if (isset($input['print_areas'])) {
            $input['print_areas'] = json_encode($input['print_areas']);
        }
        if (isset($input['options'])) {
            $input['options'] = json_encode($input['options']);
        }
        if (isset($input['position'])) {
            $input['position'] = json_encode($input['position']);
        }

        if ($isTemplate) {
            unset($input['product_sku_id']);
        }

        $profile->fill($input);
        $profile->save();

        return $profile;
    }

    private function logProfile($logData) {
        // Log::info('SAVE-PROFILE-OUTPUT-' . env('APP_LOCALE'), $logData);
        $logData = [
            'target_id' => $logData['order_id'],
            'target_type' => 'ORDER_ITEM',
            'data' => json_encode($logData),
            'event_type' => 'SAVE-PROFILE-OUTPUT',
            'created_at' => new \DateTime()
        ];
        DB::table('log')->insert($logData);
    }

    private function _saveDesign($design, $skuId) {
        $skuId = DB::table('product_sku')->where('id', '=', $skuId)->select('product_id')->first();
        
        if (!empty($skuId->product_id)) {
            $productNDesign = DB::table('product_n_design')->where('product_id', '=', $skuId->product_id)->exists();
            foreach ($design as $key => $value) {
//                $designObj = Design::where('image_url', '=', $value)->first();
                if (!$productNDesign) {
                    $designObj = new Design;
                    $designObj->image_url = $value;
                    $designObj->save();

                    DB::table('product_n_design')->insert([
                        'design_id' => $designObj->id,
                        'product_id' => $skuId->product_id,
                        'is_primary' => 0
                    ]);
                }
            }
        }
        
    }

    public function loadZipcode(Request $request)
    {
        $retVal = [
            'status' => self::STATUS_FAIL
        ];

        $code = $request->input('code');
        $countryId = $request->input('country_id');

        if($code && $countryId) {

            $codeParts = explode(' ', $code);
            $code = preg_replace('/\s+/', '', $code);
            $zipcode = Zipcode::where('code', $code)
                ->where('country_id', $countryId)
                ->orderBy('verified', 'desc')
                ->first();

            if(!$zipcode && count($codeParts) > 1) {
                $zipcode = Zipcode::where('code', 'like', $codeParts[0] . '%')
                    ->where('country_id', $countryId)
                    ->orderBy('verified', 'desc')
                    ->first();
            }

            if($zipcode) {
                $retVal['status'] = self::STATUS_SUCCESSFUL;
                $retVal['result'] = $zipcode;
                
            }
        }

        return $retVal;
    }

    public function saveZipcode(Request $request)
    {
        $retVal = [
            'status' => self::STATUS_FAIL
        ];

        $data = $request->all();
        if(isset($data['code'])) {
            if(isset($data['country_id'])) {
                $zipcode = Zipcode::where('code', $data['code'])
                    ->where('country_id', $data['country_id']);

                if($data['region']) $zipcode->where('region', $data['region']);
                if($data['city']) $zipcode->where('city', $data['city']);
            }

            $zipcode = $zipcode->first();
            
            if(!$zipcode) {
                $zipcode = new Zipcode();

                // find country
                $country = Country::find($data['country_id']);
                if($country) {
                    $zipcode->country = $country->nicename;
                }

            }

            $zipcode->fill($data);
            $zipcode->verified = 1;
            $zipcode->updated_at = date('Y-m-d H:i:s');

            if($zipcode->save()) {
                $retVal['status'] = self::STATUS_SUCCESSFUL;
                $retVal['result'] = $zipcode;
            }

        }

        return $retVal;
    }

    public function updateProductId() {

        $perPage = 1000;
        $maxId = DB::table('printing_profile')->max('id');
        $pageCount = ceil($maxId / $perPage);

        $err = [];

        for ($i = 0; $i < $pageCount; $i ++) {
            $start = $i * $perPage;
            $stop = $start + $perPage;

            $printingProfile = DB::table('printing_profile')->select(['printing_profile.id', 'printing_profile.product_sku_id', 'product_sku.product_id'])
                ->join('product_sku', 'product_sku.id', '=', 'printing_profile.product_sku_id')
                ->whereNull('printing_profile.product_id')
                ->where('printing_profile.id', '>=', $start)
                ->where('printing_profile.id', '<', $stop)
                ->get();

            foreach ($printingProfile as $profile) {
                try {
                    DB::table('printing_profile')->where('id', $profile->id)
                        ->update([
                            'product_id' => $profile->product_id
                        ]);
                } catch (\Exception $e) {
                    $err[] = [
                        'profile' => $profile,
                        'message' => $e->getMessage()
                    ];
                }

            }
        }

        return $err;

    }
}
