<?php

namespace JetBackup\Wordpress;

use Exception;
use JetBackup\Alert\Alert;
use JetBackup\Encryption\Crypt;
use JetBackup\Entities\Util;
use JetBackup\Exception\IOException;
use JetBackup\Factory;
use JetBackup\JetBackup;
use WP_User;

if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');

class Helper {

	private string $_homedir='';

	private const SUPPORT_USER_TTL = 30 * 24 * 60 * 60; // 30 days in seconds

	const MYSQL_AUTH_PATTERNS = [
		'db_name'      => "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
		'db_user'      => "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
		'db_password'  => "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
		'db_host'      => "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
		'table_prefix' => "/^\s*\\\$table_prefix\s*=\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*;/",
	];

	public static function isMainSite(): bool {
		return (function_exists('is_main_site') && is_main_site());
	}

	public static function getMainSiteId(): int {
		$main_site_id = 1;
		if (function_exists('get_main_site_id')) return get_main_site_id();
		if (function_exists('get_site_option')) $main_site_id = (int) get_site_option('main_site_id');
		if ($main_site_id > 0) return $main_site_id;
		return $main_site_id;
	}

	public static function isMultisite(): bool {
		if (function_exists('is_multisite') && is_multisite()) return true;
		if (defined( 'MULTISITE')) return MULTISITE;
		if (defined( 'SUBDOMAIN_INSTALL') || defined( 'VHOST') || defined( 'SUNRISE')) return true;
		return false;
	}

	public static function isNetworkAdminUser(): bool {
		return function_exists('current_user_can') && current_user_can('manage_network') &&
		       function_exists('is_super_admin') && is_super_admin();
	}

	/**
	 * @throws Exception
	 */
	public static function getWordpressUser($username) : ?array {

		if(!function_exists('get_user_by')) throw new Exception("Function get_user_by() not defined");
		if(!function_exists('wp_set_password')) throw new Exception("Function wp_set_password() not defined");

		$user = get_user_by('login', $username);
		if(!$user) return null;


		$password = Util::generatePassword();
		wp_set_password($password, $user->ID);

		return [
			'username' => $user->user_login,
			'email' => $user->user_email,
			'password' => $password,
			'wordpress_admin_url' =>  Wordpress::getAdminURL(),

		];

	}

	/**
	 * @throws Exception
	 */
	public static function clearSupportUser() : void {

		$config = Factory::getConfig();
		if(!$username = $config->getSupportUsername()) return;
		if(!$user = get_user_by('login', $username)) return;
		$user_created_date = $user->data->user_registered ?? null;
		if(!$user_created_date) return;
		if(!$user_created_timestamp = strtotime($user_created_date)) return;

		if ((time() - $user_created_timestamp) > self::SUPPORT_USER_TTL) {

			if (self::isMultisite()) {
				if (!function_exists('wpmu_delete_user')) require_once WP_ROOT . JetBackup::SEP . 'wp-admin' . JetBackup::SEP . 'includes' . JetBackup::SEP . 'ms.php';
				revoke_super_admin($user->ID); // super admin user is protected by wp_delete user, so we need to revoke first
				wpmu_delete_user($user->ID);
			} else {
				if (!function_exists('wp_delete_user')) require_once WP_ROOT . JetBackup::SEP . 'wp-admin' . JetBackup::SEP . 'includes' . JetBackup::SEP . 'user.php';
				wp_delete_user($user->ID);
			}
			$config->setSupportUsername('');
			$config->save();
			Alert::add('System Cleanup', "Temporary support user '{$user->data->user_login}' created 30 days ago removed", Alert::LEVEL_INFORMATION);
		}

	}

	/**
	 * @throws IOException
	 * @throws Exception
	 */
	public static function createSupportUser() : array {

		$config = Factory::getConfig();
		$username = $config->getSupportUsername();
		if ($username && $output = self::getWordpressUser($username)) return $output;

		if(!function_exists('wp_create_user')) throw new Exception("Function wp_create_user() not defined");
		if(!function_exists('grant_super_admin')) throw new Exception("Function grant_super_admin() not defined");

		// Generate new user details
		$email = "support+" . Util::generateRandomString(5) . "@jetbackup.com";
		$password = Util::generatePassword();
		$username = "jetbackup_" . Util::generateRandomString(5);

		// Create new user
		$user_id = wp_create_user($username, $password, $email);
		if (is_wp_error($user_id)) throw new Exception($user_id->get_error_message());

		$user = new WP_User($user_id);
		$user->set_role('administrator');
		if (self::isMultisite()) grant_super_admin($user_id); // Grant network admin privileges

		$config->setSupportUsername($username);
		$config->save();

		return [
			'username' => $user->user_login,
			'email' => $email,
			'password' => $password,
			'wordpress_admin_url' =>  Wordpress::getAdminURL(),
		];

	}

	public static function isAdminUser(): bool {
		return function_exists('current_user_can') && current_user_can('manage_options') &&
		       function_exists('is_super_admin') && is_super_admin();
	}

	/**
	 * Returns true/false if I am in the network admin GUI interface (not if I am network admin USER)
	 * @return bool
	 */
	public static function isNetworkAdminInterface(): bool {
		if (function_exists('is_network_admin') && is_network_admin()) return true;
		if (isset($GLOBALS['current_screen'])) return $GLOBALS['current_screen']->in_admin( 'network' );
		elseif (defined('WP_NETWORK_ADMIN')) return WP_NETWORK_ADMIN;
		return false;
	}

	/**
	 * @param bool $public
	 * Example output: /home/user/public_html/
	 * public flag: public_html (no ending /)
	 * @return string
	 */
	public function getWordPressHomedir(bool $public = false): string {

		if (!$this->_homedir) {
			$homedir = defined('WP_ROOT') ? WP_ROOT : Wordpress::getAbsPath();
			$this->_homedir = rtrim($homedir, JetBackup::SEP) . JetBackup::SEP;
		}

		if ($public) return basename(rtrim($this->_homedir, JetBackup::SEP));
		return $this->_homedir;
	}

	/**
	 * @return string
	 * Returns public restore file location
	 * Example -
	 * Alternate path enabled: /home/user/public_html/wp-content/plugins/backup/public/cron
	 * Alternate path disabled: /home/user/public_html
	 */
	public function getRestoreFileLocation() : string {
		if (Factory::getSettingsRestore()->isRestoreAlternatePathEnabled()) return rtrim(JetBackup::CRON_PATH, JetBackup::SEP);
		return rtrim($this->getWordPressHomedir(), JetBackup::SEP);
	}

	/**
	 * @return string
	 * Return's WordPress public_dir relative to homedir, needed for nested sites (sites inside subfolders)
	 * Example
	 *  - getWordPressHomedir: /home/user/sites/www.mydomain.com/subfolder
	 *  - getUserHomedir: /home/user
	 *  - getWordPressRelativePublicDir: /sites/wp2.jetbackup.com/subfolder
	 */
	public function getWordPressRelativePublicDir() : string  {

		$public_dir = trim($this->getWordPressHomedir(), JetBackup::SEP);
		$getUserHomedir = trim($this->getUserHomedir(), JetBackup::SEP);
		$relative_path = $getUserHomedir;
		if ($public_dir != $getUserHomedir && str_starts_with($public_dir, $getUserHomedir)) {
			$relative_path = trim(substr($public_dir, strlen($getUserHomedir)), JetBackup::SEP);
		}

		return JetBackup::SEP . $relative_path;
	}

	/**
	 * @return string
	 * Example output: /home/user
	 */

	public static function getUserHomedir(): ?string {

		$user_details = Util::getpwuid(Util::geteuid());
		return $user_details['dir'] ?? null;

	}

	/**
	 * @param string|null $decryption_key Optional key to decrypt runtime credentials (queue unique ID)
	 * @throws Exception
	 */
	public static function parseWpConfig(?string $decryption_key = null): \stdClass {

		// Check if runtime credentials are available (from restore file)
		// This handles cloud environments (WordPress.com, WP Cloud, Porkbun) where
		// wp-config.php doesn't contain literal credentials
		if (defined('JB_RUNTIME_CREDENTIALS') && $decryption_key) {
			$decrypted = Crypt::decrypt(JB_RUNTIME_CREDENTIALS, $decryption_key);
			$creds = json_decode($decrypted);

			if ($creds && !empty($creds->db_name)) {
				$output = new \stdClass();
				$output->db_name = $creds->db_name;
				$output->db_user = $creds->db_user;
				$output->db_password = $creds->db_password;
				$output->db_host = $creds->db_host;
				$output->table_prefix = $creds->table_prefix ?? 'wp_';
				$output->db_port = Factory::getSettingsGeneral()->getMySQLDefaultPort();

				// Handle host:port notation
				if (strpos($output->db_host, ':') !== false) {
					list($output->db_host, $output->db_port) = explode(':', $output->db_host, 2);
				}

				return $output;
			}
		}

		// Fall back to parsing wp-config.php file
		$config_file = Factory::getSettingsGeneral()->getAlternateWpConfigLocation();
		if(!file_exists($config_file)) throw new Exception("The wp-config.php file does not exist at the specified path. ($config_file)");

		$output = new \stdClass();

		if(!($f = fopen($config_file, 'r'))) throw new Exception("Unable to open the wp-config.php file.");

		$patterns = self::MYSQL_AUTH_PATTERNS;

		while (($line = fgets($f)) !== false) {
			$line = trim($line);
			// Skip commented lines
			if (strpos($line, '//') === 0 || strpos($line, '#') === 0) continue;

			foreach ($patterns as $key => $pattern) {
				if (!preg_match($pattern, $line, $matches)) continue;
				$value = stripcslashes($matches[1]);

				if ($key === 'db_host' && strpos($value, ':') !== false) {
					list($output->db_host, $output->db_port) = explode(':', $value, 2);
				} else {
					$output->{$key} = $value;
				}

				unset($patterns[$key]);
				break; // Break the foreach loop if a pattern matches
			}

			// Break the while loop if all patterns are found
			if (empty($patterns)) break;
		}

		fclose($f);

		// Set default port if not found
		if (!isset($output->db_port)) $output->db_port = Factory::getSettingsGeneral()->getMySQLDefaultPort(); // Default MySQL port
        if (!isset($output->db_name)) $output->db_name = defined('DB_NAME') ? DB_NAME : '';
        if (!isset($output->db_user)) $output->db_user = defined('DB_USER') ? DB_USER : '';
        if (!isset($output->db_password)) $output->db_password = defined('DB_PASSWORD') ? DB_PASSWORD : '';
        if (!isset($output->db_host)) $output->db_host = defined('DB_HOST') ? DB_HOST : '';

		return $output;

	}

	public function getUploadDir(): string {
		$_uploads = Wordpress::getUploadDir()['basedir'] ?? 'uploads';
		return Wordpress::WP_CONTENT . JetBackup::SEP . basename($_uploads);
	}

	public static function validateEmail($email): bool {
		// Ensure $email is an array
		$emails = is_array($email) ? $email : [$email];

		foreach ($emails as $singleEmail) {
			$sanitizedEmail = Wordpress::sanitizeEmail($singleEmail);
			if (!Wordpress::isEmail($sanitizedEmail)) return false; // Invalid email found
		}

		return true;
	}

	static function getCurrentScreen():?string {
		if($screen = Wordpress::getCurrentScreen()) return $screen->id;
		return null;
	}

	public static function getUserId():?int {
		if($user = Wordpress::getCurrentUser()) return $user->ID;
		return null;
	}

	public static function getUserEmail():?string {
		if($user = Wordpress::getCurrentUser()) return $user->user_email;
		return null;
	}

	public static function getUserIP(): ?string {
		$ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? null;
		if ($ip === null) return null;
		//If forwarded IPs are present, take the first one
		if ( Wordpress::strContains( $ip, ',' ) ) {$ip = trim(explode(',', $ip)[0]);}
		$ip = Wordpress::sanitizeTextField($ip);
		if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
		return null;
	}



	public static function isWPCli(): bool {
		return (defined('WP_CLI') && WP_CLI);
	}


	public static function isCLI():bool {

		if (isset($_SERVER['HTTP_TE']) || isset($_SERVER['HTTP_COOKIE']) || isset($_SERVER['HTTP_ACCEPT'])) return false;
		if(self::isWPCli() || in_array( PHP_SAPI, ['cli', 'cli-server']) || defined('STDIN')) return true;
		return isset($_SERVER['argv']) && sizeof($_SERVER['argv']);

	}
}
