<?php
/**
 * Activity logging for Security Pack.
 *
 * @package ArkHost_Security_Pack
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Activity log class.
 */
class ARKSP_Activity_Log {

	/**
	 * Event types.
	 */
	const EVENT_LOGIN_SUCCESS  = 'login_success';
	const EVENT_LOGIN_FAILED   = 'login_failed';
	const EVENT_LOCKOUT        = 'lockout';
	const EVENT_IP_BLOCKED     = 'ip_blocked';
	const EVENT_GEO_BLOCKED    = 'geo_blocked';
	const EVENT_LOCKOUT_LIFTED = 'lockout_lifted';

	/**
	 * Constructor.
	 */
	public function __construct() {
		// Hooks are set up by Login Protection class.
	}

	/**
	 * Log an event.
	 *
	 * @param string      $event_type Event type.
	 * @param string|null $ip_address IP address (auto-detected if null).
	 * @param string|null $username   Username.
	 * @param string|null $details    Additional details.
	 * @return int|false Insert ID or false on failure.
	 */
	public static function log( $event_type, $ip_address = null, $username = null, $details = null ) {
		global $wpdb;

		if ( null === $ip_address ) {
			$ip_address = ARKSP_Helper::get_client_ip();
		}

		// Get user agent.
		$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] )
			? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 255 )
			: '';

		// Get country code if geo-blocking is available.
		$country_code = null;
		$geo_blocking = arksp()->get_component( 'geo_blocking' );
		if ( $geo_blocking && $ip_address ) {
			$country_code = $geo_blocking->get_country_code( $ip_address );
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$result = $wpdb->insert(
			$wpdb->prefix . 'arksp_activity_log',
			array(
				'event_type'   => $event_type,
				'ip_address'   => $ip_address ? $ip_address : '',
				'username'     => $username,
				'user_agent'   => $user_agent,
				'country_code' => $country_code,
				'details'      => $details,
				'created_at'   => current_time( 'mysql' ),
			),
			array( '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
		);

		return $result ? $wpdb->insert_id : false;
	}

	/**
	 * Get recent logs.
	 *
	 * @param array $args Query arguments.
	 * @return array
	 */
	public static function get_logs( $args = array() ) {
		global $wpdb;

		$defaults = array(
			'limit'      => 50,
			'offset'     => 0,
			'event_type' => '',
			'ip_address' => '',
		);

		$args   = wp_parse_args( $args, $defaults );
		$limit  = absint( $args['limit'] );
		$offset = absint( $args['offset'] );

		// Build query based on filters. Always order by created_at DESC for security logs.
		if ( ! empty( $args['event_type'] ) && ! empty( $args['ip_address'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return $wpdb->get_results(
				$wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s AND ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d",
					$args['event_type'],
					$args['ip_address'],
					$limit,
					$offset
				)
			);
		} elseif ( ! empty( $args['event_type'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return $wpdb->get_results(
				$wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s ORDER BY created_at DESC LIMIT %d OFFSET %d",
					$args['event_type'],
					$limit,
					$offset
				)
			);
		} elseif ( ! empty( $args['ip_address'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return $wpdb->get_results(
				$wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}arksp_activity_log WHERE ip_address = %s ORDER BY created_at DESC LIMIT %d OFFSET %d",
					$args['ip_address'],
					$limit,
					$offset
				)
			);
		}

		// No filters.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}arksp_activity_log ORDER BY created_at DESC LIMIT %d OFFSET %d",
				$limit,
				$offset
			)
		);
	}

	/**
	 * Get total log count.
	 *
	 * @param array $args Query arguments.
	 * @return int
	 */
	public static function get_log_count( $args = array() ) {
		global $wpdb;

		// Build query based on filters.
		if ( ! empty( $args['event_type'] ) && ! empty( $args['ip_address'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s AND ip_address = %s",
					$args['event_type'],
					$args['ip_address']
				)
			);
		} elseif ( ! empty( $args['event_type'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE event_type = %s",
					$args['event_type']
				)
			);
		} elseif ( ! empty( $args['ip_address'] ) ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
			return (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log WHERE ip_address = %s",
					$args['ip_address']
				)
			);
		}

		// No filters.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security data must be real-time.
		return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}arksp_activity_log" );
	}

	/**
	 * Get log statistics.
	 *
	 * @param int $days Number of days to look back.
	 * @return array
	 */
	public static function get_stats( $days = 30 ) {
		global $wpdb;

		$cutoff = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days} days" ) );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Security stats must be real-time.
		$results = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT event_type, COUNT(*) as count FROM {$wpdb->prefix}arksp_activity_log WHERE created_at >= %s GROUP BY event_type",
				$cutoff
			)
		);

		$stats = array(
			'login_success' => 0,
			'login_failed'  => 0,
			'lockout'       => 0,
			'ip_blocked'    => 0,
			'geo_blocked'   => 0,
			'total'         => 0,
		);

		foreach ( $results as $row ) {
			$stats[ $row->event_type ] = (int) $row->count;
			$stats['total']           += (int) $row->count;
		}

		return $stats;
	}

	/**
	 * Cleanup old logs (cron job).
	 */
	public static function cleanup_old_logs() {
		global $wpdb;

		$retention_days = ARKSP_Plugin::get_setting( 'log_retention_days', 30 );
		$cutoff         = gmdate( 'Y-m-d H:i:s', strtotime( "-{$retention_days} days" ) );

		// Delete old logs.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->prefix}arksp_activity_log WHERE created_at < %s",
				$cutoff
			)
		);

		// Delete expired lockouts (lockout_until stores Unix timestamp as integer).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup operation, caching not applicable.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->prefix}arksp_lockouts WHERE lockout_until IS NOT NULL AND lockout_until > 0 AND lockout_until < %d",
				time()
			)
		);
	}

	/**
	 * Clear all logs.
	 *
	 * @return bool
	 */
	public static function clear_all_logs() {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Truncate for admin action.
		return false !== $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}arksp_activity_log" );
	}

	/**
	 * Get event type label.
	 *
	 * @param string $event_type Event type.
	 * @return string
	 */
	public static function get_event_label( $event_type ) {
		$labels = array(
			self::EVENT_LOGIN_SUCCESS  => __( 'Login Success', 'arkhost-security-pack' ),
			self::EVENT_LOGIN_FAILED   => __( 'Login Failed', 'arkhost-security-pack' ),
			self::EVENT_LOCKOUT        => __( 'Lockout', 'arkhost-security-pack' ),
			self::EVENT_IP_BLOCKED     => __( 'IP Blocked', 'arkhost-security-pack' ),
			self::EVENT_GEO_BLOCKED    => __( 'Geo Blocked', 'arkhost-security-pack' ),
			self::EVENT_LOCKOUT_LIFTED => __( 'Lockout Lifted', 'arkhost-security-pack' ),
		);

		return isset( $labels[ $event_type ] ) ? $labels[ $event_type ] : $event_type;
	}
}
