<?php
namespace Teknisa\PebLibs\Services;

use \Estoque\Entities\Posestdia;
use \Geral\Entities\Paramfilial;
use \Geral\Entities\Prodfili;
use Teknisa\Libs\Util\DateUtil;
use Zeedhi\Framework\Util\Functions;
use Teknisa\Libs\Util\XLS\FileLoader;
use Teknisa\Libs\Util\XLS\FileReader;
use Teknisa\Libs\Util\XLS\Table;
use Teknisa\Libs\Util\XLS\Exception;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

class Est58000Retirada {

    protected $newCode;
    protected $Est58000Factory;
    protected $ValidacoesService;
    protected $environment;

    protected $nrlancestq;
    protected $nrorg;
    protected $currentTime;
    protected $operador;
    protected $filialLogada;
    protected $idativo = 'S';
    protected $retirada = '3';

    public function __construct($newCode, $environment, $Est58000Factory, $ValidacoesService) {
        $this->newCode = $newCode;
        $this->Est58000Factory = $Est58000Factory;
        $this->ValidacoesService = $ValidacoesService;
        $this->operador = $environment->getCdOperador();
        $this->nrorg = $environment->getNrOrg();
        $this->filialLogada = $environment->getCdFilial();
        $this->currentTime = $environment->getCurrentTime()->format("d/m/Y H:i:s");
        $this->environment = $environment;
    }

    public function validateReqComp($lancamento, $itens) {
        if($this->shouldCreateReqComp($itens)) {
            $reqComp = $this->getRequisicaoComplementarAberta($lancamento);

            $this->insertItensForReqComp($reqComp, $lancamento, $itens);
        }
    }

    public function insertItensForReqComp($NRREQUESTO, $lancamento, $itens) {
        $params = [
            "NRREQUESTO" => $NRREQUESTO,
            "CDFILIAL" => $lancamento['CDFILIMOVI'],
            "QTDEVOREQU" => 0,
            "IDSTITREQU" => 'A',
            "CDFILILANC" => null,
            "NRLANCESTQ" => null,
            "NRSEQUITEM" => null,
            "CDFILLANCREQC" => $lancamento['CDFILIAL'],
            "NRLANCREQC" => $lancamento['NRLANCESTQ']
        ];

        foreach ($itens as $item) {
            if(Functions::arrayKeyExists('createReqComp', $item) && $item['createReqComp']) {
                $params['NRSEQITREQC'] = $item['NRSEQUITEM'];
                $params['CDPRODUTO'] = $item['CDPRODUTO'];
                $params['NRLOTEESTQ'] = $item['NRLOTEESTQ'];
                $params['NRSUBLOTE'] = $item['NRSUBLOTE'];
                $params['QTREQUESTO'] = $item['QTLANCREQCOMP'];



                $newCodeParams = implode('', [$lancamento['CDFILIAL'],
                                  $lancamento['NRREQUESTO'],
                                  $item['CDPRODUTO']]);

                $NRSEQITEM = $this->geraNewSequencial('SEQITREQESTO', $newCodeParams, 4);

                $params['NRSEQITEM'] = $NRSEQITEM;

                $this->Est58000Factory->insertItrequestoComplementar($params);
            }
        }
    }

    public function shouldCreateReqComp($itens) {
        foreach ($itens as $item) {
            if(Functions::arrayKeyExists('createReqComp', $item) && $item['createReqComp'])
                return true;
        }

        return false;
    }

    public function getRequisicaoComplementarAberta($lancamento) {
        $params = [
            "CDFILLANCREQC" => $lancamento['CDFILIAL'],
            "NRLANCREQC" => $lancamento['NRLANCESTQ']
        ];
        $reqComp = $this->Est58000Factory->getRequisicaoComplementarAberta($params);

        if(empty($reqComp)) {
            $NRREQUCOMPESTO = $this->geraNewSequencial('NRREQUESTO');

            $params = [
                "CDFILIMOVI" => $lancamento['CDFILIMOVI'],
                "NRREQUESTO" => $lancamento['NRREQUESTO']
            ];
            $reqCompParameters = $this->Est58000Factory->getRequisicaoComplementarParameters($params);

            $params = [
                "CDFILIAL" => $lancamento['CDFILIAL'],
                "NRREQUESTO" => $NRREQUCOMPESTO,
                "DTREQUESTO" => $lancamento['DTLANCESTQ'],
                "DTPREVREQU" => $lancamento['DTLANCESTQ'],
                "DSREQUESTO" => 'Requisição Complementar - Req. Orig.: ' . $lancamento['NRREQUESTO'] . ' Baixa Orig.:  ' . $lancamento['NRLANCESTQ'],
                "CDSETOR" => $reqCompParameters['CDSETOR'],
                "CDCENTCUST" => $reqCompParameters['CDCENTCUST'],
                "CDOPERREQU" => $this->operador,
                "CDALMOXSAID" => $reqCompParameters['CDALMOXSAID'],
                "CDLCESTSAID" => $reqCompParameters['CDLCESTSAID'],
                "CDALMOXENTR" => $reqCompParameters['CDALMOXENTR'],
                "CDLCESTENTR" => $reqCompParameters['CDLCESTENTR'],
                "CDFILLANCREQC" => $lancamento['CDFILIAL'],
                "NRLANCREQC" => $lancamento['NRLANCESTQ']
            ];
            $this->Est58000Factory->insertRetiradaestoComplementar($params);

            return $NRREQUCOMPESTO;
        }

        return $reqComp['NRREQUESTO'];
    }

    /**
     * Essa função é chamda quando está se inserindo uma retirada com uma requisição.
     * Ela tem a finalidade de desabilitar a requisição que foi passada, para que não
     * se possa mais inserir retiradas com aquela requisição que foi passada.
     *
     * @param $lancamento | row com os dados do lançamento
     * @param $itensLancesto | array com rows com os dados de cada item do lançamento
     */
    public function validateRequisicao($lancamento, $itensLancesto) {
        $this->updateItemStatus($lancamento, $itensLancesto);

        $newReqStatus = $this->getProperRequisicaoStatus($lancamento);

        $this->updateRequisicaoStatus($lancamento["CDFILIMOVI"], $lancamento["NRREQUESTO"], $newReqStatus);
    }

    //atualiza os status dos itens de uma requisição na base
    public function updateItemStatus($lancamento, $itensLancesto) {
        $itensRequesto = [];
        foreach($itensLancesto as $itemLancesto) {
            if(isset($itemLancesto['NRREQUESTO'])){    
                if($itemLancesto['NRREQUESTO'] && $itemLancesto['NRSEQITEM']) {
                    $itensRequesto[] = $itemLancesto;
                }
            }
        }

        if($itensRequesto) {
            $this->encerrarItensRequesto($lancamento, $itensRequesto);
        }
    }

    //lança os CDPRODUTOs pra factory pra atualizar os status dos itens para "E"
    public function encerrarItensRequesto($lancamento, $itensRequesto) {
        $params = [
            "IDSTITREQU" => "E",
            "NRLANCESTQ" => $lancamento["NRLANCESTQ"]
        ];

        foreach ($itensRequesto as $item) {
            $params["CDFILILANC"] = $lancamento["CDFILIALLAN"];
            $params["NRSEQITEM"]  = $item["NRSEQITEM"];
            $params["CDFILIAL"]   = $item["CDFILIAL"];
            $params["NRREQUESTO"] = $item["NRREQUESTO"];
            $params["OLD_NRLANCESTQ"] = null;
            $params["CDPRODUTO"]  = $item["CDPRODUTO"];
            $params["NRSEQUITEM"] = $item["NRSEQUITEM"];
            $params["NRORG"]      = $this->nrorg;

            $this->Est58000Factory->updateItrequestoStatus($params);
        }
    }

    public function getPosestDia($filter) {
        $params = ["CDPRODUTO" => $filter["CDPRODUTO"]];
        $CDPRODESTO = $this->Est58000Factory->getProdesto($params)["CDPRODESTO"];

        $params = [
            "CDFILIAL" => $filter["CDFILIAL"],
            "NRORG" => $this->nrorg
        ];
        $INTPCUSTO = $this->Est58000Factory->getCdtipocusto($params)["CDTIPCUSTEST"];
 
        $params = [
            "CDFILIAL" => $filter["CDFILIAL"],
            "CDALMOXARIFE" => $filter["CDALMOXARIFE"],
            "CDLOCALESTOQ" => $filter["CDLOCALESTOQ"],
            "NRLOTEESTQ" => $filter["NRLOTEESTQ"],
            "NRSUBLOTE" => $filter["NRSUBLOTE"],
            "CDPRODESTO" => $CDPRODESTO
        ];
        $DTPOSIESTQ = $this->Est58000Factory->getDtposiestq($params)["DTPOSIESTQ"];

        $params = [
            "CDFILIAL" => $filter["CDFILIAL"],
            "CDALMOXARIFE" => $filter["CDALMOXARIFE"],
            "CDLOCALESTOQ" => $filter["CDLOCALESTOQ"],
            "NRLOTEESTQ" => $filter["NRLOTEESTQ"],
            "NRSUBLOTE" => $filter["NRSUBLOTE"],
            "CDPRODESTO" => $CDPRODESTO,
            "DTPOSIESTQ" => $DTPOSIESTQ,
            "INTPCUSTO" => $INTPCUSTO
        ];
        return $this->Est58000Factory->getPosestdia($params);
    }
    
    public function limpaCampoConsumo($row){
        
        $params = array(
            "CDFILIMOVI" => $row["CDFILIMOVI"],
            "NRLANCESTQ" => $row["NRLANCESTQ"]
        );
        $this->Est58000Factory->limpaCampoConsumo($params);
    }

    //retorna o status da requisição a partir dos status dos itens da mesma
    public function getProperRequisicaoStatus($lancamento) {
        $params = [
            "CDFILIAL" => $lancamento["CDFILIMOVI"],
            "NRREQUESTO" => $lancamento['NRREQUESTO'],
            "NRORG" => $this->nrorg,
            "IDSTITREQU" => null
        ];
        
        $itensRequesto = $this->Est58000Factory->getItensByRequisicao($params);
        $statusRequesto = $itensRequesto[0]["IDSTITREQU"];
        foreach ($itensRequesto as $itemRequesto) {
            if($itemRequesto["IDSTITREQU"]!= $statusRequesto) {
                $statusRequesto = "P";
            }
        }

        return $statusRequesto;
    }

    //atualiza o status da requisição
    public function updateRequisicaoStatus($CDFILIAL, $NRREQUESTO, $IDSTATUSREQ) {
        $IDREQUESTO = ($IDSTATUSREQ == 'A')? 'P' : 'C';

        $params = [
            "IDREQUESTO" => $IDREQUESTO,
            "IDSTATUSREQ" => $IDSTATUSREQ,
            "CDFILIAL" => $CDFILIAL,
            "NRREQUESTO" => $NRREQUESTO
        ];

        $this->Est58000Factory->updateRequestoStatus($params);
    }

    /**
     * Essa função tem como finalidade editar um item já existente de uma retirada já
     * existente. Só é possivél editar 3 campos do registro de item de uma retirada.
     *
     * @param $item | row com os dados do item a ser editado
     */
    public function editItemOfRetirada($item) {
        $params = array('QTTOTLANCTO' => $item['QTTOTLANCTO'],
                        'QTDEVOLESTOQ' => $item['QTDEVOLESTOQ'],
                        'QTLANCTOEST' => $item['QTLANCTOEST'],
                        'NRLANCESTQ' => $item['NRLANCESTQ'],
                        'CDCENTCUST' => $item['CDCENTCUST'],
                        'CDTIPORETI' => $item['CDTIPORETI'],
                        'NRORG' => $this->nrorg,
                        'NRSEQUITEM' => $item['NRSEQUITEM'],
                        'CDFILIAL' => $item['CDFILIAL']);

        $this->Est58000Factory->updateItlanctoest($params);

        return ['CDFILIAL' => $item['CDFILIAL'], 'NRLANCESTQ' => $item['NRLANCESTQ']];
    }

    /**
     * Essa função tem como finalidade chama a função de novo código, para gerar um novo código
     * conforme a tabala passada.
     *
     * @param $table | string com o nome da tabela de onde se gerara o novo código
     * @param $param | parametro para geração do novo código
     *
     * @return string | valor do código gerado
     */
    public function geraNewSequencial($table, $param = '', $colSize = 10) {
        return $this->newCode->getCodeByTableName($table.$param, $this->nrorg, 1, $colSize);
    }

    /**
     * Essa função é chamada depois de se inserir uma retirada. Ela tem a finalidade de retornar
     * para o frontend os dados de código que foram gerados, e os damais dados que não está no
     * frontend, afim de setar a currentRow, evitando de dar um reload na tela e perder no foco
     * no registro inserido. Dessa forma o usuário pode seguir com as operações na requisição
     * sem problemas.
     *
     * @param $cdfilimovi | código da filial onde foi lançada a retirada
     * @param $dtlancestq | data de lançamento da retirada
     */
    public function getLancamento($cdfilimovi, $dtlancestq) {
        $params = array("CDFILIAL"=>$cdfilimovi,
                        "DATA"=>$dtlancestq,
                        "NRLANCESTQ"=>$this->nrlancestq,
                        "NRORG"=>$this->nrorg);
        return $this->Est58000Factory->getLancamento($params);
    }

    /**
     * Essa função tem como finalidade receber um array com os itens que serão inseridos na tabela
     * ITLANCTOEST. A função percorre os itens passados para ela, montando um array associativo com
     * os parametros que serão passados para a query. Os produtos são inseridos um a um na tabela.
     *
     * @param $itens | array com os itens
     * @param $dtlancestq | data da retirada
     * @param $cdtiporeti | código do Tipo de Operação da retirada
     * @param $cdcentcust | código do centro de custo do Tipo de Operação da retirada
     * @param $nrrequesto | código da requisição (null se não tiver requisição)
     *
     */
    public function insertItensForRetirada($itens, $dtlancestq, $cdtiporeti, $cdcentcust, $nrrequesto, $evolestoque = 'S') {

        for($i = 0; $i < count($itens); $i++) {
            
            //Consulta para pegar alguns dados que serão inseridos na tabela ITLANCTOEST.
            $paramProd = $this->Est58000Factory->getParamProduto(
                                $params = array("CDPRODUTO"=>$itens[$i]['CDPRODUTO'],
                                                "NRORG"=>$this->nrorg));
            //Se a row ja tiver NRLANCESTQ, significa que é a inserção de um item novo em uma
            //retirada ja existente. Se não está no processo de inserir uma retirada.
            if(empty($itens[$i]['NRLANCESTQ'])) {
                $itens[$i]['NRLANCESTQ'] = $this->nrlancestq;
            }
            
            $paramsVrbrut = [
                "CDPRODUTO" => $itens[$i]['CDPRODUTO'],
                "CDFILIAL" => $itens[$i]['CDFILIMOVI'],
                "CDALMOXARIFE" => $itens[$i]['CDALMOXARIFE'],
                "CDLOCALESTOQ" => $itens[$i]['CDLOCALESTOQ'],
                "NRLOTEESTQ" => $itens[$i]['NRLOTEESTQ'],
                "NRSUBLOTE" => $itens[$i]['NRSUBLOTE']
            ];
            $vrlanctobrut = $this->getPosestDia($paramsVrbrut);
            //Montagem dos parametros
            
            // verifica se é novo lançamento
            $paramsLancto = array("P_CDFILIAL"=>$itens[$i]['CDFILIAL'],
                                "P_NRLANCESTQ"=>$itens[$i]['NRLANCESTQ']);
            $lancto = $this->Est58000Factory->getLanctoExistente($paramsLancto);
            
            $filialToSave = $itens[$i]['CDFILIAL'];   // sempre a filial da retirada
            $itens[$i]['CDFILIMOVI'] = $filialToSave;
            // $itens[$i]['NRLANCESTQ'] = $this->nrlancestq;

            
            if(is_null($itens[$i]['NRSEQUITEM']))
            
            $itens[$i]['NRSEQUITEM'] = $this->ValidacoesService->getMaxNrSeqItemItlanctoest($filialToSave, $itens[$i]['NRLANCESTQ']);
            
            $params = array("CDFILIAL"=>$filialToSave,
                            "NRLANCESTQ"=>$itens[$i]['NRLANCESTQ'],
                            "NRSEQUITEM"=>$itens[$i]['NRSEQUITEM'],
                            "CDPRODUTO"=>$itens[$i]['CDPRODUTO'],
                            "DSLANCESTIT"=>null,
                            "CDALMOXARIFE"=>$itens[$i]['CDALMOXARIFE'],
                            "CDLOCALESTOQ"=>$itens[$i]['CDLOCALESTOQ'],
                            "NRLOTEESTQ"=>$itens[$i]['NRLOTEESTQ'],
                            "NRSUBLOTE"=>$itens[$i]['NRSUBLOTE'],
                            "CDFILIMOVI"=>$itens[$i]['CDFILIMOVI'],
                            "CDPRODMOVI"=>$paramProd['CDPRODESTO'],
                            "IDORDEMCLC"=>'9',
                            "DTLANCMOVI"=>$dtlancestq,
                            "IDEVOLESTOQ"=>$evolestoque,
                            "CDTIPORETI"=>$cdtiporeti,
                            "CDCENTCUST"=>$cdcentcust,
                            "QTTOTLANCTO"=>$itens[$i]['QTTOTLANCTO'],
                            "IDTIPOMOVI"=>$this->retirada,
                            "QTLANCTOEST"=>($itens[$i]['QTTOTLANCTO'] * $paramProd['VRFATOCONV']),

                            "VRUNILANCTO"=>round($itens[$i]['VRUNILANCTO'],5),
                            "VRTOTLANCTO"=>round($itens[$i]['VRTOTLANCTO'],5),
                            "QTDEVOLESTOQ"=>$itens[$i]['QTDEVOLESTOQ'],
                            "VRLANCTOEST"=>round($itens[$i]['VRUNILANCTO'],5),
                            "VRLANCTOBRUT"=>round($vrlanctobrut? $vrlanctobrut['VRCUSTOPROD'] : $itens[$i]['VRUNILANCTO'],5),
                            
                            "NRREQUESTO"=>$nrrequesto,
                            "NRORG"=>$this->nrorg,
                            "IDATIVO"=>$this->idativo,
                            "DTULTATU"=>$this->currentTime,
                            "NRORGULTATU"=>$this->nrorg,
                            "CDOPERULTATU"=>$this->operador,
                            "DTINCLUSAO"=>$this->currentTime,
                            "NRORGINCLUSAO"=>$this->nrorg,
                            "CDOPERINCLUSAO"=>$this->operador);
            try{
                $this->Est58000Factory->insertItlanctoest($params);
            }catch(\Exception $e){
                throw $e;
            }
        }

        return ["CDFILIAL" => $itens[0]['CDFILIAL'], "NRLANCESTQ" => $itens[0]['NRLANCESTQ']];
    }

    /**
     * Essa função tem como finalidade executar o processo de edição de uma retirada.
     * Para isso é necessário alterar registro em três tabelas diferentes. Na RETIRADESTO,
     * na LANCTOESTO. Em seguida todos os itens daquela retirada são buscado, para que os
     * campos necessários sejam editados (tabela ITLANCTOEST).
     *
     * @param $lancamento | row com so dados da retirada que esta sendo editada
     */
    public function processEditLancamento($lancamento) {
        $paramsToUpdateRetiradaesto = array('CDSETOR' => $lancamento['CDSETOR'],
                        'CDCENTCUST' => $lancamento['CDCENTCUST'],
                        'CDFILIAL' => $lancamento['CDFILIAL'],
                        'NRORG' => $lancamento['NRORG'],
                        'NRRETEST' => $lancamento['NRRETEST']);

        $this->Est58000Factory->updateRetiradaesto($paramsToUpdateRetiradaesto);

        $paramsToUpdateLanctoestoq = array('DSLANCESTQ' => $lancamento['DSLANCESTQ'],
                                           'CDFILIAL' => $lancamento['CDFILIAL'],
                                           'NRORG' => $lancamento['NRORG'],
                                           'NRLANCESTQ' => $lancamento['NRLANCESTQ']);

        $this->Est58000Factory->updateLanctoesto($paramsToUpdateLanctoestoq);

        $paramsGetItens = array('NRLANCESTQ' => $lancamento['NRLANCESTQ'],
                                'CDFILIAL' => $lancamento['CDFILIAL'],
                                'NRORG' => $lancamento['NRORG']);

        $itens = $this->Est58000Factory->getItensFromRetirada($paramsGetItens);

        for ($i = 0; $i < count($itens); $i++) {
            $paramsForUpdateItlanctoest = array('QTTOTLANCTO' => $itens[$i]['QTTOTLANCTO'],
                                                'QTDEVOLESTOQ' => $itens[$i]['QTDEVOLESTOQ'],
                                                'QTLANCTOEST' => $itens[$i]['QTLANCTOEST'],
                                                'CDCENTCUST' => $lancamento['CDCENTCUST'],
                                                'CDTIPORETI' => $lancamento['CDTIPORETI'],
                                                'NRLANCESTQ' => $itens[$i]['NRLANCESTQ'],
                                                'NRORG' => $itens[$i]['NRORG'],
                                                'NRSEQUITEM' => $itens[$i]['NRSEQUITEM'],
                                                'CDFILIAL' => $itens[$i]['CDFILIAL']);

            $this->Est58000Factory->updateItlanctoest($paramsForUpdateItlanctoest);
        }
    }

    /**
     *
     * @param $item | row com dados do item a ser excluido
     */
    public function processDeleteItem($item) {
        $this->processDeleteItemRetiradaComRequisicao($item);

        $params = [
            "CDFILIAL" => $item["CDFILIAL"],
            "NRLANCTOEST" => $item["NRLANCESTQ"],
            "NRSEQUITEM" => $item["NRSEQUITEM"]
        ];

        $this->Est58000Factory->deleteItlanctoest($params);
        
        if(!is_null($item["NRREQUESTO"])) {
            $newReqStatus = $this->getProperRequisicaoStatus($item);
            $this->updateRequisicaoStatus($item["CDFILIMOVI"], $item["NRREQUESTO"], $newReqStatus);
        }
    }

    public function processDeleteItemRetiradaComRequisicao($item) {
    if(!is_null($item["NRREQUESTO"])) {
            $this->processDeleteItemRetiradaComRequisicaoComplementar($item);

        $params = [
            "IDSTITREQU"   => 'A',
            "NRSEQUITEM"   => null,
            "NRLANCESTQ"   => null,
            "OLD_NRLANCESTQ" => null,
            "CDFILILANC"   => null,
            "NRSEQITEM"    => null,
            "CDFILIAL"     => $item["CDFILIMOVI"],
            "NRREQUESTO"   => $item["NRREQUESTO"],
            "CDPRODUTO"    => $item["CDPRODUTO"],
            "NRORG"        => $this->nrorg
        ];
        $this->Est58000Factory->updateItrequestoStatus($params);
        }
    }
    public function getRequisicaoComplementar($row) {
        $params = [
            "CDFILLANCREQC" => $row["CDFILIAL"],
            "NRLANCREQC" => $row["NRLANCESTQ"]
        ];
        return $this->Est58000Factory->getRequisicaoComplementar($params);
    }

    public function processDeleteItemRetiradaComRequisicaoComplementar($item) {
        $reqComp = $this->getRequisicaoComplementar($item);

        if(!empty($reqComp)) {
            $params = $reqComp;
            $params['CDPRODUTO'] = $item['CDPRODUTO'];
            $params['NRLOTEESTQ'] = $item['NRLOTEESTQ'];
            $params['NRSUBLOTE'] = $item['NRSUBLOTE'];

            $itReqComp = $this->getItReqComp($params);

            if(empty($itReqComp)) {
                $this->Est58000Factory->deleteItrequesto($params);
            } else {
                $this->Est58000Factory->freeItemRequisicaoComplementar($params);
            }
        }
    }

    public function getItReqComp($params) {
        $usedItrequesto = $this->Est58000Factory->getUsedItrequesto($params);

        if($usedItrequesto)
            $usedItrequesto = $usedItrequesto[0];

        return $usedItrequesto;
    }

    /**
     *
     * @param $retirada | row com os dados da retirada a ser excluida
     */
    public function processDeleteRetirada($retirada) {
        $this->processDeleteRetiradaComRequisicao($retirada);

        $params = [
            "CDFILIAL" => $retirada["CDFILIAL"],
            "NRLANCTOEST" => $retirada["NRLANCESTQ"]
        ];

        $this->Est58000Factory->deleteAllItlanctoest($params);
        $this->Est58000Factory->deleteRetiradaesto($params);
        $this->Est58000Factory->deleteLanctoestoq($params);
    }

    public function processDeleteRetiradaComRequisicao($retirada) {
        if(!is_null($retirada["NRREQUESTO"])) {
            $this->processDeleteRetiradaComRequisicaoComplementar($retirada);

            $params = [
                "IDSTITREQU" => 'A',
                "CDFILILANC" => null,
                "NRSEQUITEM" => null,
                "NRLANCESTQ" => null,
                "OLD_NRLANCESTQ" => $retirada["NRLANCESTQ"],
                "CDPRODUTO" => null,
                "NRSEQITEM" => null,
                "CDFILIAL" => $retirada["CDFILIAL"],
                "NRREQUESTO" => $retirada["NRREQUESTO"],
                "NRORG" => $this->nrorg
            ];
            $this->Est58000Factory->updateItrequestoStatus($params);
            
            $newReqStatus = $this->getProperRequisicaoStatus($retirada);
            $this->updateRequisicaoStatus($retirada["CDFILIAL"], $retirada["NRREQUESTO"], $newReqStatus);
        }
    }

    public function processDeleteRetiradaComRequisicaoComplementar($retirada) {
        $reqComp = $this->getRequisicaoComplementar($retirada);

        if(!empty($reqComp)) {
            if($this->requisicaoComplementarIsFree($reqComp)) {
                $this->Est58000Factory->deleteRequesto($reqComp);
            } else {
                $params = [
                    "CDFILIAL" => $retirada["CDFILIAL"],
                    "NRREQUESTO" => $retirada["NRREQUESTO"]
                ];
                $this->Est58000Factory->freeRequisicaoComplementar($params);
            }
        }
    }

    public function requisicaoComplementarIsFree($reqComp) {
        $params = $reqComp;
        $params['CDPRODUTO'] = null;
        $params['NRLOTEESTQ'] = null;
        $params['NRSUBLOTE'] = null;

        $usedReqCompItems = $this->Est58000Factory->getUsedItrequesto($params);

        return empty($usedReqCompItems);
    }

    /**
     * Essa função tem a finalidade de salvar a movimentação realizada na tabela LANCTOESTOQ.
     *
     * @param $dtlancestq | data da movimentação
     * @param $dslancestq | descrição da movimentação
     * @param $cdfilimovi | códugo da filial onde foi realizada a movimentação
     */
    public function saveLanctoestoq($dtlancestq, $dslancestq, $cdfilimovi) {
        $this->nrlancestq = $this->geraNewSequencial('LANCTOEST', $cdfilimovi);
        $params = array("CDFILIAL"=>$cdfilimovi,
                        "CDOPERADOR"=>$this->operador,
                        "IDTPLANCTO"=>$this->retirada,
                        "DTLANCESTQ"=>$dtlancestq,
                        "DSLANCESTQ"=>$dslancestq,
                        "CDFILIMOVI"=>$cdfilimovi,
                        "NRLANCESTQ"=>$this->nrlancestq,
                        "NRORG"=>$this->nrorg,
                        "IDATIVO"=>$this->idativo,
                        "DTULTATU"=>$this->currentTime,
                        "NRORGULTATU"=>$this->nrorg,
                        "CDOPERULTATU"=>$this->operador,
                        "DTINCLUSAO"=>$this->currentTime,
                        "NRORGINCLUSAO"=>$this->nrorg,
                        "CDOPERINCLUSAO"=>$this->operador);
        $this->Est58000Factory->insertLanctoestoq($params);
    }

    /**
     * Essa função é chamada no momento em que está se inserindo uma retirada. Ela tem a função
     * de inserir os registro na tabela RETIRADAESTO.
     *
     * @param $cdfilimovi | código da filial onde está sendo feita a retirada
     * @param $dtlancestq | data da retirada
     * @param $dslancestq | descrição da retirada
     * @param $cdsetor | código do setor passado para a retirada
     * @param $cdcentcust | código do centro de custo
     * @param $nrordprod | null
     * @param $nrrequesto | código da requisção passad (null se não tiver passado)
     */
    public function saveRetiradaesto($cdfilimovi, $dtlancestq, $dslancestq, $cdsetor,
                                     $cdcentcust, $nrordprod, $nrrequesto) {
        $nrretest = $this->geraNewSequencial('RETIESTO', $cdfilimovi);
        $params = array("CDFILIAL"=>$cdfilimovi,
                        "NRRETEST"=>$nrretest,
                        "DTRETEST"=>$dtlancestq,
                        "DSRETEST"=>$dslancestq,
                        "CDFILIALLAN"=>$cdfilimovi,
                        "NRLANCESTQ"=>$this->nrlancestq,
                        "CDSETOR"=>$cdsetor,
                        "CDCENTCUST"=>$cdcentcust,
                        "NRORDPROD"=>$nrordprod,
                        "NRREQUESTO"=>$nrrequesto,
                        "NRORG"=>$this->nrorg,
                        "IDATIVO"=>$this->idativo,
                        "DTULTATU"=>$this->currentTime,
                        "NRORGULTATU"=>$this->nrorg,
                        "CDOPERULTATU"=>$this->operador,
                        "DTINCLUSAO"=>$this->currentTime,
                        "NRORGINCLUSAO"=>$this->nrorg,
                        "CDOPERINCLUSAO"=>$this->operador);
        $this->Est58000Factory->insertRetiradaesto($params);
    }

    /**
     * Essa função tem como finaidade verificar se filial, onde está se executando as ações de
     * edição (inclui também edição/exclusão/inserção de um novo item) inclusão e exclusão,
     * tem inventário pendente, se tiver levanta um ero.
     *
     * @param $cdfilimovi | código da filial que está sendo editada/excluida/inserida
     * @param $dtlancamento | data de movimentação da filial que está sendo editada/excluida/inserida
     */
    public function verificaInventarioRotativo($cdfilimovi, $dtlancamento) {
        $params = array("CDFILIAL"=>$cdfilimovi,
                        "DTLANCAMENTO"=>$dtlancamento,
                        "NRORG"=>$this->nrorg);
        $inventario = $this->Est58000Factory->verificaInventarioRotativo($params);
        if(!empty($inventario)) {
            throw new \Exception("Operação bloqueada! Inventário pendente para a filial.", 1);
        }
    }
    
    public function buildObjectTheCode($code, $params,&$itensWithErrors,$ErrorRowNumber) {
        $pointer = 0;
        $objectRead = [];
        $boolUsaSeparador = $params['IDTPSEPARADOR'] <> 2;
        
        $data = DateUtil::getDataDeString(substr($code, $pointer, strlen($params["DSFORMDATA"])), $params["DSFORMDATA"]);
        if(is_null($data) || $data == false){
            $data = \DateTime::createFromFormat('dmY', substr($code, $pointer, strlen($params["DSFORMDATA"])));
        }
        if(!$data){
            //throw new \Exception("Formato de Data Inválida ou Parametrização Invalida");
            $message = "Formato de Data Inválida ou Parametrização Invalida";
            $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$code);
            return null;
            
        }
        
        $data = $data->format(Dateutil::FORMATO_BRASILEIRO);
        
        $objectRead["DTLANCESTQ"] = $data; //Lê a data
        $pointer += strlen($params["DSFORMDATA"]);
        
        //Lê o campo que identifica o produto
        $objectRead[$params["PRODUTO"]] = substr($code, $pointer, $params["QTDIGPRODUTO"]);
        $pointer += $params["QTDIGPRODUTO"];

        //Lê a quantidade (inteira)
        $int = substr($code, $pointer, $params["QTNUMEROINT"]);
        $pointer += $params["QTNUMEROINT"];
        //Confere se usa separador decimal
        $separador = substr($code, $pointer, 1);
        if($boolUsaSeparador){
            if(($separador != "," && $params["IDTPSEPARADOR"] == 0) || ($separador != "." && $params["IDTPSEPARADOR"] == 1)){
                $message = "Separador informado ( $separador ) não corresponde com a parametrização definida.";
                $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$code);
                return null;
            }
            $pointer += 1;
        }else{
            if(($separador == "," || $separador == ".") && ($params["IDTPSEPARADOR"] == 2)){
                $message = "Separador informado ( $separador ) não corresponde com a parametrização definida.";
                $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$code);
                return null;
            }
            $pointer += 0;
        }
        //Lê a quantidade (decimal)
        $decimal = substr($code, $pointer, $params["QTNUMERODEC"]);
        $pointer += $params["QTNUMERODEC"];
        
        //Seta Quantidade
        $objectRead["QTTOTLANCTO"] = $int || $decimal ? floatval($int . '.' . $decimal) : 0;

        //Lê o almoxarficado
        $objectRead["CDALMOXARIFE"] = substr($code, $pointer, $params["QTTAMALMOX"]);
        $pointer += $params["QTTAMALMOX"];
        //Lê o localização
        $objectRead["CDLOCALESTOQ"] = substr($code, $pointer, $params["QTTAMLOCAL"]);
        $pointer += $params["QTTAMLOCAL"];

        //Lê o lote
        $objectRead["NRLOTEESTQ"] = substr($code, $pointer, $params["QTTAMLOTE"]);
        $pointer += $params["QTTAMLOTE"];

        //Lê o sublote
        $objectRead["NRSUBLOTE"] = substr($code, $pointer, $params["QTTAMSUBLOTE"]);
        $pointer += $params["QTTAMSUBLOTE"];
        
        return $objectRead;
    }
    
    public function completaProduto($produto, $cdfilial, $cdfilimovi, $dtLancEst,&$itensWithErrors,$ErrorRowNumber){
        $produtoTabela = $this->getProdutoTabela($produto, $cdfilimovi);
        
        $codImportado = isset($produto['CDARVPROD'])? ($produto['CDARVPROD']) : (isset($produto['CDPRODUTO'])? $produto['CDPRODUTO'] : $produto['CDBARPRODUTO']);
        
        if(!$produtoTabela){
            if(empty($codImportado)){
                $message = "Filial sem parametrização Para importação";
                $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
                return null;
            }else{
                $message = "Produto não encontrado.";
                $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
                return null;
            }
        }
        
         // validações de controle de lote
        $nrorg = $this->environment->getNrOrgTrab();
        $paramfilial = Paramfilial::findOneByCdfilialAndNrorg($cdfilimovi, $nrorg);
        
        $utAlmox = $paramfilial->getIdutilalmox();
        $utLcEst = $paramfilial->getIdutillcestq();
        $utlote = $paramfilial->getIdutillote();
        
        $prodfili = Prodfili::findOneByCdprodutoAndCdfilialAndNrorg($produtoTabela["CDPRODMOVI"], $cdfilimovi, $nrorg);
        $prodUtLote  = $prodfili->getIdutctrllote();
        $prodAlmox   = $prodfili->getCdalmoxarife();
        $prodLocal   = $prodfili->getCdlocalestoq();
        
        if($utAlmox == 'S' && !$produto["CDALMOXARIFE"]){
            $message = "Filial controla almoxarifado e o mesmo não foi encontrado no arquivo";
            $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
            return null;
        }else if($utLcEst == 'S' && !$produto["CDLOCALESTOQ"]){
            $message = "Filial controla local de estoque e o mesmo não foi encontrado no arquivo.";
            $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
            return null;
        }else if($utlote == 'S' && $prodUtLote == 'S' && !$produto["NRLOTEESTQ"]){
            $message = "Filial controla lote e o mesmo não foi encontrado no arquivo.";
            $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
            return null;
        }
        
        
        
        if(isset($produto["QTTOTLANCTO"])){
             $qttotlancto = $produto["QTTOTLANCTO"] ? $produto["QTTOTLANCTO"] : $produtoTabela["QTTOTLANCTO"];
        }else{
            $qttotlancto = $produtoTabela["QTTOTLANCTO"];
        }
       
        $vrunit = isset($produto["VRUNILANCTO"]) ? $produto["VRUNILANCTO"] : $produtoTabela["VRUNILANCTO"];
        $vrunitbrut = isset($produto["VRUNITBRUT"]) ? $produto["VRUNITBRUT"] : $produtoTabela["VRUNITBRUT"];
        
        $vrtotlancto = $vrunit * $qttotlancto;
        $vrlanctobrut = $vrunitbrut * $qttotlancto;
        
        $vrfatoconv = $produtoTabela['VRFATOCONV']? $produtoTabela['VRFATOCONV'] : 1;
        $qtdevelestoq = isset($produto["QTDEVOLESTOQ"]) ? $produto["QTDEVOLESTOQ"] : 0;
        $qtlanctoest = ($qttotlancto - $qtdevelestoq) * $vrfatoconv;
        
        
        $produtoTabela["CDFILIAL"] = $cdfilial;
        $produtoTabela["CDFILIMOVI"] = $cdfilimovi;
        $produtoTabela["DTLANCESTQ"] = $produto["DTLANCESTQ"] ? $produto["DTLANCESTQ"] : $dtLancEst;
        $produtoTabela["CDPRODUTO"] = isset($produto["CDPRODUTO"]) ? $produto["CDPRODUTO"] : $produtoTabela["CDPRODUTO"];
        $produtoTabela["CDARVPROD"] = isset($produto["CDARVPROD"]) ? $produto["CDARVPROD"] : $produtoTabela["CDARVPROD"];
        $produtoTabela["CDALMOXARIFE"] = $produto["CDALMOXARIFE"] ? $produto["CDALMOXARIFE"] : ($prodAlmox? $prodAlmox : ' ');
        $produtoTabela["CDLOCALESTOQ"] = $produto["CDLOCALESTOQ"] ? $produto["CDLOCALESTOQ"] : ($prodLocal? $prodLocal : ' ');
        $produtoTabela["NRLOTEESTQ"] = $produto["NRLOTEESTQ"] ? $produto["NRLOTEESTQ"] : $produtoTabela["NRLOTEESTQ"];
        $produtoTabela["NRSUBLOTE"] = $produto["NRSUBLOTE"] ? $produto["NRSUBLOTE"] : $produtoTabela["NRSUBLOTE"];
        $produtoTabela["QTTOTLANCTO"] = $qttotlancto;
        $produtoTabela["VRUNILANCTO"] = $vrunit;
        $produtoTabela["VRUNITBRUT"] = $vrunitbrut;
        $produtoTabela["QTLANCTOEST"] = $qtlanctoest;
        $produtoTabela["QTDEVOLESTOQ"] = isset($produto["QTDEVOLESTOQ"]) ? $produto["QTDEVOLESTOQ"] : null;

        return $produtoTabela;
    }
    
    public function insertLocaisEstoque($produto){
        $this->insertLoteProduto($produto);
        $nrorg          = $nrorg = $this->environment->getNrOrgTrab();
        $cdfilial       = $produto['CDFILIMOVI'];
        $cdalmoxarife   = isset($produto['CDALMOXARIFE'])? $produto['CDALMOXARIFE'] : ' ';
        $dsalmoxarife   = isset($produto['CDALMOXARIFE'])? $produto['CDALMOXARIFE'] : ' ';
        $this->Est58000Factory->insertAlmoxarife($cdfilial, $cdalmoxarife, $dsalmoxarife, $nrorg);
        $cdlocalestoq   = isset($produto['CDLOCALESTOQ'])? $produto['CDLOCALESTOQ'] : ' ';
        $dslocalestoq   = isset($produto['CDLOCALESTOQ'])? $produto['CDLOCALESTOQ'] : ' ';
        $estdesmemb     = 'N';
        $this->Est58000Factory->insertLocalEstoque($cdfilial, $cdalmoxarife, $cdlocalestoq, $dslocalestoq, $estdesmemb, $nrorg);
    }
    
    public function insertLoteProduto($produto){
        $cdfilial   = $produto['CDFILIAL'];
        $cdproduto  = $produto['CDPRODUTO'];
        $nrloteestq = $produto['NRLOTEESTQ'];
        $nrsublote  = $produto['NRSUBLOTE'];
        $dtlancamento = $produto["DTLANCESTQ"];
        $dtvalilote = isset($produto['DTVALILOTE'])? $produto['DTVALILOTE'] : null;
        $dtfrabric = isset($produto['DTFABRPROD'])? $produto['DTFABRPROD'] : null;
        
        return $this->Est58000Factory->insertLoteProduto($cdfilial, $cdproduto, $nrloteestq, $nrsublote, $dtlancamento, $dtvalilote, $dtfrabric);
        
    }
    
    public function getProdutoTabela($produto, $cdfilial){

        if(isset($produto["CDARVPROD"])){
            $parameters = array(
                "P_CDARVPROD" => str_replace(" ", "", $produto["CDARVPROD"])
            );
        } else if(isset($produto["CDBARPRODUTO"])){
            $parameters = array(
                "P_CDBARPRODUTO" => str_replace(" ", "", $produto["CDBARPRODUTO"])
            );
        } else{
            $parameters = array(
                "P_CDPRODUTO" => str_replace(" ", "", $produto["CDPRODUTO"])
            ); 
        }
        $parameters["P_CDFILIAL"] = $cdfilial;
        return $this->Est58000Factory->getProdutoTabela($parameters);
    }
    
    public function readXls($row){
        
        $file = $row['FILE'];
        $columnNames = $row['CAMPOS'];
        
        
        $sheetsToLoad = array('Sheasdasdsadet1', 'Sheet1', 'Planilha1');
        $xlsFile = FileLoader::loadFromBase64($file['content'], $file['name']);
        if(!$xlsFile){
            throw new \Exception(self::IMPORT_LOAD_FILE_ERROR, 400);
        }
        $worksheet = $xlsFile->getActiveSheet();
        $table = FileReader::readWorksheetTable($worksheet, $columnNames);
        
        return $table;
    }
    
    public function formatXls(Table $table){
        $columnsReference = $table->getColumnsReference();
        $tableRows = $table->getRows();
        $columnsNumber = $table->getNumberOfColumns();
        $i = 0;
        foreach ($tableRows as &$row) {
            $row = $row['content'];
            for($column = 0; $column < $columnsNumber; $column++){
                $columnLetterIndex = Coordinate::stringFromColumnIndex($column+1);

                $columnName = $columnsReference[$columnLetterIndex];
                $row[$columnName] = self::formatValue($row[$columnName], $columnName);
            }
            $row["ROWNUM"] = $i;

            $i++;
        }
        return $tableRows;
    }
    
    public function buildObjectTheCodeXls($produto, $params,&$itensWithErrors,$ErrorRowNumber) {
        //builds objectRead
        $objectRead = [
            $params["PRODUTO"] => $produto[$params["PRODUTO"]],
            "DTLANCESTQ" => $this->validatesDate('DTLANCESTQ',$produto, $params,$itensWithErrors,$ErrorRowNumber),
            "DTVALILOTE" => $this->validatesDate('DTVALILOTE',$produto, $params,$itensWithErrors,$ErrorRowNumber),
            "QTTOTLANCTO"=> (float)$produto['QTTOTLANCTO'],
            "QTDEVOLESTOQ"=> (float)$produto["QTDEVOLESTOQ"],
            "CDALMOXARIFE" => $produto['CDALMOXARIFE'] ?? ' ',
            "CDLOCALESTOQ" => $produto['CDLOCALESTOQ'] ?? ' ',
            "NRLOTEESTQ" => $produto['NRLOTEESTQ'] ?? ' ',
            "NRSUBLOTE" => $produto['NRSUBLOTE'] ?? ' ',
        ];
        
        return $objectRead;
    }
    
    public function buildErrorRow(&$itensWithErrors,$ErrorRowNumber,$message,$produto = null){
        
        
        $line = ' Erro na linha '.$ErrorRowNumber.' do arquivo.';
        $codigoArvoreProduto = 0;
        $infoProduto = '';
        
        
        if($produto != null){
            $codigoArvoreProduto = $produto['CDARVPROD'];
            $infoProduto = ' Código de árvore de produto: '.$codigoArvoreProduto;
            $message = $message.$infoProduto;
        }else{
            $message = $message.$line;
        }
        
        array_push($itensWithErrors,[
            "IDREF" => $ErrorRowNumber,
            "DESCERROR" => $message
        ]);
       
    }
    
    private function formatValue($value, $columnName){
        switch($columnName){
            case $columnName == 'CDBARPRODUTO':
            case $columnName == 'CDARVPROD':
            case $columnName == 'CDPRODUTO':
                $value = self::cleanSpecialCharacters(strval($value));
                break;
            case $columnName == 'QTTOTLANCTO':
            case $columnName == 'VRUNILANCTO':
            case $columnName == 'CDALMOXARIFE':
            case $columnName == 'CDLOCALESTOQ':
            case $columnName == 'NRLOTEESTQ':
            case $columnName == 'NRSUBLOTE':
            case $columnName == 'DTLANCESTQ':
            case $columnName == 'DTFABRPROD':
            case $columnName == 'DTVALILOTE':
                break;
            default: 
                $value = strval($value);
        }
        return $value;
    }
    public function cleanSpecialCharacters($string){
        $string = str_replace('.', '', $string); // Replaces all spaces with hyphens.
        return preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars.
    }
    
    public function validatesDate($fieldName,$produto, $params, &$itensWithErrors, $ErrorRowNumber){
        
        if(!isset($produto[$fieldName]))return;
        
        $cleanDate = str_replace('/', '', $produto[$fieldName]);
        $cleanDate = str_replace('-', '', $cleanDate);
        $cleanDateParam = str_replace('/', '', $params["DSFORMDATA"]);
        $cleanDateParam = str_replace('-', '', $cleanDateParam);
        
        $data = DateUtil::getDataDeString($cleanDate, $cleanDateParam);
        
        if(is_null($data) || $data == false)
            $data = \DateTime::createFromFormat('dmY', $cleanDate);
        
        
        
        if(!$data){
            
            if($fieldName === 'DTLANCESTQ'){
                $message = "Formato de Data Inválida ou Parametrização Invalida";
                $this->buildErrorRow($itensWithErrors,$ErrorRowNumber,$message,$produto);
                return null;
            }else{
                $data = null;
            }
        }else{
            $data = $data->format(Dateutil::FORMATO_BRASILEIRO);
            
        }
            
            
        
        
        return $data;
    }
}