<?php

namespace Mnv\Models\Shop;

use Mnv\Core\Model;

use Mnv\Core\Test\Logger;
use Mnv\Core\Uploads\ImageSizes;
use Mnv\Http\Request;
use Mnv\Models\Exceptions\NoContentException;
use Mnv\Models\Exceptions\NotInsertContentException;
use Mnv\Models\Exceptions\NotUpdateContentException;
use Mnv\Models\Exceptions\NotContentApproveException;

/**
 * Class Product
 * @package Mnv\Models\Shop
 */
class Product extends Model
{

    /** @var string */
    protected string $table = 'products';

    /** @var string  */
    protected string $table_image = 'product_images';

    /** @var string  */
    protected string $primaryKey = 'productId';

    protected string $orderBy = 'publishedOn DESC';

    /** @var string  */
    protected string $columns = 'productId, title, alias, sectionId, isFeatured, url, publishedOn, orderBy, status, price, quantity, enableGallery, stock_status_id';

    public $field;

    /** @var array $image */
    public $image = array();

    /** @var int $imageId */
    public int $imageId;

    public string $typeContent;

    public $contentIds = [];
    public $product_related = [];

    public $subtracts = array(
        '0' => 'Нет',
        '1' => 'Да',
    );

    public $specials = array(
        '0' => 'Выбрать особенность товара',
        'new' => 'Новинка',
        'sale' => 'Акция',
//        'hit' => 'Хит продаж',
//        'featured' => 'Рекомендуемый',
//        'sold' => 'Распроданный',
    );

    public $stock_status = array(
        'instock'   => 'В наличии',
        'outstock'  => 'Нет в наличии',
        'waiting'   => 'Ожидание 2-3 дня',
        'preorder'  => 'Предзаказ',
    );

    public function __construct(Request $request)
    {
        $this->id           = $request->get('id');
        $this->data         = $request->get('product', '');
        $this->field        = $request->get('field', '');
        $this->contentIds   = $request->get('productIds', '');
        $this->product_related   = $request->get('product_related', '');

        $this->sortBy       = $request->get('sortBy', '');
        $this->sortOrder    = $request->get('sortOrder', '');

    }

    /**
     * фильтрация / сортировка контента
     */
    protected function sorting(): void
    {
        global $SECTIONS;

        if (!empty($this->filter['query'])) {
            connect()->grouped(function($q) {
                $q->like('title', "%" . $this->filter['query'] . "%")->orLike('content', "%" . $this->filter['query'] . "%")->orLike('keywords',"%" . $this->filter['query'] . "%");
            });
        }

        if (!empty($this->filter['status'])) connect()->where('status', $this->filter['status']);

        if (!empty($this->filter['section']) && !empty($SECTIONS[$this->filter['section']])) {
            $sectionIds = array($this->filter['section']);
            if (!empty($SECTIONS[$this->filter['section']]['allChildren'])) {
                $sectionIds = array_merge($sectionIds, $SECTIONS[$this->filter['section']]['allChildren']);
            }
            connect()->in('sectionId', $sectionIds);
        }

        if (!empty($this->filter['dateStart']) && !empty($this->filter['dateEnd'])) {
            connect()->between('publishedOn', $this->filter['dateStart'], $this->filter['dateEnd']);
        }
    }

    /** Получение */
    public function all($limit, $page)
    {
        /** фильтрация / сортировка */
        $this->sorting();

        return parent::all($limit, $page);
    }

    public function total(): void
    {
        /** фильтрация / сортировка */
        $this->sorting();

        parent::total();
    }

    /**
     * Проверка на совпадение и получение fileName
     *
     * @param string|null $fileName
     * @return int|mixed|string|null
     */
    public function checkFileName(?string $fileName)
    {
        if (empty($fileName)) {
            $maxId = $this->getMaxValue($this->primaryKey);
            return $maxId ? $maxId + 1 : 1;
        }

        if (!empty($this->id)) connect()->where($this->primaryKey,'<>', $this->id);
        if ($fileName = connect($this->table)->select('fileName')->where('LOWER(fileName)', strtolower($fileName))->getValue()) {
            return $fileName;
        }

        return null;
    }

    /**
     * @throws NotUpdateContentException
     * @throws NotInsertContentException
     */
    public function prepare(array $data, int $managerId): bool
    {

        $data['modifiedBy']   = $managerId;
        $data['modifiedOn']   = gmdate('Y-m-d H:i:s');
        $data['publishedOn']  = adjustTime(date('Y-m-d H:i:s', strtotime($data['publishedOn'])),  true);

        if (empty($this->id)) {

            $data['addedBy'] = $managerId;
            $data['addedOn'] = gmdate('Y-m-d H:i:s');
            $data['orderBy'] = connect()->table($this->table)->max('orderBy')->getValue() + 1;
            if ($this->id = $this->insert($data)) {

                Logger::init()->info("Добавлен новый контент «" . $data['title'] . "»", $managerId)->save();
                return true;
            }
            Logger::init()->error("Ошибка при добавление контента «" . $data['name'] . "»", $managerId)->save();

            throw new NotInsertContentException();
        }
        else {

            if ($this->update($data)) {

                Logger::init()->info("В контент «" . $data['title'] . "» были внесены изменения", $managerId)->save();
                return true;
            }
            Logger::init()->error("Ошибка при редактирование контента «" . $data['title'] . "»", $managerId)->save();

            throw new NotUpdateContentException();
        }

    }

    /**  */
    public function setProductRelated()
    {

        connect('product_related')->where('productId', $this->id)->delete();

        if (!empty($this->product_related)) {
            foreach ($this->product_related as $product_related) {
                $isProductRelated = connect('product_related')->where('productId', $this->id)->where('relatedId', $product_related['product_id'])->get();
                if (empty($isProductRelated)) {
                    connect('product_related')->insert([
                        'productId' => $this->id,
                        'relatedId' => $product_related['product_id'],
                        'name'      => $product_related['name'],
                    ]);
                }
            }
        }
    }

    public function getProductRelated()
    {
        if (!empty($this->id)) {
           $product_related = connect('product_related')->select('relatedId')->where('productId', $this->id)->getAll('array');
           if (!empty($product_related)) {
               $product_related = array_column($product_related, 'relatedId', 'relatedId');
               $products = connect('products')->select('productId, title')->in('productId', $product_related)->getAll('array');
               return $this->getSelectProductImage($products);
           }
        }

        return [];
    }

    public function setOptions($product_options, $quantity)
    {

        connect('product_option')->where('product_id', $this->id)->delete();
        connect('product_option_value')->where('product_id', $this->id)->delete();

//        print_r($product_options);

        if (!empty($product_options)) {
            foreach ($product_options as $product_option) {

                if ($product_option['type'] == 'select' || $product_option['type'] == 'radio' || $product_option['type'] == 'checkbox' || $product_option['type'] == 'image') {
                    if (isset($product_option['product_option_value'])) {
                        $product_option_id =  connect('product_option')->insert([
                            'product_option_id' => (int)$product_option['product_option_id'],
                            'product_id'         => $this->id,
                            'option_id'          => (int)$product_option['option_id'],
                            'required'          => (int)$product_option['required'],
                        ]);

                        foreach ($product_option['product_option_value'] as $product_option_value) {
                            connect('product_option_value')->insert([
                                'product_option_value_id'   => (int)$product_option_value['product_option_value_id'],
                                'product_option_id'         => (int)$product_option_id,
                                'product_id'                => $this->id,
                                'option_id'                 => (int)$product_option['option_id'],
                                'option_value_id'           => (int)$product_option_value['option_value_id'],
                                'quantity'                  => ($quantity == 0) ? 0 : (int)$product_option_value['quantity'],
                                'subtract'                  => ($quantity == 0) ? 0 : (int)$product_option_value['subtract'],
                                'price'                     => (float)$product_option_value['price'],
                                'price_prefix'              => $product_option_value['price_prefix'],
                            ]);

                        }
                    }
                } else {
                    connect('product_option')->insert([
                        'product_option_id' => (int)$product_option['product_option_id'],
                        'product_id'         => $this->id,
                        'option_id'          => (int)$product_option['option_id'],
                        'value'             => (int)$product_option['value'],
                        'required'          => (int)$product_option['required'],
                    ]);

                }
            }
        }
    }

    public function getProductOptions($product_id)
    {

        $product_options = connect('product_option')->leftJoin('options', 'optionId', '=', 'option_id')->select('option_id, product_id, product_option_id, name, type, required')->where('product_id', $product_id)->orderBy('orderBy ASC')->getAll('array');
        return collect($product_options)->map(function ($product_option) {
            $product_option['product_option_value'] = connect('product_option_value')->leftJoin('options', 'optionId', '=', 'option_value_id')
                ->select('product_option_value_id, option_value_id, quantity, subtract, price, price_prefix')
                ->where('product_option_id', (int)$product_option['product_option_id'])
                ->orderBy('orderBy ASC')->getAll('array');

            return $product_option;
        })->all();
    }

    public function getOptionValues($option_id)
    {
        $option_value_data = array();

        $option_value_query = connect('options')->where('parentId', $option_id)->getAll('array');
//        $option_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value ov
//        LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id)
//         WHERE ov.option_id = '" . (int)$option_id . "' ORDER BY ov.sort_order, ovd.name");

        foreach ($option_value_query as $option_value) {
            $option_value_data[] = array(
                'option_value_id' => $option_value['optionId'],
                'name'            => $option_value['name'],
//                'image'           => $option_value['image'],
//                'sort_order'      => $option_value['sort_order']
            );
        }

        return $option_value_data;
    }

    /**
     * Удаление контента
     *
     * @return bool
     */
    public function remove(): bool
    {
        if (parent::remove()) {
            connect('comments')->where($this->primaryKey, $this->id)->delete();
            connect($this->table_image)->where($this->primaryKey, $this->id)->delete();

            connect('product_variants')->where('productId', $this->id)->delete();
            connect('product_feature_value')->where('productId', $this->id)->delete();

            connect('product_option')->where('product_id', $this->id)->delete();
            connect('product_option_value')->where('product_id', $this->id)->delete();

            return true;
        }

        return false;
    }

    /**
     * Обновление статуса
     *
     * @return bool
     * @throws NoContentException
     * @throws NotContentApproveException
     */
    public function approve(): bool
    {
        if ( !empty($this->id) && $this->data = $this->get()) {
            if (connect($this->table)->where($this->primaryKey, $this->id)->update(['status' => 'V'])) {
                return true;
            }
            throw new NotContentApproveException();
        }

        throw new NoContentException();
    }

    /**
     * Групповые действия
     * @param $group_action
     */
    public function group($group_action): void
    {
        if ($group_action === 'status') {
            if ( $this->data = $this->get()) {
                $contentUpdate['status'] = ($this->data['status'] === 'V') ? 'H' : 'V';
                $this->statusContent($contentUpdate);
            }

        } else if ($group_action === 'remove') {
            if ($this->data = $this->get()) {
                $this->removeContent();
            }
        }
    }

    /**
     * Обновление статуса товара
     *
     * @param $contentUpdate
     * @return bool
     */
    public function statusContent($contentUpdate): bool
    {
        if (connect($this->table)->where($this->primaryKey, $this->id)->update($contentUpdate)) {
            return true;
        }

        return false;
    }

    /** Удаление товара */
    public function removeContent(): bool
    {
        /** удаление контента и удаление записей из базы данных прикрепленных картинок и комментариев к этому контенту */
        if (connect()->table($this->table)->where($this->primaryKey, $this->id)->delete()) {
            connect()->table('comments')->where($this->primaryKey, $this->id)->delete();
            connect()->table($this->table_image)->where($this->primaryKey, $this->id)->delete();

            if (connect()->table('product_variants')->where('productId', $this->id)->get()) {
                connect()->table('product_variants')->where('productId', $this->id)->delete();
            }

            if (connect()->table('product_feature_value')->where('productId', $this->id)->get()) {
                connect()->table('product_feature_value')->where('productId', $this->id)->delete();
            }

            return true;
        }

        return false;
    }


    /** FEATURES */

    public function getAllProductFeatures($productId, ?int $sectionId)
    {
        if (!empty($sectionId)) connect()->like('sectionIds', "%$sectionId%");
        $features = connect('product_features')->select('id, parentId, name')->orderBy('orderBy ASC')->where('status', 'V')->getAll('array');
        $features = $this->buildTreeFromArray($features, 'id', 'property');
        return collect($features)->map(function ($feature) use ($productId) {
            if (isset($feature['property'])) {
                $feature['property'] = collect($feature['property'])->map(function ($item) use ($productId) {
                    $property = connect()->table('product_feature_value')->select('value')->where('featureId', $item['id'])->where('productId', $productId)->get('array');
                    if (!empty($property['value'])) {
                        $item['value'] = $property['value'];
                        return $item;
                    }
                    return $item;
                })->all();
            } else {
                $property = connect()->table('product_feature_value')->select('value')->where('featureId', $feature['id'])->where('productId', $productId)->get('array');
                if (!empty($property['value'])) {
                    $feature['value'] = $property['value'];
                    return $feature;
                }
                return $feature;
            }

            return $feature;

        })->all();

    }
    public function getAllProductFeatureIds(?int $sectionId)
    {
//        if (!empty($sectionId)) connect()->like('sectionIds', "%$sectionId%");
        return connect('product_features')->select('id')->where('status', 'V')->orderBy('orderBy ')->indexKey('id')->valueKey('id')->getAllIndexes('array');
    }
    /**
     * @param string $name
     * @return mixed|string|null
     */
    public function existsFeature(string $name)
    {
        return connect('product_features')->select('id')->where('name', $name)->getValue();

    }
    /**
     * @param array $feature
     * @return int|null
     */
    public function addFeature(array $feature): ?int
    {
        $feature['orderBy'] = connect()->table('product_features')->max('orderBy')->getValue() + 1;
        if ($featureId = connect()->table('product_features')->insert($feature)) {
            return $featureId;
        }

        return null;
    }

    /** OPTIONS */

//    public function getOptions($productId)
//    {
//        return connect()->table('product_feature_value')->select('productId, featureId, value')->where('productId', $productId)->indexKey('featureId')->getAllIndexes();
//
//    }

    /**
     * Получить все свойства
     *
     * @param $productId
     * @return array|mixed|null
     */
    public function getProductFeatureValues($productId)
    {
        return connect()->table('product_feature_value')
            ->join('product_features', 'id', '=', 'featureId')
            ->select('productId, featureId, name, value, orderBy')
            ->where('productId', $productId)
            ->where('status', 'V')
            ->orderBy('orderBy')
            ->indexKey('featureId')->getAllIndexes('array');
    }

    /**
     * Получить `value` свойства
     *
     * @param $featureId
     * @return mixed|string|null
     */
    public function getProductFeatureValue($featureId)
    {
        return connect()->table('product_feature_value')->select('value')->where('featureId', $featureId)->getValue();
    }


    /**
     * Обновление/вставка свойства
     *
     * @param int $productId
     * @param int $featureId
     * @param string|null $value
     */
    public function updateFeatureValue(int $productId, int $featureId, string $value = null)
    {
        if (!empty($value)) {
            connect()->table('product_feature_value')->select('*')->replace([
                'productId' => $productId,
                'featureId' => $featureId,
                'value' => $value
            ]);
        } else {
            $this->deleteFeatureValue($productId, $featureId);
        }
    }

    public function updateFeatureValueNew($feature)
    {
        if (!empty($feature['value'])) {
            connect('product_feature_value')->select('*')->replace([
                'productId' => $feature['productId'],
                'featureId' => $feature['featureId'],
                'value' => $feature['value']
            ]);
        } else {
            $this->deleteFeatureValue($feature['productId'], $feature['featureId']);
        }
    }

    /**
     * Удаление свойства
     *
     * @param int $productId
     * @param int $featureId
     */
    public function deleteFeatureValue(int $productId, int $featureId)
    {
        connect()->table('product_feature_value')->where('productId', $productId)->where('featureId', $featureId)->delete();
    }





    /** ЦВЕТА */
    public function addColor($color): void
    {
        if (empty($color['colorId'])) $color['colorId'] = 0;

        $color['orderBy'] = connect('product_colors')->max('orderBy')->where('productId', $this->id)->getValue() + 1;
        connect('product_colors')->insert($color);
    }

    public function updateColor($imageId, $color): void
    {
        connect('product_colors')->select('*')->where('imageId', $imageId)->update($color);
    }

    public function deleteColor($imageId): bool
    {
        if (connect('product_colors')->where('imageId', $imageId)->delete()) {
            return true;
        }

        return false;
    }

    /**
     * Сортировать
     *
     * @param $imageId
     * @param $index
     * @return bool
     */
    public function sortByColors($imageId, $index): bool
    {
        if (!empty($imageId) && !empty($index)) {
            connect('product_colors')->where('imageId', $imageId)->update(['orderBy' => $index]);

            return true;
        }

        return false;
    }

    public function getProductColors()
    {
        if (isset($this->id) && $this->id > 0) {
            $images = connect('product_colors')->select('*')->where('productId', $this->id)->orderBy('orderBy ASC')->getAll('array');
            collect($images)->map(function ($item)  {
                if ($file = $this->getFileInfo($item['fileId'])) {
                    $this->data['colors'][] = ImageSizes::init()->get($item, $file);
                } else {
                    $this->data['colors'][] = $item;
                }
            })->all();

        }

        return $this;
    }


    public function getStockStatuses()
    {
      return  connect('product_stock_status')->select('*')->orderBy('stock_status_id ASC')->indexKey('stock_status_id')->valueKey('name')->getAllIndexes('array');
    }

    public static function getProducts()
    {
        return  connect('products')->select('productId, title')->orderBy('productId ASC')->indexKey('productId')->valueKey('title')->getAllIndexes('array');
    }
    public static function getProduct($productId)
    {
        return  connect('products')->select('title')->where('productId', $productId)->getValue();
    }

    /** работа с массивом  */
    private function buildTreeFromArray($items, $id, $subArray)
    {
        $childs = [];

        foreach ($items as &$item) {
            $childs[$item['parentId'] ?? 0][] = &$item;
        }

        unset($item);

        foreach ($items as &$item) {
            if (isset($childs[$item[$id]])) {
                $item[$subArray] = $childs[$item[$id]];
            }
        }

        return $childs[0] ?? [];
    }



    /** ПОЛУЧИТЬ опции */
    public function getSelectProducts(): ?array
    {
        if (!empty($this->filter['query'])) {
            connect()->grouped(function($q) {
                $q->like('title', "%" . $this->filter['query'] . "%");
            });
        }
        if (!empty($this->id)) connect()->notWhere('productId', $this->id);

        $products = connect($this->table)->select('productId, title')->orderBy('orderBy ASC')->pagination($this->filter['limit'], $this->filter['start'])->getAll('array');
        return $this->getSelectProductImage($products);
    }

    private function getSelectProductImage($products)
    {
        if (!empty($products)) {
            return collect($products)->map(function ($product) {
                if ($image = connect($this->table_image)->where($this->primaryKey, $product['productId'])->where('type', 'general')->orderBy('orderBy ASC')->get('array')) {
                    if ($file = $this->getFileInfo($image['fileId'])) {
                        $product['image'] = ImageSizes::init()->get($image, $file)['small'];
                    }
                }
                return $product;
            })->all();
        }

        return $products;
    }
}