<?php

declare(strict_types=1);

namespace Australcode\Rewards\Infrastructure\Wallet;

use Australcode\Rewards\Domain\Wallet\Transaction;
use Australcode\Rewards\Domain\Wallet\TransactionRepository;
use Australcode\Rewards\Domain\Wallet\TransactionStatus;
use Australcode\Rewards\Infrastructure\Database\Schema;

defined( 'ABSPATH' ) || exit;

/**
 * wp_acrwd_transactions-backed, append-only ledger repository.
 */
final class CustomTableTransactionRepository implements TransactionRepository {

	public function insert( Transaction $transaction ): int {
		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_TRANSACTIONS );

		$row               = $transaction->to_row();
		$row['created_at'] = current_time( 'mysql', true );

		$wpdb->insert(
			$table,
			$row,
			array( '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%s' )
		);

		return (int) $wpdb->insert_id;
	}

	/**
	 * @return Transaction[]
	 */
	public function for_user( int $user_id, int $limit = 20, int $offset = 0 ): array {
		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_TRANSACTIONS );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from internal Schema constant.
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM `{$table}` WHERE user_id = %d ORDER BY id DESC LIMIT %d OFFSET %d",
				$user_id,
				$limit,
				$offset
			),
			ARRAY_A
		);

		return $this->map_rows( $rows );
	}

	public function count_for_user( int $user_id ): int {
		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_TRANSACTIONS );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from internal Schema constant.
		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$table}` WHERE user_id = %d", $user_id ) );
	}

	/**
	 * @return Transaction[]
	 */
	public function active_for_order( int $order_id ): array {
		global $wpdb;
		$table = Schema::table_name( Schema::TABLE_TRANSACTIONS );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from internal Schema constant.
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM `{$table}` WHERE order_id = %d AND status = %s",
				$order_id,
				TransactionStatus::Active->value
			),
			ARRAY_A
		);

		return $this->map_rows( $rows );
	}

	/**
	 * @param int[] $ids
	 */
	public function mark_reverted( array $ids ): void {
		if ( array() === $ids ) {
			return;
		}
		global $wpdb;
		$table        = Schema::table_name( Schema::TABLE_TRANSACTIONS );
		$ids          = array_values( array_map( 'intval', $ids ) );
		$placeholders = implode( ', ', array_fill( 0, count( $ids ), '%d' ) );

		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from Schema constant; ids cast to int and bound.
		$wpdb->query(
			$wpdb->prepare(
				"UPDATE `{$table}` SET status = %s WHERE id IN ({$placeholders})",
				TransactionStatus::Reverted->value,
				...$ids
			)
		);
	}

	/**
	 * @param mixed $rows
	 * @return Transaction[]
	 */
	private function map_rows( $rows ): array {
		if ( ! is_array( $rows ) ) {
			return array();
		}
		return array_map(
			static fn ( array $row ): Transaction => Transaction::from_row( $row ),
			$rows
		);
	}
}
