<?php
/**
 * Activity log writer — records user actions for audit trail.
 *
 * @since 1.0.0
 */

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

// phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, PluginCheck.Security.DirectDB

class ZINV_Activity {

	private ZINV_Database $db;

	public function __construct() {
		$this->db = new ZINV_Database( 'zinv_activities' );
	}

	public static function log(
		string $action,
		string $object_type,
		int $object_id,
		string $description,
		array $metadata = []
	): int {
		$instance = new self();
		return $instance->write( $action, $object_type, $object_id, $description, $metadata );
	}

	public function write(
		string $action,
		string $object_type,
		int $object_id,
		string $description,
		array $metadata = []
	): int {
		$insert = [
			'user_id'      => get_current_user_id() ?: null,
			'action'       => sanitize_text_field( $action ),
			'object_type'  => sanitize_text_field( $object_type ),
			'object_id'    => $object_id,
			'description'  => sanitize_textarea_field( $description ),
			'ip_address'   => $this->get_client_ip(),
			'metadata'     => ! empty( $metadata ) ? wp_json_encode( map_deep( $metadata, 'sanitize_text_field' ) ) : null,
			'created_at'   => current_time( 'mysql' ),
		];

		return $this->db->insert( $insert );
	}

	public function get_recent( int $limit = 20 ): array {
		global $wpdb;
		$table = $this->db->get_table_name();

		return $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d",
			$limit
		) ) ?: [];
	}

	public function get_by_object( string $object_type, int $object_id, int $limit = 50 ): array {
		global $wpdb;
		$table = $this->db->get_table_name();

		return $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$table} WHERE object_type = %s AND object_id = %d ORDER BY created_at DESC LIMIT %d",
			$object_type,
			$object_id,
			$limit
		) ) ?: [];
	}

	public function archive_old( int $days = 90 ): int {
		global $wpdb;
		$activities_table = $this->db->get_table_name();
		$archive_table    = ( new ZINV_Database( 'zinv_activities_archive' ) )->get_table_name();

		$wpdb->query( 'START TRANSACTION' );

		// Explicit column list excluding id. Original "INSERT IGNORE ... SELECT *"
		// silently skipped EVERY row after the archive table acquired its own
		// auto-increment values (PK collision between activities.id and
		// activities_archive.id). The archive table gets its own ids.
		$wpdb->query( $wpdb->prepare(
			"INSERT INTO {$archive_table}
                (user_id, action, object_type, object_id, description, status, ip_address, metadata, created_at)
             SELECT user_id, action, object_type, object_id, description, status, ip_address, metadata, created_at
               FROM {$activities_table}
              WHERE created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d DAY)",
			$days
		) );

		if ( $wpdb->last_error ) {
			$wpdb->query( 'ROLLBACK' );
			return 0;
		}

		$rows = (int) $wpdb->rows_affected;

		$wpdb->query( $wpdb->prepare(
			"DELETE FROM {$activities_table} WHERE created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d DAY)",
			$days
		) );

		if ( $wpdb->last_error ) {
			$wpdb->query( 'ROLLBACK' );
			return 0;
		}

		$wpdb->query( 'COMMIT' );

		return $rows;
	}

	private function get_client_ip(): string {
		$remote = isset( $_SERVER['REMOTE_ADDR'] )
			? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) )
			: '';

		// Only honour X-Forwarded-For when the connecting IP is a declared trusted proxy.
		// Accept either ZINV_ (free) or ZINVP_ (Pro) constant so sites with either
		// or both plugins active get the same behavior.
		$trusted = [];
		if ( defined( 'ZINV_TRUSTED_PROXIES' ) ) {
			$trusted = array_merge( $trusted, (array) constant( 'ZINV_TRUSTED_PROXIES' ) );
		}
		if ( defined( 'ZINVP_TRUSTED_PROXIES' ) ) {
			$trusted = array_merge( $trusted, (array) constant( 'ZINVP_TRUSTED_PROXIES' ) );
		}
		if ( $remote && in_array( $remote, $trusted, true ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$xff   = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
			$parts = array_map( 'trim', explode( ',', $xff ) );
			$first = $parts[0] ?? '';
			if ( filter_var( $first, FILTER_VALIDATE_IP ) ) {
				return $first;
			}
		}

		return $remote ?: '';
	}
}
