<?php

declare(strict_types=1);

namespace Australcode\Rewards\Infrastructure\Wallet;

use Australcode\Rewards\Domain\Wallet\CustomerBlock;
use Australcode\Rewards\Infrastructure\Database\Schema;

defined( 'ABSPATH' ) || exit;

/**
 * wp_acrwd_balances-backed implementation of the customer block flag (B6).
 *
 * Writes is_blocked in isolation (an upsert that only touches user_id +
 * is_blocked), so it never collides with WalletService's balance upsert, which
 * lists a different set of columns and therefore leaves is_blocked untouched on
 * its UPDATE branch.
 */
final class CustomTableCustomerBlock implements CustomerBlock {

	public function is_blocked( int $user_id ): bool {
		if ( $user_id <= 0 ) {
			return false;
		}

		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_BALANCES );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- table name from internal Schema constant; value bound.
		$flag = $wpdb->get_var( $wpdb->prepare( "SELECT is_blocked FROM `{$table}` WHERE user_id = %d", $user_id ) );

		return null !== $flag && 1 === (int) $flag;
	}

	public function set_blocked( int $user_id, bool $blocked ): void {
		if ( $user_id <= 0 ) {
			return;
		}

		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_BALANCES );
		$value = $blocked ? 1 : 0;
		$now   = current_time( 'mysql', true );

		// Upsert so a customer can be blocked before they have a balance row. The
		// INSERT branch leans on the column defaults for the points/lifetime/tier
		// columns; the UPDATE branch touches only is_blocked, leaving the live
		// balance and its updated_at alone.
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- table name from internal Schema constant; values bound.
		$wpdb->query(
			$wpdb->prepare(
				"INSERT INTO `{$table}` (user_id, is_blocked, updated_at) VALUES (%d, %d, %s)
				ON DUPLICATE KEY UPDATE is_blocked = %d",
				$user_id,
				$value,
				$now,
				$value
			)
		);
	}

	public function set_blocked_many( array $user_ids, bool $blocked ): int {
		$count = 0;
		foreach ( $user_ids as $user_id ) {
			$id = (int) $user_id;
			if ( $id <= 0 ) {
				continue;
			}
			$this->set_blocked( $id, $blocked );
			++$count;
		}
		return $count;
	}

	public function count_blocked(): int {
		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_BALANCES );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- table name from internal Schema constant; no user input.
		return (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}` WHERE is_blocked = 1" );
	}

	public function blocked_among( array $user_ids ): array {
		$ids = array_values(
			array_filter(
				array_map( 'intval', $user_ids ),
				static fn ( int $id ): bool => $id > 0
			)
		);
		if ( array() === $ids ) {
			return array();
		}

		global $wpdb;
		$table        = Schema::table_name( Schema::TABLE_BALANCES );
		$placeholders = implode( ', ', array_fill( 0, count( $ids ), '%d' ) );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- table name from Schema constant; the IN-list is %d placeholders bound below.
		$rows = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT user_id FROM `{$table}` WHERE is_blocked = %d AND user_id IN ({$placeholders})",
				1,
				...$ids
			)
		);

		return array_map( 'intval', (array) $rows );
	}
}
