<?php

declare(strict_types=1);

namespace Australcode\Rewards\Service\Import;

use Australcode\Rewards\Domain\Wallet\Balance;
use Australcode\Rewards\Domain\Wallet\BalanceRepository;
use Australcode\Rewards\Domain\Wallet\Transaction;
use Australcode\Rewards\Domain\Wallet\TransactionRepository;
use Australcode\Rewards\Domain\Wallet\TransactionType;

defined( 'ABSPATH' ) || exit;

/**
 * Migrates point balances from YITH WooCommerce Points and Rewards.
 *
 * Balance-only, snapshot import: YITH's per-user total
 * (`_ywpar_user_total_points`) is authoritative — already net of
 * earnings, redemptions, expirations and cancellations — so we trust it
 * rather than replaying YITH's 54k-row, 65%-cancelled ledger (ambiguous
 * semantics, high double-count risk). For each user we write the
 * denormalized balance (with YITH's lifetime totals) plus a single opening
 * ledger entry so the import is visible and auditable in the history.
 *
 * Idempotent: each migrated user is flagged with `_acrwd_yith_imported`, so
 * re-running only picks up users imported after the previous pass.
 */
final class YithImporter {

	/** YITH usermeta holding the current spendable balance (authoritative). */
	private const META_BALANCE = '_ywpar_user_total_points';

	/** YITH usermeta holding gross lifetime points collected. */
	private const META_COLLECTED = '_ywpar_points_collected';

	/** YITH usermeta holding gross lifetime points used (redeemed). */
	private const META_USED = '_ywpar_used_points';

	/** Our marker so the import is idempotent across runs. */
	private const META_IMPORTED = '_acrwd_yith_imported';

	private const OPENING_REASON = 'Imported from YITH Points and Rewards';

	public function __construct(
		private readonly BalanceRepository $balances,
		private readonly TransactionRepository $transactions,
	) {
	}

	/**
	 * Runs (or simulates, when $dry_run) the import and returns a summary.
	 */
	public function run( bool $dry_run = false ): ImportReport {
		$candidates       = 0;
		$already_imported = 0;
		$eligible         = 0;
		$points           = 0;
		$imported         = 0;

		foreach ( $this->candidate_user_ids() as $user_id ) {
			++$candidates;

			if ( '' !== (string) get_user_meta( $user_id, self::META_IMPORTED, true ) ) {
				++$already_imported;
				continue;
			}

			$balance   = (int) get_user_meta( $user_id, self::META_BALANCE, true );
			$collected = (int) get_user_meta( $user_id, self::META_COLLECTED, true );
			$used      = (int) get_user_meta( $user_id, self::META_USED, true );

			// Nothing worth migrating for this user.
			if ( 0 === $balance && 0 === $collected && 0 === $used ) {
				continue;
			}

			++$eligible;
			$points += $balance;

			if ( $dry_run ) {
				continue;
			}

			$this->import_user( $user_id, $balance, $collected, $used );
			++$imported;
		}

		return new ImportReport( $candidates, $already_imported, $eligible, $points, $imported );
	}

	/**
	 * Writes one user's balance + opening ledger entry and flags it imported.
	 */
	private function import_user( int $user_id, int $balance, int $collected, int $used ): void {
		$this->balances->save(
			new Balance(
				user_id: $user_id,
				points_balance: $balance,
				lifetime_earned: $collected,
				lifetime_redeemed: $used,
			)
		);

		$this->transactions->insert(
			new Transaction(
				id: null,
				user_id: $user_id,
				type: TransactionType::Adjust,
				points_delta: $balance,
				balance_after: $balance,
				reason: self::OPENING_REASON,
			)
		);

		update_user_meta( $user_id, self::META_IMPORTED, current_time( 'mysql', true ) );
	}

	/**
	 * Source users: everyone with a YITH balance record.
	 *
	 * @return int[]
	 */
	private function candidate_user_ids(): array {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- one-shot CLI migration over the usermeta table.
		$ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s ORDER BY user_id ASC",
				self::META_BALANCE
			)
		);

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