<?php

class MySQLAdapter {
	public $raw;

	function __construct() {
		$db_params = json_decode(file_get_contents('secrets/db.json'), true);

		try {
			if(false !== getenv('MYSQL_HOST')) {
				$this->raw = mysqli_connect(getenv('MYSQL_HOST'), 
					getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'), 
					getenv('MYSQL_DATABASE'),
					getenv('MYSQL_PORT'));
			}
			else {
				$this->raw = mysqli_connect($db_params['MYSQL_HOST'], 
					$db_params['MYSQL_USER'], $db_params['MYSQL_PASSWORD'], 
					$db_params['MYSQL_DATABASE'],
					$db_params['MYSQL_PORT']);
			}
		} catch (\Throwable $th) {
			echo 'Connection failed<br>';
			echo 'Error number: ' . mysqli_connect_errno() . '<br>';
			echo 'Error message: ' . mysqli_connect_error() . '<br>';
			die();

			//throw $th;
		}
	}

	function _sanitize_path($post_path) {
		$post_path = chop($post_path, '/');

		if($post_path == "") {
			return "";
		}

		if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) {
			echo "Post path match against " . $post_path . " failed!";
			die();
		}

		return $post_path;
	}

	function _exec($qery, $argtypes = '', ...$args) {
		$stmt = $this->raw->prepare($qery);
		if($argtypes != ""){
			$stmt->bind_param($argtypes, ...$args);
		}
		$stmt->execute();

		return $stmt->get_result();
	}

	function _normalize_post_data($post_data) {
		$post_data ??= ['found' => null];
    	
		if(isset($post_data['found']) && $post_data['found'] == false) {
			return $post_data;
		}

		$post_data["found"] = true;

		$post_data['post_metadata'] = json_decode($post_data["post_metadata"], true) ?? [];
		$post_data["post_content"] ??= '';

		return $post_data;
	}

	function bump_post($post_path, $post_metadata = [], $create_dirs = true) {
		$post_path = $this->_sanitize_path($post_path);
		$path_depth = substr_count($post_path, "/");

		if($create_dirs) {
			$this->make_post_directory(dirname($post_path));
		}

		$qry = "
		INSERT INTO posts
			(post_path, post_path_depth, post_metadata, post_content)
		VALUES
			( ?, ?, ?, ?) AS new
		ON DUPLICATE KEY UPDATE post_path=new.post_path;";

		$this->_exec($qry, "siss", 
			$post_path,
			$path_depth,
			json_encode($post_metadata),
			'');
	}

	function make_post_directory($directory) {
		$json_metadata = ["type" => 'directory'];
		
		while(strlen($directory) > 1) {
			try {
				$this->bump_post($directory, $json_metadata, false);
			}
			catch(Exception $e) {
			}

			$directory = dirname($directory);
		}
	}

	function log_post_access($post_path, $agent, $referrer, $time) {
		$post_path = $this->_sanitize_path($post_path);

		$qry = "INSERT INTO path_access_counts
			(access_time, 
				post_path, agent, referrer,
				path_access_count,
				path_processing_time)
		VALUES ( from_unixtime(floor(unix_timestamp(CURRENT_TIMESTAMP) / 300)*300),
			?, ?, ?, 1, ? 
		) AS new
		ON DUPLICATE KEY
			UPDATE path_access_count=path_access_counts.path_access_count+1,
				path_processing_time=path_access_counts.path_processing_time+new.path_processing_time;
		";

		$this->_exec($qry, "sssd", $post_path, $agent, $referrer, $time);

		if(preg_match('/^user/', $agent)) {
			$this->_exec("UPDATE posts SET post_access_count=post_access_count+1 WHERE post_path=?", "s", $post_path);
		}
	}

	function get_post_access_counters() {
		$qry = "
		SELECT post_path, agent, path_access_count, path_processing_time
		FROM path_access_counts
		WHERE path_last_access_time > ( CURRENT_TIMESTAMP  - INTERVAL 10 MINUTE );
		";

		$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);

		$out_data = [];

		foreach($data AS $post_data) {
			$path = $post_data['post_path'];

			$agent_data = ($out_data[$path] ?? []);
			
			$agent_data[$post_data['agent']] = [
				'count' => $post_data['path_access_count'],
				'time' => round($post_data['path_processing_time'], 6)
			];

			$out_data[$path] = $agent_data;
		}

		return $out_data;
	}

	function get_post_access_counters_line() {
		$qry = "
		SELECT access_time, post_path, agent, referrer, path_access_count, path_processing_time
		FROM path_access_counts
		WHERE access_time < ( CURRENT_TIMESTAMP  - INTERVAL 6 MINUTE )
		ORDER BY access_time DESC;
		";

		$this->raw->begin_transaction();
		$top_access_time = null;

		try {
			$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);

			$data_prefix="access_metrics,host=" . $_SERVER['SERVER_NAME'];

			$out_data = "";

			foreach($data AS $post_data) {
				$top_access_time ??= $post_data['access_time'];

				$path = $post_data['post_path'];
				if($path == '') {
					$path = '/';
				}
				$out_data .= $data_prefix . ",agent=".$post_data['agent'].",path=".$path.",referrer=".$post_data['referrer'];

				$out_data .= " access_sum=" . $post_data['path_access_count'] . ",time_sum=" . $post_data['path_processing_time'];
				$out_data .= " " . strtotime($post_data['access_time']) . "000000000\n";
			}

			
			$this->_exec("DELETE FROM path_access_counts WHERE access_time <= ?", "s", $top_access_time);
			
			$this->raw->commit();
			return $out_data;

		} catch (\Throwable $th) {
			$this->raw->rollback();
			
			throw $th;
		}
	}

	function reset_post_settings_cache($post_path) {
		$post_path = $this->_sanitize_path($post_path);

		$this->_exec("
			UPDATE posts
			SET post_settings_cache=NULL
			WHERE post_path LIKE ?;
		", "s", $post_path . "%");
	}

	function update_or_create_post($post_path, $post_metadata, $post_content) {
		$post_path = $this->_sanitize_path($post_path);
		$path_depth = substr_count($post_path, "/");
		
		$this->make_post_directory(dirname($post_path));

		$this->reset_post_settings_cache($post_path);

		$qry = "
		INSERT INTO posts
			(post_path, post_path_depth, post_metadata, post_content)
		VALUES
			( ?, ?, ?, ?) AS new
		ON DUPLICATE KEY 
			UPDATE  post_metadata=new.post_metadata, 
					post_content=new.post_content,
					post_update_time=CURRENT_TIMESTAMP;";

		$this->_exec($qry, "siss", 
			$post_path,
			$path_depth,
			json_encode($post_metadata),
			$post_content);
	}

	function get_settings_for_path($post_path) {
		$post_path = $this->_sanitize_path($post_path);

		$post_settings = $this->_exec("
			SELECT post_path, post_settings_cache
			FROM posts
			WHERE post_path = ?
		", "s", $post_path)->fetch_assoc();

		if(!isset($post_settings)) {
			return [];
		}
		if(isset($post_settings['post_settings_cache'])) {
			return json_decode($post_settings['post_settings_cache'], true);
		}

		$parent_settings = [];
		if($post_path != "") {
			$parent_settings = $this->get_settings_for_path(dirname($post_path));
		}

		$post_settings = [];
		$post_metadata = $this->_exec("
			SELECT post_path, post_metadata
			FROM posts
			WHERE post_path = ?
		", "s", $post_path)->fetch_assoc();

		if(isset($post_metadata['post_metadata'])) {
			$post_metadata = json_decode($post_metadata['post_metadata'], true);

			if(isset($post_metadata['settings'])) {
				$post_settings = $post_metadata['settings'];
			}
		}

		$post_settings = array_merge($parent_settings, $post_settings);

		$this->_exec("UPDATE posts SET post_settings_cache=? WHERE post_path=?", "ss",
			json_encode($post_settings), $post_path);

		return $post_settings;
	}
	
	function get_post_by_path($post_path, 
			$with_subposts = false, $with_settings = true) {
		
		$qry = "SELECT * 
		FROM posts WHERE post_path = ?
		";

		$post_path = $this->_sanitize_path($post_path);

		$post_data = $this->_exec($qry, "s", $post_path)->fetch_assoc();
				
		if(!isset($post_data)) {
			$post_data = ['found' => false];
		}
		$post_data['post_path'] = $post_path;
		
		$post_data = $this->_normalize_post_data($post_data);
		
		if(!$post_data['found']) {
			return $post_data;
		}

		if($with_subposts) {
			$post_data['subposts'] = $this->get_subposts_by_path($post_path);
		}
		if($with_settings) {
			$post_data['settings'] = $this->get_settings_for_path($post_path);
		}

		return $post_data;
	}

	function get_subposts_by_path($path) {
	    global $sql;
	
	    $path = $this->_sanitize_path($path);
	
	    $path_depth = substr_count($path, "/");
	
	    $qry = "SELECT post_path, post_metadata, post_update_time
	        FROM posts 
	        WHERE (post_path LIKE CONCAT(?,'/%')) 
	            AND post_path_depth = ?
	            ORDER BY post_create_time DESC
	            LIMIT 10";

	   $post_data = $this->_exec($qry, "si", $path, $path_depth+1)->fetch_all(MYSQLI_ASSOC);

		$fn = function($data) {
			return $this->_normalize_post_data($data);
		};
		
	    $post_data = array_map($fn, $post_data);
	
	    return $post_data; 
	}
}

?>
