<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class WpironLogging {

	const LOG_TYPE_LOGIN = 'login';
	const LOG_TYPE_FAILED_LOGIN = 'failed_login';
	const LOG_TYPE_LOCKOUT = 'lockout';
	const LOG_TYPE_2FA = 'two_factor_auth';
	const LOG_TYPE_SETTINGS = 'settings_change';
	const LOG_TYPE_FILE_CHANGE = 'file_change';
	const LOG_TYPE_USER_ENUM = 'user_enumeration';
	const LOG_TYPE_REST_API = 'rest_api';
	const LOG_TYPE_XML_RPC = 'xml_rpc';
	const LOG_TYPE_SECURITY = 'security';
	const LOG_TYPE_ADMIN_ACTION = 'admin_action';
	const LOG_TYPE_USER_CREATED = 'user_created';
	const LOG_TYPE_USER_MODIFIED = 'user_modified';
	const LOG_TYPE_PLUGIN_ACTION = 'plugin_action';
	const LOG_TYPE_MALICIOUS_REQUEST = 'malicious_request';
	const LOG_TYPE_ROLE_CHANGE = 'role_change';
	const LOG_TYPE_CLICKJACK_ATTEMPT = 'clickjack_attempt';
	const LOG_TYPE_API_REQUEST = 'api_request';
	const LOG_TYPE_SUSPICIOUS_BEHAVIOR = 'suspicious_behavior';
	const LOG_TYPE_HTTP_ERROR = 'http_error';
	const LOG_TYPE_CORE_UPDATE = 'core_update';
	const LOG_TYPE_THEME_ACTION = 'theme_action';
	const DB_TABLENAME = 'iron_security_logs';

	const STATUS_SUCCESS = 'success';
	const STATUS_FAILURE = 'failure';
	const STATUS_WARNING = 'warning';
	const STATUS_INFO = 'info';

	private WpironHelper $helper;

	public function __construct(WpironHelper $helper) {
		$this->helper = $helper;
	}
	public function getSecurityLogs( ) {
		$log_type   = isset( $_POST['log_type'] ) ? sanitize_text_field( $_POST['log_type'] ) : '';
		$status     = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
		$search     = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
		$ip_address = isset( $_POST['ip_address'] ) ? sanitize_text_field( $_POST['ip_address'] ) : '';
		$date_from  = isset( $_POST['date_from'] ) ? sanitize_text_field( $_POST['date_from'] ) : '';
		$date_to    = isset( $_POST['date_to'] ) ? sanitize_text_field( $_POST['date_to'] ) : '';
		$page       = isset( $_POST['page'] ) ? intval( $_POST['page'] ) : 1;
		$per_page   = isset( $_POST['per_page'] ) ? intval( $_POST['per_page'] ) : 50;

		$args = array(
			'log_type'   => $log_type,
			'status'     => $status,
			'search'     => $search,
			'ip_address' => $ip_address,
			'date_from'  => $date_from,
			'date_to'    => $date_to,
			'page'       => $page,
			'per_page'   => $per_page
		);

		$logs_data = $this->get_logs( $args );
		$summary = $this->get_logs_summary();

		wp_send_json_success( array(
			'logs'    => $logs_data['logs'],
			'total'   => $logs_data['total'],
			'pages'   => $logs_data['pages'],
			'summary' => $summary
		) );
	}

	public function get_logs($args = array()) {
		global $wpdb;

		$defaults = array(
			'per_page'   => 20,
			'page'       => 1,
			'log_type'   => '',
			'status'     => '',
			'search'     => '',
			'ip_address' => '',
			'user_id'    => '',
			'date_from'  => '',
			'date_to'    => '',
		);

		$args = wp_parse_args($args, $defaults);

		$per_page   = absint($args['per_page']);
		$page       = absint($args['page']);
		$log_type   = sanitize_text_field($args['log_type']);
		$status     = sanitize_text_field($args['status']);
		$search     = sanitize_text_field($args['search']);
		$ip_address = sanitize_text_field($args['ip_address']);
		$user_id    = absint($args['user_id']);
		$date_from  = sanitize_text_field($args['date_from']);
		$date_to    = sanitize_text_field($args['date_to']);

		// Calculate offset for pagination
		$offset = ($page - 1) * $per_page;

		// Build query conditions
		$where = [];
		$where_args = [];

		if (!empty($log_type)) {
			$where[] = 'log_type = %s';
			$where_args[] = $log_type;
		}

		if (!empty($status)) {
			$where[] = 'status = %s';
			$where_args[] = $status;
		}

		if (!empty($search)) {
			$where[] = '(message LIKE %s OR username LIKE %s)';
			$search_term = '%' . $wpdb->esc_like($search) . '%';
			$where_args[] = $search_term;
			$where_args[] = $search_term;
		}

		if (!empty($ip_address)) {
			$where[] = 'ip_address = %s';
			$where_args[] = $ip_address;
		}

		if (!empty($user_id)) {
			$where[] = 'user_id = %d';
			$where_args[] = $user_id;
		}

		if (!empty($date_from)) {
			$where[] = 'date_created >= %s';
			$where_args[] = $date_from . ' 00:00:00';
		}

		if (!empty($date_to)) {
			$where[] = 'date_created <= %s';
			$where_args[] = $date_to . ' 23:59:59';
		}

		$where_clause = '';
		if (!empty($where)) {
			$where_clause = 'WHERE ' . implode(' AND ', $where);
		}

		if (!empty($where_args)) {
			$count_query = $wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->prefix}iron_security_logs $where_clause",
				$where_args
			);
			$total = $wpdb->get_var($count_query);
			
			$prepared_query = $wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}iron_security_logs $where_clause ORDER BY date_created DESC LIMIT %d OFFSET %d",
				array_merge($where_args, [$per_page, $offset])
			);
			$logs = $wpdb->get_results($prepared_query);
		} else {
			$total = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->prefix}iron_security_logs $where_clause"));
			
			$prepared_query = $wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}iron_security_logs $where_clause ORDER BY date_created DESC LIMIT %d OFFSET %d",
				[$per_page, $offset]
			);
			$logs = $wpdb->get_results($prepared_query);
		}

		$formatted_logs = [];
		foreach ($logs as $log) {
			$extra_data = maybe_unserialize($log->extra_data);

			if (is_array($extra_data)) {
				foreach ($extra_data as $key => $value) {
					if (is_object($value)) {
						$extra_data[$key] = (array)$value;
					}

					if ($value instanceof \__PHP_Incomplete_Class) {
						$extra_data[$key] = 'Incomplete class data';
					}
				}

				$json_test = json_encode($extra_data);
				if ($json_test === false) {
					$extra_data = [
						'data_error' => 'The extra data contains elements that cannot be properly displayed.',
						'data_keys' => array_keys($extra_data)
					];
				}
			} elseif (is_object($extra_data)) {
				$extra_data = (array)$extra_data;
			} elseif (!empty($extra_data) && is_string($extra_data) && !is_serialized($extra_data)) {
				$json_data = json_decode($extra_data, true);
				if ($json_data !== null) {
					$extra_data = $json_data;
				}
			}

			$formatted_log = [
				'id'           => $log->id,
				'log_type'     => $log->log_type,
				'message'      => $log->message,
				'status'       => $log->status,
				'user_id'      => $log->user_id,
				'username'     => $log->username,
				'ip_address'   => $log->ip_address,
				'date_created' => $log->date_created,
				'extra_data'   => $extra_data
			];

			$formatted_logs[] = $formatted_log;
		}

		return [
			'logs'      => $formatted_logs,
			'total'     => absint($total),
			'page'      => $page,
			'per_page'  => $per_page,
			'pages'     => ceil($total / $per_page)
		];
	}

	private function get_logs_summary() {
		global $wpdb;
		$table_name = $wpdb->prefix . 'iron_security_logs';

		$total = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name" );

		$success_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'success'" );
		$failure_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'failure'" );
		$warning_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'warning'" );
		$info_count    = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE status = 'info'" );

		return array(
			'total'   => (int) $total,
			'success' => (int) $success_count,
			'failure' => (int) $failure_count,
			'warning' => (int) $warning_count,
			'info'    => (int) $info_count
		);
	}

	public function clearSecurityLogs($log_type) {

		$result = $this->clearLogs( $log_type );

		if ( $result ) {
			wp_send_json_success( array(
				'message' => empty( $log_type ) ? __( 'All logs cleared successfully',
					'iron-security' ) : sprintf( 'Logs of type "%s" cleared successfully',
					$log_type )
			) );
		} else {
			wp_send_json_error( __( 'Failed to clear logs', 'iron-security' ) );
		}

	}

	private function clearLogs($log_type = '') {
		global $wpdb;
		$table = $wpdb->prefix . 'iron_security_logs';

		if (empty($log_type)) {
			$result = $wpdb->query("TRUNCATE TABLE $table");
		} else {
			$result = $wpdb->delete($table, array('log_type' => $log_type), array('%s'));
		}

		return $result !== false;
	}

	public function add_log($log_type, $message, $status = 'info', $user_id = null, $username = null, $extra_data = array()) {

		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}

		global $wpdb;

		try {

			if ($user_id === null && function_exists('is_user_logged_in') && is_user_logged_in()) {
				if (function_exists('get_current_user_id')) {
					$user_id = get_current_user_id();
				}
			}

			if ($username === null && $user_id && function_exists('get_user_by')) {
				$user = get_user_by('id', $user_id);
				if ($user) {
					$username = $user->user_login;
				}
			}

			$ip_address = method_exists($this->helper, 'get_user_ip')
				? $this->helper->get_user_ip()
				: $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

			$serialized_data = null;
			if (!empty($extra_data)) {
				$serialized_data = function_exists('maybe_serialize')
					? maybe_serialize($extra_data)
					: serialize($extra_data);
			}

			$current_time = function_exists('current_time')
				? current_time('mysql')
				: gmdate('Y-m-d H:i:s');

			$data = array(
				'log_type'     => $log_type,
				'user_id'      => $user_id,
				'username'     => $username,
				'ip_address'   => $ip_address,
				'message'      => $message,
				'status'       => $status,
				'date_created' => $current_time,
				'extra_data'   => $serialized_data,
			);

			$formats = array('%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s');
			$result = $wpdb->insert($wpdb->prefix . self::DB_TABLENAME, $data, $formats);

			if ($result === false) {
				return false;
			}

			return $wpdb->insert_id;

		} catch (Exception $e) {
			return false;
		}
	}

	public  function log_successful_login($username, $user_id) {
		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}
		return self::add_log(
			self::LOG_TYPE_LOGIN,
			sprintf('User "%s" logged in successfully', $username),
			self::STATUS_SUCCESS,
			$user_id,
			$username,
			null,
		);
	}

	public function log_failed_login($username) {
		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}
		return self::add_log(
			self::LOG_TYPE_FAILED_LOGIN,
			sprintf('Failed login attempt for user "%s"', $username),
			self::STATUS_FAILURE,
			null,
			$username
		);
	}

	public function log_lockout($username, $attempts) {
		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}
		return self::add_log(
			self::LOG_TYPE_LOCKOUT,
			sprintf('User "%s" locked out after %d failed login attempts', $username, $attempts),
			self::STATUS_WARNING,
			null,
			$username,
			array('attempts' => $attempts)
		);
	}

	public static function log_2fa($username, $user_id, $success = true) {
		return self::add_log(
			self::LOG_TYPE_2FA,
			sprintf('2FA authentication %s for user "%s"',
				$success ? 'successful' : 'failed',
				$username
			),
			$success ? self::STATUS_SUCCESS : self::STATUS_FAILURE,
			$user_id,
			$username
		);
	}

	public static function log_settings_change($setting, $old_value, $new_value) {
		return self::add_log(
			self::LOG_TYPE_SETTINGS,
			sprintf('Setting "%s" changed', $setting),
			self::STATUS_INFO,
			null,
			null,
			array(
				'setting' => $setting,
				'old_value' => $old_value,
				'new_value' => $new_value
			)
		);
	}


	public static function get_log_types() {
		return array(
			self::LOG_TYPE_LOGIN => 'Successful Logins',
			self::LOG_TYPE_FAILED_LOGIN => 'Failed Logins',
			self::LOG_TYPE_LOCKOUT => 'Lockouts',
			self::LOG_TYPE_2FA => 'Two-Factor Authentication',
			self::LOG_TYPE_SETTINGS => 'Settings Changes',
			self::LOG_TYPE_FILE_CHANGE => 'File Changes',
			self::LOG_TYPE_USER_ENUM => 'User Enumeration Attempts',
			self::LOG_TYPE_REST_API => 'REST API Access',
			self::LOG_TYPE_XML_RPC => 'XML-RPC Access',
			self::LOG_TYPE_SECURITY => 'Security Events',
			self::LOG_TYPE_ADMIN_ACTION => 'Admin Actions',
			self::LOG_TYPE_USER_CREATED => 'User Creation',
			self::LOG_TYPE_USER_MODIFIED => 'User Modification',
			self::LOG_TYPE_PLUGIN_ACTION => 'Plugin Actions',
			self::LOG_TYPE_MALICIOUS_REQUEST => 'Malicious Requests',
			self::LOG_TYPE_ROLE_CHANGE => 'Role Changes',
			self::LOG_TYPE_CLICKJACK_ATTEMPT => 'Clickjack Attempts',
			self::LOG_TYPE_API_REQUEST => 'API Requests',
			self::LOG_TYPE_SUSPICIOUS_BEHAVIOR => 'Suspicious Behavior',
			self::LOG_TYPE_HTTP_ERROR => 'HTTP Errors',
			self::LOG_TYPE_CORE_UPDATE => 'Core Updates',
			self::LOG_TYPE_THEME_ACTION => 'Theme Actions'
		);
	}


	public static function get_status_types() {
		return array(
			self::STATUS_SUCCESS => 'Success',
			self::STATUS_FAILURE => 'Failure',
			self::STATUS_WARNING => 'Warning',
			self::STATUS_INFO => 'Information'
		);
	}

	public function log_user_created($user_id, $userdata = array()) {
		$user = get_userdata($user_id);
		$username = $user ? $user->user_login : 'Unknown';
		$creator_id = get_current_user_id();
		$creator = get_userdata($creator_id);
		$creator_name = $creator ? $creator->user_login : 'System';

		return self::add_log(
			self::LOG_TYPE_USER_CREATED,
			sprintf('New user "%s" created by %s', $username, $creator_name),
			self::STATUS_INFO,
			$creator_id,
			$creator_name,
			array(
				'new_user_id' => $user_id,
				'new_username' => $username,
				'user_role' => isset($userdata['role']) ? $userdata['role'] : 'Unknown',
				'user_email' => isset($userdata['user_email']) ? $userdata['user_email'] : 'Unknown'
			)
		);
	}

	public function log_role_change($user_id, $new_role, $old_roles = array()) {
		$user = get_userdata($user_id);
		$username = $user ? $user->user_login : 'Unknown';
		$changer_id = get_current_user_id();
		$changer = get_userdata($changer_id);
		$changer_name = $changer ? $changer->user_login : 'System';

		return self::add_log(
			self::LOG_TYPE_ROLE_CHANGE,
			sprintf('User "%s" role changed from "%s" to "%s" by %s',
				$username,
				implode(', ', $old_roles),
				$new_role,
				$changer_name
			),
			self::STATUS_INFO,
			$changer_id,
			$changer_name,
			array(
				'target_user_id' => $user_id,
				'target_username' => $username,
				'old_roles' => $old_roles,
				'new_role' => $new_role
			)
		);
	}

	public function log_plugin_action($action, $plugin) {
		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}
		$plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin);
		$plugin_name = isset($plugin_data['Name']) ? $plugin_data['Name'] : $plugin;

		return self::add_log(
			self::LOG_TYPE_PLUGIN_ACTION,
			sprintf('Plugin "%s" %s', $plugin_name, $action),
			self::STATUS_INFO,
			null,
			null,
			array(
				'plugin' => $plugin,
				'plugin_name' => $plugin_name,
				'action' => $action,
				'version' => isset($plugin_data['Version']) ? $plugin_data['Version'] : 'Unknown'
			)
		);
	}

	public function log_clickjack_attempt($details = '') {
		return self::add_log(
			self::LOG_TYPE_CLICKJACK_ATTEMPT,
			'Potential clickjacking attempt detected',
			self::STATUS_WARNING,
			null,
			null,
			array(
				'details' => $details,
				'http_referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
				'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
				'request_method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '',
				'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''
			)
		);
	}

	public function log_admin_action($action, $details = array()) {
		return self::add_log(
			self::LOG_TYPE_ADMIN_ACTION,
			sprintf('Admin action: %s', $action),
			self::STATUS_INFO,
			null,
			null,
			array_merge(array(
				'action' => $action,
				'http_referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
				'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''
			), $details)
		);
	}

	public function log_api_request($api, $endpoint, $method = 'GET') {
		return self::add_log(
			self::LOG_TYPE_API_REQUEST,
			sprintf('%s request to %s API endpoint: %s', $method, $api, $endpoint),
			self::STATUS_INFO,
			null,
			null,
			array(
				'api' => $api,
				'endpoint' => $endpoint,
				'method' => $method,
				'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
				'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''
			)
		);
	}

	public function log_core_update($old_version, $new_version) {
		if ( ! $this->helper->wpironis_table_exists() ) {
			return;
		}
		return self::add_log(
			self::LOG_TYPE_CORE_UPDATE,
			sprintf('WordPress core updated from %s to %s', $old_version, $new_version),
			self::STATUS_INFO,
			null,
			null,
			array(
				'old_version' => $old_version,
				'new_version' => $new_version
			)
		);
	}




}