<?php
function cmt_update_convert_version($version) {
	if (is_numeric($version)) {
		return "0.0." . $version;
   }
   return $version;
}

function cmt_update_delay($delay = 0) {
	$delay = (int)$delay;
	if ($delay > 0) usleep($delay * 300);
}

function cmt_update_current_version($module) {
	$module = preg_replace('/[^a-zA-Z0-9_\-]/', '', (string)$module);
	$file = ($module == 'core') ? 'version.txt' : 'modules/'.$module.'/version.txt';
	if (!is_file($file)) return null;
	$value = trim((string)file_get_contents($file));
	return $value !== '' ? $value : null;
}

function cmt_update_installed_modules() {
	$installed_modules = array();
	$installed_modules['core'] = cmt_update_current_version('core');
	if (is_dir('modules')){
		$dirs = array_diff(scandir('modules'), array('.', '..'));
		foreach ($dirs AS $dir){
			if (is_dir('modules/'.$dir) && is_file('modules/'.$dir.'/version.txt')){
				$installed_modules[$dir] = trim((string)file_get_contents('modules/'.$dir.'/version.txt'));
			}
		}
	}
	return $installed_modules;
}

function cmt_update_remote_token() {
	global $conn;
	$token = null;
	if (isset($conn) && check_table('settings_options')){
		$result = $conn->prepare("SELECT value FROM settings_options WHERE title = 'remote_update_token' LIMIT 1");
		$result->execute();
		if ($result->rowCount()){
			$arr = $result->fetch();
			$token = trim((string)$arr['value']);
		}
		if (!$token){
			$token = bin2hex(random_bytes(32));
			$sql = "INSERT INTO settings_options SET c_public = '0', title = 'remote_update_token', value = :value";
			$result = $conn->prepare($sql);
			$result->bindValue(':value', $token, PDO::PARAM_STR);
			$result->execute();
		}
	}
	return $token;
}

function cmt_update_report_installation() {
	$payload = array(
		'domain'  => preg_replace('/^www\./i', '', $_SERVER['SERVER_NAME']),
		'modules' => cmt_update_installed_modules(),
		'remote_update_token' => cmt_update_remote_token()
	);
	remote_call('https://admin.comator.org/report.php?data='.urlencode(json_encode($payload)));
}

function cmt_update_remote_response($channel = 'staging') {
	return remote_call('https://update.comator.org/update.php?domain='.$_SERVER['SERVER_NAME'].'&channel='.$channel);
}

function cmt_update_available_list($install_missing = false, $channel = 'staging') {
	$response = cmt_update_remote_response($channel);
	$list = array();
	if (!isset($response->module) || !$response->module) return $list;
	foreach ($response->module AS $key => $value){
		if ($key == 'setup') continue;
		$current = cmt_update_current_version($key);
		$target = isset($value->version) ? (string)$value->version : '';
		if ($current === null){
			if ($install_missing) $list[] = array('module' => $key, 'current' => 'nicht installiert', 'target' => $target, 'action' => 'install');
		} else if (version_compare(cmt_update_convert_version($target), cmt_update_convert_version($current), '>')){
			$list[] = array('module' => $key, 'current' => $current, 'target' => $target, 'action' => 'update');
		}
	}
	usort($list, function($a, $b){
		if ($a['module'] == 'core') return -1;
		if ($b['module'] == 'core') return 1;
		return strcmp($a['module'], $b['module']);
	});
	return $list;
}


function cmt_update_database($file, $dbname, $current_version = 0){
	global $conn;
	$db_data = array();
	$db_update = array();
	if (is_file($file)){
		include($file);
		if (isset($db_data['structure'])){
			foreach ($db_data['structure'] AS $key => $value){ 
				if (!check_table($key)) $conn->prepare($value)->execute();
			}
			if (isset($db_data['indizies'])){
				foreach ($db_data['indizies'] AS $key => $value){
					if (check_table($key)) {
						$sql_check = "SELECT * FROM information_schema.columns WHERE table_schema = :table_schema AND table_name = :table_name AND column_key = 'PRI';";
						$result = $conn->prepare($sql_check);
						$result->bindValue(':table_schema', $dbname, PDO::PARAM_STR);
						$result->bindValue(':table_name', $key, PDO::PARAM_STR);
						$result->execute();
						if (!$result->rowCount()) $conn->prepare($value)->execute();
					}
				}
			}
			if (isset($db_data['increment'])){
				foreach ($db_data['increment'] AS $key => $value){
					if (check_table($key)) $conn->prepare($value)->execute();
				}
			}
			if (isset($db_data['data'])){
				foreach ($db_data['data'] AS $key => $value){
					if (check_table($key)) $conn->prepare($value)->execute();
				}
			}
			unlink($file);
		}
		if (isset($db_update) && $db_update){
			if (isset($db_update['structure'])){
				foreach ($db_update['structure'] AS $version => $update){
					if (version_compare(convertToVersion($version), $current_version, '>')){
						foreach ($update AS $key => $value){
							if (!check_table($key))	$conn->prepare($value)->execute();
						}
					}
				}
			}
			if (isset($db_update['indizies'])){
				foreach ($db_update['indizies'] AS $version => $update){
					if (version_compare(convertToVersion($version), $current_version, '>')){
						foreach ($update AS $key => $value){
							if (check_table($key)) {
								$sql_check = "SELECT * FROM information_schema.columns WHERE table_schema = :table_schema AND table_name = :table_name AND column_key = 'PRI';";
								$result = $conn->prepare($sql_check);
								$result->bindValue(':table_schema', $dbname, PDO::PARAM_STR);
								$result->bindValue(':table_name', $key, PDO::PARAM_STR);
								$result->execute();
								if (!$result->rowCount())$conn->prepare($value)->execute();
							}
						}
					}
				}
			}
			if (isset($db_update['increment'])){
				foreach ($db_update['increment'] AS $version => $update){
					if (version_compare(convertToVersion($version), $current_version, '>')){
						foreach ($update AS $key => $value){
							if (check_table($key))	$conn->prepare($value)->execute();
						}
					}
				}
			}
			if (isset($db_update['data'])){
				foreach ($db_update['data'] AS $version => $update){
					if (version_compare(convertToVersion($version), $current_version, '>')){
						foreach ($update AS $key => $value){
							if (check_table($key))	$conn->prepare($value)->execute();
						}
					}
				}
			}
			unlink($file);
		}
	}
}


function cmt_update_execute($module, $action = 'update', $options = array()) {
	global $db;
	$module = preg_replace('/[^a-zA-Z0-9_\-]/', '', (string)$module);
	$action = ($action == 'install') ? 'install' : 'update';
	$delay = isset($options['delay']) ? (int)$options['delay'] : 0;
	$channel = isset($options['channel']) ? $options['channel'] : 'staging';
	$log = array();
	
	if (!$module) return array('success' => false, 'message' => 'Kein Modul angegeben.', 'log' => $log);
	
	$log[] = 'Update-Server abfragen ...';
	
	$response = remote_call('https://update.comator.org/update.php?domain='.$_SERVER['SERVER_NAME'].'&channel='.$channel.'&module='.$module);
	if (!isset($response->module) || !isset($response->module->$module)) {
		return array('success' => false, 'message' => 'Modul nicht im Update-Feed gefunden.', 'log' => $log);
	}
	
	$remote = $response->module->$module;
	$current_version = cmt_update_convert_version(cmt_update_current_version($module));
	if ($action == 'install') $current_version = cmt_update_convert_version(0);
	if (empty($remote->path)) {
		return array('success' => false, 'message' => 'Kein Download-Pfad im Update-Feed.', 'log' => $log);
	}
	
	$path = (string)$remote->path;
	$log[] = 'Download prüfen ...';
	cmt_update_delay($delay);
	
	$headers = @get_headers($path);
	if (!$headers || substr($headers[0], 9, 3) != '200') {
		return array('success' => false, 'message' => 'Download nicht erreichbar.', 'log' => $log);
	}
	
	if (!is_dir('module_manager')) mkdir('module_manager');
	$log[] = 'Download startet ...';
	cmt_update_delay($delay);
	$update_file = @file_get_contents($path);
	if ($update_file === false) {
		return array('success' => false, 'message' => 'Download fehlgeschlagen.', 'log' => $log);
	}
	
	$zip_path = 'module_manager/'.$module.'.zip';
	$handle = fopen($zip_path, 'w');
	if (!$handle || fwrite($handle, $update_file) === false) {
		return array('success' => false, 'message' => 'ZIP konnte nicht geschrieben werden.', 'log' => $log);
	}
	
	fclose($handle);
	$log[] = 'Download abgeschlossen ('.number_format(filesize($zip_path) / 1024, 1, ',', '.').' KB).';
	$log[] = 'Entpacken ...';
	cmt_update_delay($delay);
	$zip = new ZipArchive;
	if ($zip->open($zip_path) !== true) {
		return array('success' => false, 'message' => 'ZIP konnte nicht geöffnet werden.', 'log' => $log);
	}
	
	$zip->extractTo('../');
	$zip->close();
	unlink($zip_path);
	if (is_dir('module_manager')) @rmdir('module_manager');
	$log[] = 'Entpacken abgeschlossen.';
	
	if (is_dir('databases')){
		$log[] = 'Datenbank-Updates ausführen ...';
		cmt_update_delay($delay);
		$files = array_diff(scandir('databases'), array('.', '..'));
		foreach ($files AS $file) {
			if ($action == 'install' && str_ends_with($file, '_setup.php')) {
				cmt_update_database('databases/'.$file, $db['dbname'], $current_version);
			} else if (str_ends_with($file, '_update.php')) {
				cmt_update_database('databases/'.$file, $db['dbname'], $current_version);
		  } else if (is_file('databases/'.$file)) {
		  	unlink('databases/'.$file);
		  }
		}
		@rmdir('databases');
	}
	
	if (is_dir('deletes')){
		$log[] = 'Delete-Skripte prüfen ...';
		cmt_update_delay($delay);
		$files = array_diff(scandir('deletes'), array('.', '..'));
		foreach ($files AS $file) {
			if (is_file('deletes/'.$file)){
				$delete_file = array();
				include('deletes/'.$file);
				if (isset($delete_file) && $delete_file){
					foreach ($delete_file AS $version => $deletes){
						if ($version > $current_version OR $version == 0){ 
							foreach ($deletes AS $delete){ 
								if (is_file('../'.$delete)) unlink('../'.$delete);
							}
						}
					}
				}
				unlink('deletes/'.$file);
			}
		}
		@rmdir('deletes');
	}
	
	if (is_dir('scripts')){
		$log[] = 'Update-Skripte ausführen ...';
		cmt_update_delay($delay);
		$files = array_diff(scandir('scripts'), array('.', '..'));
		foreach ($files AS $file) { 
			if (is_file('scripts/'.$file)){ 
				include('scripts/'.$file); 
				unlink('scripts/'.$file);
			}
		}
		@rmdir('scripts');
	}
	
	$log[] = 'Installation melden ...';
	cmt_update_delay($delay);
	cmt_update_report_installation();
	return array('success' => true, 'message' => 'OK', 'module' => $module, 'log' => $log);
}


if (!function_exists('convertToVersion')) {
	function convertToVersion($version) { return cmt_update_convert_version($version); }
}
if (!function_exists('update_database')) {
	function update_database($file, $dbname, $current_version = 0){ return cmt_update_database($file, $dbname, $current_version); }
}
