<?php

namespace Teknisa\Libs\Util;
use Teknisa\Libs\Util\Environment;
use Zeedhi\Framework\DTO;

class LibsValidations {
    
    protected $environment;
    protected $sessionParams;
    const INVALID_UNIT = 'AUTH_INVALID_UNIT';
    const PREFIX = 'P_';
    const NAMES_UNIT = ["cdfilial", "filial", "filiais", "pcdfilial"];
    const ALLOWED_VALUES_UNIT = ['FILIAL_LOGADA'];

    public function __construct(Environment $environment) {
        $this->environment = $environment;
        $this->sessionParams = array(
            'NRORG' => $this->environment->getNrOrgTrab(),
            'CDOPERADOR' => $this->environment->getCdOperador()
        );
    }
    //Ao inves de percorrer chamando callfunction verificar cada propriedade da row e fazer um isEquals para pegar casos como isEquals
    
    public function callAuthenticator($request, $validateUnit = false){
        $this->validate($request, $validateUnit);
    }
    
    // Para ignorar requisição de pesquisa
    private function validateRequest($filter){
        return (isset($filter['operator']) && stristr($filter['operator'], 'LIKE') === false);
    }
    
    //Validações gerais de valores provenientes do front
    private function validate($request, $validateUnit){
        if($request instanceof DTO\Request\Row){
            $row = $request->getRow();
            if($validateUnit && !$this->validateUnit($row)) throw new \Exception(self::INVALID_UNIT, 500);
            $newRow = $this->replaceValuesBySession($row);
            $this->setProtectedProperty($request, 'row', $newRow);
           
        } else if($request instanceof DTO\Request\DataSet){
            $rows = $request->getDataSet()->getRows();
            $newRows = array();
            foreach($rows as $cRow) {
               if($validateUnit && !$this->validateUnit($cRow)) throw new \Exception(self::INVALID_UNIT, 500);
               array_push($newRows, $this->replaceValuesBySession($cRow));
            }
            $this->setProtectedProperty($request->getDataSet(), 'rows', $newRows);
        } else if($request instanceof DTO\Request\Filter){
            $conditions = $request->getFilterCriteria()->getConditions();
            $newConditions = array();
            foreach($conditions as $filter){
                if($validateUnit && !$this->validateUnit($filter, true)) throw new \Exception(self::INVALID_UNIT, 500);
                array_push($newConditions, $this->replaceValuesBySession($filter, true));
            }
            $this->setProtectedProperty($request->getFilterCriteria(), "conditions", $newConditions);
        }
    }
    
    //Substituição do nrorg e cdoperador pelos registrados na autenticação no sistema
    private function replaceValuesBySession($row, $isFilter = false){
        $arrayParams = ['NRORG', 'CDOPERADOR'];
        if($isFilter){
            foreach($arrayParams as $param){
                if($this->isEquals($row['columnName'], $param) || $this->isEquals($row['columnName'], self::PREFIX.$param)){
                    $row['value'] = $this->sessionParams[$param];
                }
            }
            return $row;
        }
        $rowKeys = array_keys((array)$row); 
        foreach($arrayParams as $param){
            foreach($rowKeys as $key){
                if($this->isEquals($key, $param) || $this->isEquals($key, self::PREFIX.$param)){
                    $row[$key] = $this->sessionParams[$param];
                }
            }
        }
        return $row;
    }
    
    //Verificação da filial 
    public function validateUnit($row, $isFilter = false){
        if($isFilter){
            if(!$this->validateRequest($row)) return true;
            return $this->validateUnitAllNames($row['columnName'], $row['value'], $isFilter);
        } 
        return $this->validateUnitAllNames($row, null, $isFilter);
    }
    /* Verify first occurrence name unit*/
    private function validateUnitAllNames($rowOrColumnName, $value, $isFilter = false){
        foreach(self::NAMES_UNIT as $name){
            if($isFilter){
                if($this->isEquals($name, $rowOrColumnName) || $this->isEquals(self::PREFIX.$name, $rowOrColumnName)){
                    if(!$this->isValidUnit($value))  return false;
                }
            } else {
                $rowKeys = array_keys((array)$rowOrColumnName); 
                foreach($rowKeys as $key){
                    if($this->isEquals($key, $name) || $this->isEquals($key, self::PREFIX.$name)){
                        return $this->isValidUnit($rowOrColumnName[$key]);
                    }
                }
                
            }
        }
        return true;
    }

    private function isEquals($a, $b){
        return strtolower($a) === strtolower($b);
    }
    
    //Verifica se o operador possui acesso a filial informada (ou lista de filiais informadas)
    private function isValidUnit($unit){
        $operUnits = $this->environment->getFiliOper();
        
        if (!$unit || $operUnits === null) return true;
        if (is_array($operUnits) && count($operUnits) === 0) return false;
        //Array de filiais
        if(is_array($unit)){
            if($unit[0] == 'T') return true;
            
            foreach($unit as $currentUnit){
                if(array_search($currentUnit, $operUnits) === false && $this->isValidFormatUnit($currentUnit)){
                    return false;    
                }
            }
            return true;
        }
        else{
            
            if(is_string($unit)){
                if(array_search($unit, self::ALLOWED_VALUES_UNIT) !== false) return true;
                //Filiais concatenadas
                $unitArray = explode("_", $unit);
                if(count($unitArray) > 1){
                    foreach($unitArray as $currentUnit){
                        if(!empty($currentUnit)){
                            if(array_search($currentUnit, $operUnits) === false && $this->isValidFormatUnit($currentUnit)){
                                return false;
                            }
                        }
                    }
                    
                    return true;
                }
                //Valor individual
                else{
                    return ( $unit == 'T' || array_search($unit, $operUnits) !== false || !$this->isValidFormatUnit($unit));
                }
            }
            else{
                return ( array_search($unit, $operUnits) !== false || !$this->isValidFormatUnit($unit) );
            }
        }
        return false;
    }
    
    // Valida se o código da filial está valido, evitando que o pentest invalide casos como CDFILIAL | NMFILIAL ou flags como 'X'
    public function isValidFormatUnit($unit) {
        return preg_match('/^[A-z0-9]{4}$/', $unit) == 1;
    }
    
    //Criada para permitir alteração nos valores da requisição
    private function setProtectedProperty($obj, $property, $value) {
        $reflection = new \ReflectionClass($obj);
        $property = $reflection->getProperty($property);
        $property->setAccessible(true);
        return $property->setValue($obj, $value);
    }
}