<?php
/**
 * Snapshots a job's affected products/variations before their prices are
 * overwritten by BatchApplier (T-305).
 *
 * @package EmberPrice
 */

namespace EmberPrice\Apply;

defined( 'ABSPATH' ) || exit;

use EmberPrice\Core\Rows;
use EmberPrice\Core\Snapshots;

/**
 * SPEC §6 step 1: "Confirmación → snapshot de `_regular_price` y
 * `_sale_price` de cada producto/variación afectado". Reads those two meta
 * keys directly with `get_post_meta()` rather than instantiating a
 * `WC_Product` (see `WooCommerceCatalogRepository::gtin_for()` for the same
 * pattern) -- the snapshot only needs the raw values BatchApplier is about
 * to replace, not any WooCommerce business logic.
 */
final class Snapshotter {

	/**
	 * Row storage.
	 *
	 * @var Rows
	 */
	private Rows $rows;

	/**
	 * Snapshot storage.
	 *
	 * @var Snapshots
	 */
	private Snapshots $snapshots;

	/**
	 * Constructor.
	 *
	 * @param Rows      $rows      Row storage.
	 * @param Snapshots $snapshots Snapshot storage.
	 */
	public function __construct( Rows $rows, Snapshots $snapshots ) {
		$this->rows      = $rows;
		$this->snapshots = $snapshots;
	}

	/**
	 * Snapshots every row about to be applied (the same `auto`/`confirmed`
	 * set priced by `JobsController::preview_job()` and exported by
	 * `export_job()`) -- one row per matched product/variation, in a single
	 * pass over the job.
	 *
	 * @param int $job_id Job id.
	 */
	public function snapshot_job( int $job_id ): void {
		$snapshotted = array();

		foreach ( $this->rows->get_by_job_and_statuses( $job_id, array( 'auto', 'confirmed' ) ) as $row ) {
			if ( null === $row['match_product_id'] ) {
				continue;
			}

			$priced_id = $row['match_variation_id'] ?? $row['match_product_id'];

			if ( isset( $snapshotted[ $priced_id ] ) ) {
				continue;
			}

			$snapshotted[ $priced_id ] = true;

			$this->snapshots->create(
				$job_id,
				(int) $row['match_product_id'],
				$row['match_variation_id'],
				$this->read_price( $priced_id, '_regular_price' ),
				$this->read_price( $priced_id, '_sale_price' )
			);
		}
	}

	/**
	 * Reads a price meta value, or null if it's unset/empty.
	 *
	 * @param int    $post_id  Product or variation post id.
	 * @param string $meta_key `_regular_price` or `_sale_price`.
	 */
	private function read_price( int $post_id, string $meta_key ): ?float {
		$value = get_post_meta( $post_id, $meta_key, true );

		return is_numeric( $value ) ? (float) $value : null;
	}
}
