<?php

namespace App\Services;

use App\Enums\BasicUnitEnum;
use App\Enums\DbStatusFieldEnum;
use App\Enums\ProductUnitEnum;
use App\Models\Product;
use App\Repositories\PriceListRepository;
use App\Repositories\PriceListRepositoryInterface;
use App\Repositories\ProducerRepository;
use App\Repositories\ProductRepository;
use App\Repositories\SupplierRepository;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;

class PriceListService
{
    protected PriceListRepository $PriceListRepository;

    protected SupplierRepository $supplierRepository;

    protected ProducerRepository $producerRepository;

    protected ProductRepository $productRepository;

    protected MessageBag $error;

    public function __construct(PriceListRepositoryInterface $PriceListRepository)
    {
        $this->PriceListRepository = $PriceListRepository;
        $this->error = new MessageBag;
    }

    /**
     * Get's a record by it's ID
     *
     * @param int
     */
    public function get($id): collection
    {
        return $this->PriceListRepository->get($id);
    }

    /**
     * Get's all records.
     *
     * @return mixed
     */
    public function all()
    {
        return $this->PriceListRepository->all();
    }

    /**
     * Deletes a record.
     *
     * @param int
     */
    public function delete($id)
    {
        return $this->PriceListRepository->delete($id);
    }

    /**
     * Updates a post.
     *
     * @param int
     * @param array
     */
    public function update($id, array $data)
    {
        $this->PriceListRepository->update($id, $data);
    }

    public function getError()
    {
        return $this->error->toArray();
    }

    private array $excelFieldPointer = [
        'supplierProductNumber' => 0,
        'productGroupMain' => 1,
        'productGroupSub1' => 2,
        'productGroupSub2' => 3,
        'name' => 4,
        'packing' => 5,
        'unitValue' => 6,
        'productUnit' => 7,
        'producerName' => 8,
        'sellerUnit' => 9,
        'unitMultiplier' => 10,
        'amountUnit' => 11,
        'vat' => 12,
        'price' => 13,
        'priceNew' => 14,
        'priceChange' => 15,
        'hooreycaId' => 16,
        'HooreycaUnit' => 17,
        'HooreycaMultiplier' => 18,
        'buyerProductName' => 19,
        'note' => 20,
        'krel' => 21,

    ];

    private array $excelFieldAcceptedType = [
        'supplierProductNumber' => ['integer', 'string'],  // 0
        'productGroupMain' => ['string'],                 // 1
        'productGroupSub1' => ['string'],                 // 2
        'productGroupSub2' => ['string'],                 // 3
        'name' => ['string'],                             // 4
        'packing' => ['integer', 'double'],                // 5
        'unitValue' => ['integer', 'double'],              // 6
        'productUnit' => ['string', 'enum' => BasicUnitEnum::class],                      // 7
        'producerName' => ['string'],                     // 8
        'sellerUnit' => ['string', 'enum' => ProductUnitEnum::class],                       // 9
        'unitMultiplier' => ['integer', 'double'],         // 10
        'amountUnit' => ['integer', 'string', 'enum' => ProductUnitEnum::class],             // 11
        'vat' => ['integer', 'string', 'double'],           // 12
        'price' => ['integer', 'string', 'double'],         // 13
        'priceNew' => ['integer', 'string', 'double'],      // 14
        'priceChange' => ['integer', 'string', 'double'],   // 15
        'hooreycaId' => ['integer', 'string'],             // 16
        'HooreycaUnit' => ['string'],                     // 17
        'HooreycaMultiplier' => ['integer', 'double', 'string'],     // 18
        'buyerProductName' => ['string'],                 // 19
        'note' => ['string'],                             // 20
        'krel' => ['string'],                             // 21
    ];

    private array $excelFieldConverter = [
        'priceNew' => ['numberFormat', ['decimals' => 2]],
        'priceChange' => ['percentage'],
        'vat' => ['percentage'],
        'note' => ['null'],
        'krel' => ['booleanCustom'],
        'hooreycaId' => ['null'],
        'HooreycaUnit' => ['null'],
        'HooreycaMultiplier' => ['null'],
    ];

    private function convertBooleanCustom($value, $options)
    {
        if (strlen(trim($value)) > 0) {
            return 1;
        } else {
            return 0;
        }
    }

    private function convertNull($value, $options)
    {
        if (! is_null($value)) {
            if ($value == 'null') {
                $value = null;
            } elseif (strlen($value.'') === 0) {
                $value = null;
            }
        }

        return $value;
    }

    private function convertPercentage($value, $options)
    {
        if (gettype($value) == 'string') {
            $value = (float) $value;
        }
        if (! isset($options['multiplier']) || $options['multiplier'] == true) {
            $value = $value * 100;
        }
        if (! isset($options['precision'])) {
            $precision = 0;
        }

        return $this->convertNumberFormat($value, [
            'decimals' => $precision,
        ]
        );
    }

    private function convertNumberFormat($value, $options)
    {
        if (gettype($value) == 'string') {
            $value = str_replace(',', '.', $value);
            $value = str_replace(' ', '', $value);
        }
        $value = (float) $value;
        $decimals = 0;
        if (isset($options['decimals'])) {
            $decimals = $options['decimals'];
        }
        $decimal_separator = '.';
        if (isset($options['decimal_separator'])) {
            $decimal_separator = $options['decimal_separator'];
        }
        $thousands_separator = '';
        if (isset($options['thousands_separator'])) {
            $thousands_separator = $options['thousands_separator'];
        }

        return number_format($value, $decimals, $decimal_separator, $thousands_separator);
    }

    private function convertRowData($data)
    {
        foreach ($data as $fieldName => $value) {
            if (isset($this->excelFieldConverter[$fieldName])) {
                $converterParameter = $this->excelFieldConverter[$fieldName];
                $functionName = 'convert'.ucfirst($converterParameter[0]);
                $arg = [];
                if (isset($converterParameter[1])) {
                    $arg = $converterParameter[1];
                }
                $data[$fieldName] = call_user_func([$this, $functionName], $value, $arg);
                // $this->['convert'.ucfirst($this->excelFieldConverter[$fieldName])]()
                /*                dd([
                                    $fieldName,
                                    $functionName,
                                    $arg,
                                    method_exists($this,$functionName),
                                    $value,
                                    $this->convertNumberFormat($value,$arg),
                                    call_user_func(array($this, $functionName), $value,$arg)
                                ]);*/
            }
        }

        return $data;
    }

    private function checkExcelRowFieldType($data)
    {
        // dd($this->excelFieldAcceptedType[array_search('12',$this->excelFieldPointer)]);
        $retArray = [];
        foreach ($data as $key => $value) {
            if ($key > max($this->excelFieldPointer)) {
                break;
            }
            $fieldName = array_search($key, $this->excelFieldPointer);

            try {
                $acceptedType = $this->excelFieldAcceptedType[$fieldName];
            } catch (\Exception $e) {
                dd([
                    $data,
                    $key,
                    $value,
                    $this->excelFieldAcceptedType,
                    $this->excelFieldPointer,
                    $fieldName,
                ]);
            }

            if (! in_array(gettype($value), $acceptedType)) {
                $retArray[$fieldName] = 'invalid Type '.var_export($acceptedType, true);
                if (gettype($value) == 'object') {
                    $retArray[$fieldName] .= '['.gettype($value).'/'.get_class($value).']';
                } else {
                    $retArray[$fieldName] .= '['.gettype($value).']';
                }
                $this->importableDataFile = false;
            } else {
                if (isset($acceptedType['enum'])) {
                    // dd($acceptedType['enum']);
                    if (! in_array($value, $acceptedType['enum']::getValues())) {
                        // dd([$value,$acceptedType['enum']::getValues()]);
                        $retArray[$fieldName] = 'Nem megfelelő érték.';
                        // $retArray[$key].='['.gettype($value).'/'.get_class($value).']';
                    }
                }
            }
        }
        if (count($retArray) > 0) {
            return $retArray;
        }

        return false;
    }

    private function convertExcelRowToNamedArray($data)
    {
        $returnArray = [];
        foreach ($data as $key => $value) {
            if ($fieldName = array_search($key, $this->excelFieldPointer)) {
                $returnArray[$fieldName] = $value;
            } else {
                $returnArray[$key] = $value;
            }

        }

        return $returnArray;
    }

    private array $productGroupArray = [];

    private function initGroupsArray()
    {
        // ellenorizni kell a csoportokat
        $ProductGroup = new \App\Models\ProductGroup;
        // $productGroupArray=$ProductGroup->allWithParents(2);
        $this->productGroupArray = $ProductGroup->allWithPath(3);
        // path szerint idexelni.

    }

    private function getProductGroupByName($mainGroupName, $subGroupName = null, $subGroup2Name = null)
    {
        // var_export($this->productGroupArray[4]);
        // var_export(func_get_args());
        $searchName = '';
        if ($subGroup2Name) {
            $searchName = $subGroup2Name;
        } elseif ($subGroupName) {
            $searchName = $subGroupName;
        } else {
            $searchName = $mainGroupName;
        }
        foreach ($this->productGroupArray as $key => $groupData) {
            if ($groupData['name'] == $searchName) {
                return $groupData;
            }
        }

        return false;
    }

    public function checkProductUnit($name)
    {
        return \App\Enums\ProductUnitEnum::hasValue($name);
    }

    private array $rowData = [];

    private function addRowFieldError($namedArrayErrors)
    {
        if (! isset($this->rowData['error']['fields'])) {
            $this->rowData['error']['fields'] = $namedArrayErrors;
        } else {
            foreach ($namedArrayErrors as $key => $value) {
                $this->rowData['error']['fields'][$key] = $value;
            }
            // $this->rowData['error']['fields']=array_merge($this->rowData['error']['fields'],$namedArrayErrors);
        }
        $this->rowData['status'] = 'error';

        return $this->rowData;
    }

    private bool $importableDataFile = true;

    public function importPriceListCheck($supplierId = null, $data = null)
    {

        if (! intval($supplierId) ||
            $supplierId < 1
        ) {
            $this->error->add('supplierId', 'Nem megfelelő beszállító');

            return false;
        }
        $SupplierRepository = new SupplierRepository;

        if (! $SupplierData = $SupplierRepository->get($supplierId)) {
            $this->error->add('supplierId', 'Ismeretlen beszállító');

            return false;
        }

        if (! $data || count($data) < 1) {
            $this->error->add('priceListData', 'Nincsenek sorok');

            return false;
        }

        if (count($data[array_key_first($data)]) > count($this->excelFieldPointer)) {
            $this->error->add('priceListData', 'Túl sok oszlop. [max:'.count($this->excelFieldPointer).']');

            return false;
        }

        $this->loadProducers('name');
        $this->initGroupsArray();
        $this->initProductRepository();

        $fromLine = 370; // nincs 2-es alcsoportja 370
        $fromLine = 6; // tizedes az arban
        $fromLine = 0; // OK sor
        // $fromLine=360; //nincs 2-es alcsoportja 370
        $linesNum = 50;
        $linesNum = 400;
        $linesNum = 9999;

        $data = array_splice($data, $fromLine, $linesNum);

        $excelPointer = $this->excelFieldPointer;
        $checkedData = [];

        foreach ($data as $key => $itemData) {
            $this->rowData = [
                'rowNum' => $key + 1,
                'status' => 'OK',
                'originalData' => null,
                'oldStoredData' => null,
                'additionalData' => [],
                'error' => [],
            ];
            $this->rowData['originalData'] = $this->convertExcelRowToNamedArray($itemData);
            $this->rowData['originalData']['supplier_id'] = $SupplierData['id'];

            // Excel importnal lehetnek rossz a mezotipusok ezert ellenorizni kell
            if ($fieldHasTypeError = $this->checkExcelRowFieldType($itemData)) {
                $this->addRowFieldError($fieldHasTypeError);
                $checkedData[] = $this->rowData;

                continue;
            } else {
                // ha  nem hibas a mezotipus akkoro koverteljuk
                $this->rowData['originalData'] = $this->convertRowData($this->rowData['originalData']);
            }

            // Check product
            /**
             * PM108 supplierProductNumber required
             */
            if (! $itemData[$this->excelFieldPointer['supplierProductNumber']]
                || strlen(preg_replace('/\s+/', '', $itemData[$this->excelFieldPointer['supplierProductNumber']])) < 1
            ) {
                $this->addRowFieldError([$this->excelFieldPointer['supplierProductNumber'] => 'unknown value']);
            }

            /*$storedData=Product::where('supplier_id',$supplierId)
                ->where('supplierProductNumber',$itemData[$excelPointer['supplierProductNumber']])->get()->first();
            */
            $storedData = $this->productRepository->getBySupplierAndSupplierProductNumber($supplierId, $itemData[$excelPointer['supplierProductNumber']], true);

            if (! $storedData) {
                // $this->rowData['error']=array_merge($this->rowData['error'],['all'=>'new Item']);
                $this->rowData['status'] = 'new';
            } else {
                $this->rowData['oldStoredData'] = $storedData;
            }

            // productGroupData
            if ($groupData = $this->getProductGroupByName($itemData[$this->excelFieldPointer['productGroupMain']],
                $itemData[$this->excelFieldPointer['productGroupSub1']],
                $itemData[$this->excelFieldPointer['productGroupSub2']]
            )) {
                $this->rowData['additionalData']['groupData'] = $groupData;
                $this->rowData['originalData']['product_group_id'] = $groupData['id'];
            } else {
                // nem talaltuk meg a csoporot
                $this->addRowFieldError([$this->excelFieldPointer['productGroupSub2'] => 'invalid group']);
                // $this->rowData['error']['fields'][$this->excelFieldPointer['productGroupSub2']]='invalid group';
                $this->importableDataFile = false;
            }

            // producerData
            if ($producerData = $this->getProducerByName($itemData[$this->excelFieldPointer['producerName']])) {
                $this->rowData['additionalData']['producerData'] = $producerData;
                $this->rowData['originalData']['producer_id'] = $producerData['id'];
            }

            // productUnit

            if (! $this->checkProductUnit($itemData[$this->excelFieldPointer['productUnit']])) {
                $this->addRowFieldError([$this->excelFieldPointer['productUnit'] => 'unknown productUnit']);
                // dd($this->rowData);
            }

            // PM159 check horreycadata
            if ($this->rowData['originalData']['hooreycaId'] != null) {
                if ($this->rowData['originalData']['HooreycaUnit'] == null) {
                    $this->addRowFieldError([$this->excelFieldPointer['HooreycaUnit'] => 'unknown value']);

                }
                if ($this->rowData['originalData']['HooreycaMultiplier'] == null) {
                    $this->addRowFieldError([$this->excelFieldPointer['HooreycaMultiplier'] => 'unknown value']);
                }
            }

            if (count($this->rowData['error']) < 1) {
                /*todo:ha van akkor ellenorizni kella fobb adatinak valtozasa, meg el kell menteni a kulombsegeket a historyba
      */
                if ($this->rowData['status'] != 'new') {
                    $changed = $this->productRepository->checkIsChanged($this->rowData['oldStoredData']['id'], $this->rowData['originalData']);
                    if (count($changed) > 0) {
                        // var_dump($changed);

                        if (isset($changed['error'])) {
                            $this->rowData['error'] = $changed['error'];
                            $this->rowData['status'] = 'error';
                        } else {
                            $this->rowData['changed'] = $changed;
                            $this->rowData['status'] = 'changed';
                        }
                    }
                }
            } else {
                // indexeljuk a nevekkel a hibalistat
                if (isset($this->rowData['error']['fields']) && count($this->rowData['error']['fields']) > 0) {
                    $this->rowData['error']['fields'] = $this->convertExcelRowToNamedArray($this->rowData['error']['fields']);
                }
            }
            //            dd($this->rowData);

            // dd($this->rowData['oldStoredData']);
            $checkedData[] = $this->rowData;
            // return [$retData];
            // return [$key,$retData];
        }

        return [
            'importableFile' => $this->importableDataFile,
            'rows' => $checkedData,
        ];

        return $data;
    }

    private ?array $producers = null;

    private ?string $producerIndexedBy = null;

    private function loadProducers(?string $index = null)
    {
        $this->initProducerRepository();
        if (! $index) {
            $this->producerIndexedBy = 'id';
        } else {
            $this->producerIndexedBy = $index;
        }
        $this->producers = $this->producerRepository->allByIndex($index);

    }

    private function getProducerByName($name)
    {
        $this->initProducerRepository();
        if (isset($this->producers[$name])) {
            return $this->producers[$name];
        }

        return false;
    }

    private function initProducerRepository()
    {
        if (! isset($this->producerRepository)) {
            $this->producerRepository = new ProducerRepository;
        }
    }

    private function addNewProducer($data)
    {
        $this->initProducerRepository();
        $data['canSee'] = 1;
        $data['status'] = \App\Enums\DbStatusFieldEnum::active;
        if ($id = $this->producerRepository->add($data)) {
            return $id;
        }

        return false;
    }

    public function updateProductData($id, array $changedData)
    {
        return $this->productRepository->update($id, $changedData);
    }

    public function importPriceList($supplierId = null, $availableDate = null, $data = null, $otherField = [])
    {
        if (false === $priceListData = $this->importPriceListCheck($supplierId, $data)) {
            return false;
        }
        if ($priceListData['importableFile'] == false) {
            return false;
        }

        $newPriceListData = [];
        $newPriceListData['supplier_id'] = $supplierId;
        $newPriceListData['canSee'] = 1;
        $newPriceListData['available'] = $availableDate;
        $newPriceListData['status'] = DbStatusFieldEnum::draft;
        if (isset($otherField['note'])) {
            $newPriceListData['note'] = $otherField['note'];
        }
        $priceListId = $this->PriceListRepository->add($newPriceListData);
        $priceListData['importSuccess'] = false;
        $priceListData['id'] = $priceListId;
        $affectedProductIds = [];
        foreach ($priceListData['rows'] as $key => $rowData) {
            if (! isset($rowData['additionalData']['producerData'])) {
                //                dd($rowData);

                if (! $producerData = $this->getProducerByName($rowData['originalData']['producerName'])) {
                    if ($producerId = $this->addNewProducer(['name' => $rowData['originalData']['producerName']])) {
                        $this->loadProducers('name');
                        $producerData = $this->producerRepository->get($producerId);
                    } else {
                        // nem lett felveve a producer
                    }
                }
                if ($rowData['status'] !== 'new') {
                    $rowData['status'] = 'changed';
                    if (! isset($rowData['changed'])) {
                        $rowData['changed'] = [];
                    }
                    $old_producer_id = null;
                    if (isset($rowData['oldStoredData']['producer_id'])) {
                        $old_producer_id = $rowData['oldStoredData']['producer_id'];
                    }
                    $rowData['changed']['producer_id'] = [
                        'old' => $old_producer_id,
                        'new' => $producerData['id'],
                    ];
                }
                $rowData['additionalData']['producerData'] = $producerData;
            }
            $fromOriginalData = [
                'name',
                'packing',
                'unitValue',
                'productUnit',
                'sellerUnit',
                'unitMultiplier',
                'amountUnit',
                'vat',
                'supplierProductNumber',
                'hooreycaId',
                'HooreycaUnit',
                'HooreycaMultiplier',
                'buyerProductName',
                'note',
                'krel',
            ];
            $productData = [];
            foreach ($fromOriginalData as $pointer) {
                if (! array_key_exists($pointer, $rowData['originalData'])) {
                    // debug(__CLASS__.__LINE__,$pointer,$rowData['originalData'],$rowData);
                    // dd(__CLASS__.__LINE__,$pointer,$rowData['originalData'],$rowData);
                } else {
                    $productData[$pointer] = $rowData['originalData'][$pointer];
                }
            }

            $productData['supplier_id'] = $supplierId;
            $productData['producer_id'] = $rowData['additionalData']['producerData']['id'];
            /*            if(!array_key_exists('groupData',$rowData['additionalData'])) {
                            dd(__CLASS__.__LINE__,$rowData);
                        }*/
            $productData['product_group_id'] = $rowData['additionalData']['groupData']['id'];
            $productData['type'] = $rowData['additionalData']['groupData']['type'];

            // dd($productData,$rowData);
            if ($rowData['status'] == 'changed') {
                $this->updateProductData($rowData['oldStoredData']['id'], $productData);

            }
            if ($rowData['status'] == 'new') {
                // fel kell venni az uj tetelt
                $productData['id'] = $this->addNewProduct($productData);
                if ($productData['id'] !== null) {
                    // ár elmenteése a termékhez
                    $this->PriceListRepository->attachProduct($productData['id'], $rowData['originalData']['priceNew'], $priceListId);
                } else {
                    // hibat hozzaadni!
                }
            }// elseif($rowData['status']=='OK'){
            else {
                $this->PriceListRepository->attachProduct($rowData['oldStoredData']['id'], $rowData['originalData']['priceNew'], $priceListId);
            }
            if (isset($productData['id'])) {
                $affectedProductIds[] = $productData['id'];
            } else {
                $affectedProductIds[] = $rowData['oldStoredData']['id'];
            }

        }
        $this->PriceListRepository->update($priceListId, ['status' => DbStatusFieldEnum::active]);
        $this->updateProductVisibilities($affectedProductIds);
        $priceListData['importSuccess'] = true;

        return $priceListData;
    }

    private function updateProductVisibilities($productIds)
    {
        $this->initProductRepository();

        return $this->productRepository->updateVisibilitiesByIds($productIds);
    }

    private function initProductRepository()
    {
        if (! isset($this->productRepository)) {
            $this->productRepository = new ProductRepository;
        }
    }

    private function addNewProduct($productData): ?int
    {
        $this->initProductRepository();
        $productData['canSee'] = 1;
        $productData['status'] = \App\Enums\DbStatusFieldEnum::active;

        return $this->productRepository->add($productData);
    }
}
