<?php
include_once("utilities/na_utils.php");
include_once("utilities/utilities.php");
include_once("utilities/sql_utilies.php");

require_once '../allInOneWeb/demosettings.php';
require_once '../allInOneWeb/sqlUtils.php';

class CompareWLClass{
    private $mysqli = null;
    private $data = array();
    private $ivEntityMatcherService = "";
    private $resultsCols = [];
    private $mappedCols = [];
    private $mappedColsView = [];
    private $accumColToSingle = [];
    
    private $metadata = []; //metadata from file (type, size etc)
    private $input_fields = []; //
    private $allowed_ext = []; //allowed ext (in config.php)
    
    private $names_upload = []; //array for names fetched from csv to upload as WL
    private $csv_seperator = ""; //in config.php - for csv parsing
    private $name_index; //index of name column in csv - could be array of few columns
    private $sqlValues = [];    
    private $bulk_limit;
    private $wl_table;
    private $rules_json;
    private $rules_relation_json;
    private $columns_metadata;
    private $reverseOrder;
    
    function __construct($mysqli, $action, $inputData = null){   
        session_write_close();
        
        global $ivProjectName;
        require('..'.DIRECTORY_SEPARATOR.'iventitymatcherhtm'.DIRECTORY_SEPARATOR .'config.php');
        
        $this->mysqli = $mysqli;
        
        if($action =="name"){
            $formvalue = $inputData;
            $text = $inputData["content"] ?? $inputData["fileContent"] ?? $inputData[0] ?? null; //fetch teh text data from the file (base64)
        }
        elseif($action=="list"){
            $formvalue = $inputData['formvalue'];
            $data = $inputData['data'];
            $text = $inputData["content"] ?? $inputData["fileContent"] ?? $inputData[0] ?? null; //fetch teh text data from the file (base64)
        }
        else{
            $formvalue = $inputData;
            $data = $formvalue;        
        }
        
        
        //to assure it is delcared 
        if(empty($csv_seperator)){
            global $csv_seperator;
        }
        if(empty($allowed_ext)){
            global $allowed_ext;
        }
 
        if(empty($ivEntityMatcherService)){
            global $ivEntityMatcherService;
        }
        
        if(empty(($bulk_limit))){
            global $bulk_limit;
        }
        
        $this->data = $text ?? $data ?? "";
        $this->metadata = $data["fileData"] ?? null;
        
        //from config.php
        $this->ivEntityMatcherService = getSystemSettingsProp($this->mysqli, "ivEntityMatcherService");
        
        $this->csv_seperator =  getSystemSettingsProp($this->mysqli, "csv_seperator");
        $this->allowed_ext = $allowed_ext;
        
        $this->bulk_limit = getSystemSettingsProp($this->mysqli, "bulk_limit");
        
        $this->wl_table = $data[0]["table_name"] ?? $data["table_name"] ?? null;
        
        $possible_same_person_rules = json_decode(file_get_contents('../iventitymatcherhtm/jsons/possible_same_person_rules.json',true));
        $relations_rules = json_decode(file_get_contents('../iventitymatcherhtm/jsons/relations_rules.json',true));
        
        $this->rules_json = json_decode(json_encode($possible_same_person_rules), TRUE);           
        $this->rules_relation_json = json_decode(json_encode($relations_rules), TRUE);
        
        $this->rules_json = 
        !empty($formvalue["relationMatching"]) || $_REQUEST["Action"] == "uploadWL" || ($_REQUEST["relationMatching"] ?? null) ? 
                            array_merge($this->rules_json, $this->rules_relation_json) :
                            $this->rules_json;
        
        $columns_json = json_decode(file_get_contents('../iventitymatcherhtm/jsons/columns.json',true));
        $columns_arr = json_decode(json_encode($columns_json), TRUE);
        $this->resultsCols = $columns_arr["resultsCols"];
        $this->mappedCols = $columns_arr["mappedCols"];
        $this->mappedColsView = $columns_arr["mappedColsView"];
        
        $this->accumColToSingle = $columns_arr["accumColToSingle"];
        
        $this->columns_metadata = ["mappedCols" => $this->mappedCols, "accumColToSingle" => $this->accumColToSingle, "mappedColsView" => $this->mappedColsView];
        
        $this->reverseOrder = $formvalue['reverseOrder'] ?? false;
        
        
        
    }

    /**
     * compareDBWLs
     */
    function compareDBWLs(){
        $wlSelected = $source = $this->data["target"];
        $wl_source = sqlCreateDBConnection($source);
        
        $target = $this->data["target_second"];
        $wl_target = sqlCreateDBConnection($target);
        $first_row = true;
        
        //1 - get full data of both lists
        $source_data = getWlFullAnalyseData($wl_source, $source);
        $target_data = getWlFullAnalyseData($wl_target, $target);
        $index = 0;
        $totalNames = count($target_data);
        write_to_log("INFO", "compareWls - running on $totalNames rows");
        $full_matches_arr = [];
        
        //2 - fetch an array of matches 
        $target_data = $this->getFullMatchesArr($source, $target, $source_data, $target_data, $wl_target);
        
        //3 - perpare the data and insert to SQL
        $index = 0;
        foreach($target_data as &$cur_res_arr){
            foreach($cur_res_arr as $cur_res){
                
                //look for exact match as well
                if(isset($cur_res["warning"]) && $cur_res["warning"] == "No Analysis"){
                    $exact_matches_arr = getExactMatchData($wl_source, $cur_res);
                    $matches = [];
                    if(!empty($exact_matches_arr)){
                        $input = key($exact_matches_arr["possible_same_person"]);
                        $ids_arr = $exact_matches_arr["possible_same_person"][$input];
                        $matches["possible_same_person"][] =  ["Name" => $input,"ID" => implode(',', $ids_arr)];
    
                        $cur_res["matching_results"]["possible_same_person"] = $matches["possible_same_person"];
                        $cur_res["matching_metadata"] = "";
                    }
                }
                
                if(!array_key_exists("matching_results", $cur_res)){
                    $cur_res["matching_results"] = "";
                    $cur_res["matching_metadata"] = "";
                }
                if(!array_key_exists("triggered_rule", $cur_res)){
                    $cur_res["triggered_rule"] = "";
                }
                
                if(empty($cur_res["matching_results"]) && ($this->data["matchesOnly"] ?? false)){
                    continue;
                }
                
                if(array_key_exists("matching_metadata", $cur_res) && is_array($cur_res["matching_metadata"])){
                    foreach($cur_res["matching_metadata"] as $rule=>&$arr){
                        $arr = array_values($arr);
                    }
                }
                
                //             $matching_results = accumulateMatches([$cur_res["matching_results"]], $this->rules_json);
                $cur_res["matching_results"] = $cur_res["matching_results"] ?? [];
                $cur_res["matching_metadata"] = $cur_res["matching_metadata"] ?? [];
                
                addValuesDB($this->mysqli, $cur_res, $this->sqlValues);
                
                if(count($this->sqlValues) % $this->bulk_limit == 0 && count($this->sqlValues) > 0){
                    write_to_log("INFO", "$index/$totalNames analysed"); 
                    $columns = array_unique(array_merge(array_keys($cur_res), $this->resultsCols, ["matching_results", "matching_metadata"]));
                    insertBulkDB($this->mysqli, $this->sqlValues,  $this->wl_table, $columns, $first_row,$wlSelected);
                    $this->sqlValues = [];
                }
                $index++;
            }
        }
        if(count($this->sqlValues) > 0){
            write_to_log("INFO", "Finishing: $index/$totalNames analysed");
            $columns = array_unique(array_merge(array_keys($cur_res), $this->resultsCols, ["matching_results", "matching_metadata"]));
            insertBulkDB($this->mysqli, $this->sqlValues,  $this->wl_table, $columns, $first_row,$wlSelected);
            $this->sqlValues = [];
        }
        
        return $this->wl_table;
    }
    

    /**
     *
     * @param unknown $source
     * @param unknown $target
     * @param unknown $target_data
     * @param unknown $wl_target
     * @return array[]|unknown
     */
    function getFullMatchesArr($source, $target, $source_data, $target_data, $wl_target){
        $full_matches_arr = [];
        foreach(array_keys($this->rules_json) as $rule){
//             $intersect_sql = "SELECT DISTINCT t1.person_ref ref1,t2.person_ref ref2
//             FROM $source.$rule t1
//             INNER JOIN $target.$rule t2 ON
//             t1.rule_key=t2.rule_key";
            
            $intersect_sql = "SELECT  GROUP_CONCAT(DISTINCT t1.person_ref) AS ref1,
                	           t2.person_ref AS ref2,
                                GROUP_CONCAT(DISTINCT t2.na_index) AS na2
                                FROM $source.$rule t1
                                INNER JOIN $target.$rule t2 ON
                                t1.rule_key=t2.rule_key GROUP BY ref2";
            
            $res = sqlquery($wl_target,$intersect_sql);
            while ($row = $res->fetch_object()){
                if(empty($full_matches_arr[$rule])){
                    $full_matches_arr[$rule] = [];
                }
                $ref_1 = $row->ref1;
                $ref_2 = $row->ref2;
                $ref_2_unique = implode(',',array_unique(explode(',', $ref_2)));
                
                $na_indexes_2 = $row->na2;
                
                $ref_2_input = trim($target_data[$ref_2_unique][0]["input"]);
 
                $full_matches_arr[$rule][$ref_2_unique][$ref_2_input] = ["source_refs"=>$ref_1, "target_na_indexes"=>$na_indexes_2]; 
            }
        }
        foreach($full_matches_arr as $rule=>$data_arr){
            foreach($data_arr as $source_id=>$target_matches){
                foreach($target_matches as $input=>$matches){
                    $source_refs = $matches["source_refs"];
                    $target_na_indexes = $matches["target_na_indexes"];
                    
                    $curr_target_arr = $target_data[$source_id];
                 
                    if(!extraValidationRules($curr_target_arr[0], $target_data, $rule, $source_id, $input)){
                        continue;
                    }
                    
                    $target_na_arr = explode(',', $target_na_indexes);
                    foreach($target_na_arr as $index){
                 
                        $curr_target_arr[$index]["matching_results"][$rule][] = ["ID"=>$source_refs, "Name"=>$input];
                    }
                 
                    $target_data[$source_id] = $curr_target_arr;
                }
            }
        }
        return $target_data;
        }

    /**
     * 
     * @param unknown $id
     * @param unknown $array
     * @return unknown|NULL
     */
    function searchForPersonRef($id, $array) {
        $keys = [];
        foreach ($array as $key => $val) {
            if ($val['person_ref'] === $id) {
                $keys[] = $key;
            }
        }
        return $keys;
    }
  
    
    function getCsvData($file_Data){
        $decoded_data = base64_decode($file_Data);
        
        $lines = preg_split("/\\r\\n|\\r|\\n/", $decoded_data);
        $first = true;
        $input_fields = [];
        $name_index = [];
        $curr_id = 0;
        foreach ($lines as $name) {
            $curr_id++;
            if(empty($name)){
                continue;
            }
            $input_data = handleCSV($name, $this->csv_seperator, $first, $input_fields, $name_index, $curr_id);
            $name = $input_data["name"] ?? null;
            if(empty($name)){
                continue;
            }
            $name = ucwords(strtolower($name));
            $name = str_replace('.', '', $name);
            
            
            $names_arr[] = $name;
            $extra_input[] = $input_data["extra_input"] ?? null;
        }
        return ["names_arr"=>$names_arr, "extra_input"=>$extra_input];
    }
    
    /**
     * compareWls - will compare 2 wl and return the results of that compare    
     * @param unknown $wlSelected
     * @param unknown $sourcefile
     * @return the matches in the response
     */
    function compareWls(){
        write_to_log("INFO", "compareWls Process");
        set_time_limit(0); //disable time limit for the script to run
        
        //initilize variables
        $response = array();       
        $file_Data_arr = [];
        $first_row = true;
        $wlSelected = $this->data["target"];
        $sourcefile = $this->data;
        $file_Data = $sourcefile["content"];
        $wl_mysql = sqlCreateDBConnection($wlSelected);
        $ext = pathinfo($this->data["fileData"]["name"], PATHINFO_EXTENSION);
        
        //check if the file has supported extension (csv or txt)
        if(!in_array(strtolower(trim($ext)), $this->allowed_ext)){
            write_to_log("ERROR", "extenstion <$ext> not allowed");
            return array("Error" => "extenstion <$ext> not allowed");
        }
        
        //fetch the names from the csv file
        if($ext == 'csv'){
            write_to_log("TRACE", "Running on csv file");
            $csv_res = $this->getCsvData($file_Data);
            $names_arr = $csv_res["names_arr"];
            $extra_input= $csv_res["extra_input"];
              
        }
        //or from txt file
        elseif($ext == "txt"){
            $names_arr = explode("\n",base64_decode($file_Data));     
        }

        $extra_input_arr = !empty($extra_input) ? array_chunk($extra_input, $this->bulk_limit) : null;
        
        $chunks_res = getListChunks($names_arr, $this->bulk_limit); //creates chunks from the whole names_arr - if its exceeding the bulk_limit
        $totalNames = $chunks_res["total"];
        $file_Data_arr = $chunks_res["file_Data_arr"];
        
        //iterate over the names
     
        $arr_index = 0;
        /**
         * $file_Data_arr - contains chunk of names at size $this->bulk_limit
         */
        foreach($file_Data_arr as $entities_data){            
            $curr_extra_input = !empty($extra_input_arr) ? $extra_input_arr[$arr_index] : null;
            $arr_index++;
            write_to_log("INFO", "Running bulk $arr_index out of ".count($file_Data_arr));
            
            $resEM = searchList($wl_mysql, $this->ivEntityMatcherService, $wlSelected, $this->rules_json, $this->columns_metadata, $entities_data,$this->reverseOrder);
 
            ksort($resEM);
            
            foreach($resEM as $element){   
                $curr_cols = $this->elementProcess($element, $curr_extra_input, $totalNames, $wlSelected);
            }
        }
        /**
         * once done with the loop - if we have values in the DB array - add them to the db and empty
         */
        if(count($this->sqlValues) > 0){
            $columns = array_unique(array_merge(($curr_cols), $this->resultsCols, ["matching_results", "matching_metadata"]));
            
            insertBulkDB($this->mysqli, $this->sqlValues,  $this->wl_table, $columns, $first_row,$wlSelected);
            $this->sqlValues = [];
        }

        return $this->wl_table;
    }     
    
    /**
     * elementProcess - runs over each element in list for the compare WL process
     * @param unknown $element
     * @param unknown $curr_extra_input
     * @param unknown $totalNames
     * @param unknown $wlSelected
     * @return array
     */
    function elementProcess($element, $curr_extra_input, $totalNames, $wlSelected){
        $index = 0 ;
        $curr_id = 0;
        $innerRes = array();
        if(empty($element)){
            return;
        }
        $name = $element['name'] ?? $element['Name'];
        $name = preg_replace('!\s+!', ' ', $name); //replace multiple spaces with single in name
        
        $innerRes = array();
        $index = 0;
        $curr_id++;
        
        $analysedData = fetchPersonData($this->ivEntityMatcherService, $name, $this->reverseOrder);
        $columns = [];
        for($curr_na_index = 0; $curr_na_index < count($analysedData["Name Alternatives"]); $curr_na_index++){
            $na = $analysedData["Name Alternatives"][$curr_na_index];
            
            $cur_res = array_combine($this->resultsCols, array_fill(0, count($this->resultsCols), ''));
            $cur_res["id"] = $curr_id;
            $cur_res["input"] = $name;
            foreach($cur_res as $col=>$val){
                
                
                $na["id"] = $curr_id;
                //if the column appears in the current element
                if(array_key_exists($col, $na)){
                    if(is_array($na[$col])){
                        $na[$col] = !empty($_REQUEST["instances"]) ? $na[$col]["instance"] : $na[$col]["caption"];
                    }
                    //B - check for mapped columns
                    if(array_key_exists($col, $this->columns_metadata["mappedColsView"])){
                        $new_col = $this->columns_metadata["mappedColsView"][$col];
                        
                        if(!empty($cur_res[$new_col])){
                            $cur_res[$new_col] .= $na[$col];
                        }
                        else{
                            $cur_res[$new_col] = $na[$col];
                        }
                        
                        continue;
                    }
                    $cur_res[$col] = $na[$col];
                }
            }
            foreach($element["Rules"] as $rule=>$results){
                foreach($results as $match){
                    $na_indexes = explode(',', $match['na_indexes']);
                    unset($match['na_indexes']);
                    if(in_array($curr_na_index,$na_indexes)){
                        $cur_res["matching_results"][$rule][] = $match ?? "";
                        $cur_res["matching_metadata"][$rule][] = "";//$match ?? "";
                    }
                }
            }        
            if(empty($cur_res["matching_results"])){
                if($this->data["matchesOnly"] ?? false){
                    continue;
                }
                else{
                    $cur_res["matching_results"] = "";
                    $cur_res["matching_metadata"] = "";
                }
            }
            
            /**
             * extra checks patch
             */
            extraChecks($cur_res, $element["ID"], $curr_extra_input ?? null);
            
            /**
             * add the current output to the db array
             * once it arrives the bulk_amount - insert and empty the array sqlValues
             */
            addValuesDB($this->mysqli,$cur_res, $this->sqlValues);
            
            $columns = array_keys($cur_res);
            
            if(count($this->sqlValues) % $this->bulk_limit == 0 && count($this->sqlValues) > 0){       
                write_to_log("INFO", "$curr_id/$totalNames Done");
                $columns = array_unique(array_merge(array_keys($cur_res), $this->resultsCols, ["matching_results", "matching_metadata"]));
                
                insertBulkDB($this->mysqli, $this->sqlValues,  $this->wl_table, $columns, $first_row,$wlSelected);
                
                $this->sqlValues = [];
            }
            
        } 
        return $columns;
    }

    /**
     * search person in wl $wlSelected
     * @param unknown $searchedPerson
     * @param unknown $wlSelected
     * @return the matches in the response
     */
    function searchPersonInList($searchedPerson, $wlSelected, $ignore_ref = false){
        $wl_mysql = sqlCreateDBConnection($wlSelected);
        
        write_to_log("TRACE", "Getting searchEntity, using: $this->ivEntityMatcherService");
        $response = array();
        
        //search $searchedPerson in list $wlSelected
        $person_data = fetchPersonData($this->ivEntityMatcherService, $searchedPerson,  $this->reverseOrder);
        $resEM = searchPerson($wl_mysql, $searchedPerson, $person_data, $wlSelected, $this->rules_json, $this->columns_metadata);
        
        //analyse each name and attached the "Match" ruled from $resEm
        if(!empty($resEM)){
            foreach($resEM as $element){
                $innerRes = array();
                $analysedData = analyseName($element["Name"],$this->ivEntityMatcherService, $this->columns_metadata, $this->resultsCols, $this->reverseOrder);
                for($curr_na_index = 0; $curr_na_index < count($analysedData); $curr_na_index++){  
                    $curr_res = $analysedData[$curr_na_index];
                    foreach($element["Rules"] as $rule=>$results){
                        foreach($results as $match){
                            $na_indexes = explode(',', $match['na_indexes'] ?? "") ;
                            unset($match['na_indexes']);
                            if(in_array($curr_na_index,$na_indexes)){
                                $curr_res = array_merge(["id"=>$element["ID"]], $curr_res);
                                $curr_res["matching_results"][$rule][] = $match ?? "";
                                $curr_res["matching_metadata"][$rule][] = "";//$match ?? "";
                            }
                        }
                    }
                    $response[] = $curr_res;
                }
            }
        }
        return $response;
    }

}