<?php
/**
 * Esquema de base de datos de Entrepo.
 *
 * Núcleo de la arquitectura: la tabla `entrepo_precios` es la MATRIZ DE PRECIOS
 * PRECALCULADA. En vez de reglas dinámicas que se recalculan en cada request
 * (el patrón que hace que las tiendas grandes se arrastren a 8-16 s y que un
 * catálogo genere cientos de miles de reglas), aquí cada combinación
 * (producto, variación, rol) tiene su precio neto ya resuelto y persistido.
 * El runtime hace un único lookup indexado + memoización por request.
 *
 * @package Entrepo
 */

namespace Entrepo\Persistencia;

defined( 'ABSPATH' ) || exit;

/**
 * Esquema de base de datos de Entrepo: crea/migra la tabla `entrepo_precios`.
 *
 * Toda la lógica de `dbDelta()` vive aquí; `Activator::activate()` y
 * `Plugin::boot()` (rutina de upgrade) son los únicos que la invocan.
 */
final class Migrations {

	/** Se incrementa al cambiar el esquema para disparar dbDelta en upgrades. */
	public const SCHEMA_VERSION = '2';

	/**
	 * Nombre completo (con prefijo de tabla de WP) de la tabla de precios.
	 *
	 * @return string
	 */
	public static function precios_table_name(): string {
		global $wpdb;
		return $wpdb->prefix . 'entrepo_precios';
	}

	/**
	 * Crea o actualiza (vía `dbDelta()`) la tabla `entrepo_precios`.
	 */
	public static function run(): void {
		global $wpdb;

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		$tabla           = self::precios_table_name();
		$charset_collate = $wpdb->get_charset_collate();

		// Matriz de precios precalculada. variation_id = 0 significa "a nivel de
		// producto" (aplica a todas las variaciones sin precio propio). El precio
		// es el NETO final (ex-IVA) ya resuelto para ese rol: sin cálculo en runtime.
		// El orden del índice importa para el RENDIMIENTO, no solo para la unicidad.
		// La consulta caliente es `role = ? AND product_id IN (...)`. El índice
		// LIDERA con role (igualdad → clava 1/N del catálogo) y sigue con
		// product_id (range dentro): lee ~tamaño-del-carrito, no todas las filas
		// del rol. Un solo índice sirve: la consulta caliente, el borrado por rol
		// y la unicidad de (role, product_id, variation_id). NO se añade un índice
		// de `role` suelto: confundía al optimizador, que lo prefería y leía
		// rows≈nº-productos (bench F0), degradando con el catálogo.
		//
		// Índice secundario `product_id` (schema v2): `guardar_producto()` borra
		// por `product_id` solo, que NO es prefijo por la izquierda de
		// `role_prod_var` (lidera con role) — sin este índice, cada guardado de
		// producto haría un full table scan. Con él, el DELETE usa índice.
		$sql = "CREATE TABLE {$tabla} (
			id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
			product_id BIGINT UNSIGNED NOT NULL,
			variation_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
			role VARCHAR(60) NOT NULL,
			price DECIMAL(12,4) NOT NULL,
			updated_at DATETIME NOT NULL,
			PRIMARY KEY  (id),
			UNIQUE KEY role_prod_var (role, product_id, variation_id),
			KEY product_id (product_id)
		) {$charset_collate};";

		dbDelta( $sql );
	}
}
