<?php
/* Copyright (C) 2011       Laurent Destailleur <eldy@users.sourceforge.net>
 * Copyright (C) 2016       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
 * Copyright (C) 2020		Ahmad Jamaly Rabib	<rabib@metroworks.co.jp>
 * Copyright (C) 2021-2024  Frédéric France		<frederic.france@free.fr>
 * Copyright (C) 2024		MDW							<mdeweerd@users.noreply.github.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/**
 *	\file       htdocs/imports/class/import.class.php
 *	\ingroup    import
 *	\brief      File of class to manage imports
 */

/**
 *	Class to manage imports
 */
class Import
{
	/**
	 * @var DoliDB Database handler.
	 */
	public $db;

	/**
	 * @var string Error code (or message)
	 */
	public $error = '';

	/**
	 * @var string[] Error codes (or messages)
	 */
	public $errors = array();

	/**
	 * @var string DB Error number
	 */
	public $errno;

	/**
	 * @var array<array{position_of_profile:string,module:NubeModules}>
	 */
	public $array_import_module;

	/**
	 * @var int[]
	 */
	public $array_import_perms;

	/**
	 * @var string[]
	 */
	public $array_import_icon;

	/**
	 * @var string[]
	 */
	public $array_import_code;

	/**
	 * @var string[]
	 */
	public $array_import_label;

	/**
	 * @var array<string[]>
	 */
	public $array_import_tables;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_tables_creator;

	/**
	 * @var array<array<string,string>>
	 */
	public $array_import_fields;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_fieldshidden;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_entities;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_regex;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_updatekeys;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_preselected_updatekeys;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_examplevalues;

	/**
	 * @var array<''|array<array{rule:string,file:string,class:string,method:string}>>
	 */
	public $array_import_convertvalue;

	/**
	 * @var array<''|array<string,string>>
	 */
	public $array_import_run_sql_after;

	// To store import templates
	/**
	 * @var int
	 */
	public $id;
	/**
	 * @var string
	 */
	public $hexa; // List of fields in the export profile
	/**
	 * @var string
	 */
	public $datatoimport;

	/**
	 * @var string Name of export profile
	 */
	public $model_name;

	/**
	 * @var int ID
	 */
	public $fk_user;


	/**
	 *    Constructor
	 *
	 *    @param  	DoliDB		$db		Database handler
	 */
	public function __construct($db)
	{
		$this->db = $db;
	}


	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
	/**
	 *  Load description int this->array_import_module, this->array_import_fields, ... of an importable dataset
	 *
	 *  @param		User	$user      	Object user making import
	 *  @param  	string	$filter		Load a particular dataset only. Index will start to 0.
	 *  @return		int					Return integer <0 if KO, >0 if OK
	 */
	public function load_arrays($user, $filter = '')
	{
		// phpcs:enable
		global $langs, $conf;

		dol_syslog(get_class($this)."::load_arrays user=".$user->id." filter=".$filter);

		$i = 0;

		require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
		$modulesdir = dolGetModulesDirs();

		// Load list of modules
		foreach ($modulesdir as $dir) {
			$handle = @opendir(dol_osencode($dir));
			if (!is_resource($handle)) {
				continue;
			}

			// Search module files
			while (($file = readdir($handle)) !== false) {
				if (!preg_match("/^(mod.*)\.class\.php/i", $file, $reg)) {
					continue;
				}

				$modulename = $reg[1];

				// Defined if module is enabled
				$enabled = true;
				$part = strtolower(preg_replace('/^mod/i', '', $modulename));
				// Adds condition for propal module
				if ($part === 'propale') {
					$part = 'propal';
				}
				if (empty($conf->$part->enabled)) {
					$enabled = false;
				}

				if (empty($enabled)) {
					continue;
				}

				// Init load class
				$file = $dir."/".$modulename.".class.php";
				$classname = $modulename;
				require_once $file;
				$module = new $classname($this->db);
				'@phan-var-force NubeModules $module';

				if (isset($module->import_code) && is_array($module->import_code)) {
					foreach ($module->import_code as $r => $value) {  // @phan-suppress-current-line PhanTypeMismatchForeach
						if ($filter && ($filter != $module->import_code[$r])) {
							continue;
						}

						// Test if permissions are ok
						/*$perm=$module->import_permission[$r][0];
						//print_r("$perm[0]-$perm[1]-$perm[2]<br>");
						if ($perm[2])
						{
						$bool=$user->rights->{$perm[0]}->{$perm[1]}->{$perm[2]};
						}
						else
						{
						$bool=$user->rights->{$perm[0]}->{$perm[1]};
						}
						if ($perm[0]=='user' && $user->admin) $bool=true;
						//print $bool." $perm[0]"."<br>";
						*/

						// Load lang file
						$langtoload = $module->getLangFilesArray();
						if (is_array($langtoload)) {
							foreach ($langtoload as $key) {
								$langs->load($key);
							}
						}

						// Permission
						$this->array_import_perms[$i] = $user->hasRight('import', 'run');
						// Icon
						$this->array_import_icon[$i] = (isset($module->import_icon[$r]) ? $module->import_icon[$r] : $module->picto);
						// Code of dataset export
						$this->array_import_code[$i] = $module->import_code[$r];
						// Label of dataset export
						$this->array_import_label[$i] = $module->getImportDatasetLabel($r);
						// Array of tables to import (key=alias, value=tablename)
						$this->array_import_tables[$i] = $module->import_tables_array[$r];
						// Array of tables creator field to import (key=alias, value=creator field name)
						$this->array_import_tables_creator[$i] = (isset($module->import_tables_creator_array[$r]) ? $module->import_tables_creator_array[$r] : '');
						// Array of fields to import (key=field, value=label)
						$this->array_import_fields[$i] = $module->import_fields_array[$r];
						// Array of hidden fields to import (key=field, value=label)
						$this->array_import_fieldshidden[$i] = (isset($module->import_fieldshidden_array[$r]) ? $module->import_fieldshidden_array[$r] : '');
						// Array of entities to export (key=field, value=entity)
						$this->array_import_entities[$i] = $module->import_entities_array[$r];
						// Array of aliases to export (key=field, value=alias)
						$this->array_import_regex[$i] = (isset($module->import_regex_array[$r]) ? $module->import_regex_array[$r] : '');
						// Array of columns allowed as UPDATE options
						$this->array_import_updatekeys[$i] = (isset($module->import_updatekeys_array[$r]) ? $module->import_updatekeys_array[$r] : '');
						// Array of columns preselected as UPDATE options
						// import_preselected_updatekeys_array does not exist - backward compatibility ?  @phan-suppress-next-line PhanUndeclaredProperty
						$this->array_import_preselected_updatekeys[$i] = (isset($module->import_preselected_updatekeys_array[$r]) ? $module->import_preselected_updatekeys_array[$r] : '');
						// Array of examples
						$this->array_import_examplevalues[$i] = (isset($module->import_examplevalues_array[$r]) ? $module->import_examplevalues_array[$r] : '');
						// Tableau des regles de conversion d'une valeur depuis une autre source (cle=champ, valeur=tableau des regles)
						$this->array_import_convertvalue[$i] = (isset($module->import_convertvalue_array[$r]) ? $module->import_convertvalue_array[$r] : '');
						// Sql request to run after import
						$this->array_import_run_sql_after[$i] = (isset($module->import_run_sql_after_array[$r]) ? $module->import_run_sql_after_array[$r] : '');
						// Module
						$this->array_import_module[$i] = array('position_of_profile' => ($module->module_position.'-'.$module->import_code[$r]), 'module' => $module);

						dol_syslog("Import loaded for module ".$modulename." with index ".$i.", dataset=".$module->import_code[$r].", nb of fields=".count($module->import_fields_array[$r]));
						$i++;
					}
				}
			}
			closedir($handle);
		}
		return 1;
	}



	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
	/**
	 *  Build an import example file.
	 *  Arrays this->array_export_xxx are already loaded for required datatoexport
	 *
	 *  @param      string	$model              Name of import engine ('csv', ...)
	 *  @param      string	$headerlinefields   Array of values for first line of example file
	 *  @param      string	$contentlinevalues	Array of values for content line of example file
	 *  @param		string	$datatoimport		Dataset to import
	 *  @return		string						Return integer <0 if KO, >0 if OK
	 */
	public function build_example_file($model, $headerlinefields, $contentlinevalues, $datatoimport)
	{
		// phpcs:enable
		global $conf, $langs;

		$indice = 0;

		dol_syslog(get_class($this)."::build_example_file ".$model);

		// Create the import class for the model Import_XXX
		$dir = DOL_DOCUMENT_ROOT."/core/modules/import/";
		$file = "import_".$model.".modules.php";
		$classname = "Import".$model;
		require_once $dir.$file;
		$objmodel = new $classname($this->db, $datatoimport);
		'@phan-var-force CommonObject $objmodel';

		$outputlangs = $langs; // Lang for output
		$s = '';

		// Genere en-tete
		$s .= $objmodel->write_header_example($outputlangs);

		// Genere ligne de titre
		$s .= $objmodel->write_title_example($outputlangs, $headerlinefields);

		// Genere ligne de titre
		$s .= $objmodel->write_record_example($outputlangs, $contentlinevalues);

		// Genere pied de page
		$s .= $objmodel->write_footer_example($outputlangs);

		return $s;
	}

	/**
	 *  Save an import model in database
	 *
	 *  @param		User	$user 	Object user that save
	 *  @return		int				Return integer <0 if KO, >0 if OK
	 */
	public function create($user)
	{
		global $conf;

		dol_syslog("Import.class.php::create");

		// Check parameters
		if (empty($this->model_name)) {
			$this->error = 'ErrorWrongParameters';
			return -1;
		}
		if (empty($this->datatoimport)) {
			$this->error = 'ErrorWrongParameters';
			return -1;
		}
		if (empty($this->hexa)) {
			$this->error = 'ErrorWrongParameters';
			return -1;
		}

		$this->db->begin();

		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'import_model (';
		$sql .= 'fk_user,';
		$sql .= ' label,';
		$sql .= ' type,';
		$sql .= ' field';
		$sql .= ')';
		$sql .= " VALUES (";
		$sql .= (isset($this->fk_user) ? (int) $this->fk_user : 'null').",";
		$sql .= " '".$this->db->escape($this->model_name)."',";
		$sql .= " '".$this->db->escape($this->datatoimport)."',";
		$sql .= " '".$this->db->escape($this->hexa)."'";
		$sql .= ")";

		$resql = $this->db->query($sql);
		if ($resql) {
			$this->db->commit();
			return 1;
		} else {
			$this->error = $this->db->lasterror();
			$this->errno = $this->db->lasterrno();
			$this->db->rollback();
			return -1;
		}
	}

	/**
	 *  Load an import profil from database
	 *
	 *  @param		int		$id		Id of profil to load
	 *  @return		int				Return integer <0 if KO, >0 if OK
	 */
	public function fetch($id)
	{
		$sql = 'SELECT em.rowid, em.field, em.label, em.type';
		$sql .= ' FROM '.MAIN_DB_PREFIX.'import_model as em';
		$sql .= ' WHERE em.rowid = '.((int) $id);

		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
		$result = $this->db->query($sql);
		if ($result) {
			$obj = $this->db->fetch_object($result);
			if ($obj) {
				$this->id                   = $obj->rowid;
				$this->hexa                 = $obj->field;
				$this->model_name           = $obj->label;
				$this->datatoimport         = $obj->type;
				$this->fk_user              = $obj->fk_user;
				return 1;
			} else {
				$this->error = "Model not found";
				return -2;
			}
		} else {
			dol_print_error($this->db);
			return -3;
		}
	}

	/**
	 *	Delete object in database
	 *
	 *	@param      User		$user        	User that delete
	 *  @param      int<0,1>	$notrigger	    0=launch triggers after, 1=disable triggers
	 *	@return		int						Return integer <0 if KO, >0 if OK
	 */
	public function delete($user, $notrigger = 0)
	{
		$error = 0;

		$sql = "DELETE FROM ".MAIN_DB_PREFIX."import_model";
		$sql .= " WHERE rowid=".((int) $this->id);

		$this->db->begin();

		dol_syslog(get_class($this)."::delete", LOG_DEBUG);
		$resql = $this->db->query($sql);
		if (!$resql) {
			$error++;
			$this->errors[] = "Error ".$this->db->lasterror();
		}

		/* Not used. This is not a business object. To convert it we must herit from CommonObject
		if (!$error) {
				// Call trigger
				$result=$this->call_trigger('IMPORT_DELETE',$user);
				if ($result < 0) $error++;
				// End call triggers
			}
		}
		*/

		// Commit or rollback
		if ($error) {
			foreach ($this->errors as $errmsg) {
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
			}
			$this->db->rollback();
			return -1 * $error;
		} else {
			$this->db->commit();
			return 1;
		}
	}
}
