<?php
/**
 * Configurador de IVA español en un clic: la feature que ningún competidor
 * da (la mayoría de tiendas WooCommerce tienen los impuestos SIN configurar
 * de fábrica — este plugin se los deja listos).
 *
 * @package Entrepo
 */

namespace Entrepo\Fiscal;

defined( 'ABSPATH' ) || exit;

/**
 * Activa el cálculo de impuestos y siembra los tipos españoles (IVA
 * 21/10/4/0% + recargo de equivalencia 21+5,2 / 10+1,4 / 4+0,5%).
 *
 * Idempotente por diseño: cada clase se siembra UNA sola vez (si ya tiene
 * filas para España, se deja intacta — nunca se duplica ni se actualiza un
 * tipo existente, porque un tendero pudo haberlo tocado a mano después de la
 * primera ejecución). Las clases de recargo (`CLASE_RECARGO_*`) son
 * constantes públicas: `FiscalHooks` (F2-6) las reutiliza para remapear la
 * clase de impuesto de un producto según si el cliente está en recargo, y
 * DEBEN coincidir con los slugs exactos que crea esta clase.
 */
final class ConfiguradorIva {

	/** Clase de impuesto para el recargo de equivalencia del tipo general (21% + 5,2%). */
	public const CLASE_RECARGO_GENERAL = 'recargo-general';

	/** Clase de impuesto para el recargo de equivalencia del tipo reducido (10% + 1,4%). */
	public const CLASE_RECARGO_REDUCIDO = 'recargo-reducido';

	/** Clase de impuesto para el recargo de equivalencia del tipo superreducido (4% + 0,5%). */
	public const CLASE_RECARGO_SUPERREDUCIDO = 'recargo-superreducido';

	/**
	 * Slug de la clase "tasa reducida" (10%). Ya existe en la tienda de
	 * referencia (creada a mano) — esta clase la asegura, no asume que la
	 * crea ella misma.
	 */
	public const CLASE_TASA_REDUCIDA = 'tasa-reducida';

	/** Slug de la clase "tasa cero" (0%). Mismo caso que `CLASE_TASA_REDUCIDA`. */
	public const CLASE_TASA_CERO = 'tasa-cero';

	/**
	 * Slug de la clase "tasa superreducida" (4%, IVA base — NO confundir con
	 * `CLASE_RECARGO_SUPERREDUCIDO`, que es el 4%+0,5% de recargo). Fix
	 * IMPORTANT del lote de revisión de rama: `FiscalHooks::clase_recargo_para()`
	 * ya remapeaba esta clase (con el literal `'tasa-superreducida'`) a
	 * `CLASE_RECARGO_SUPERREDUCIDO`, pero esta clase base NUNCA se creaba ni
	 * se sembraba aquí — un producto asignado a `tasa-superreducida` no tenía
	 * NINGÚN tipo de IVA sembrado (huérfana), así que se vendía al 0% en vez
	 * de al 4%: una pérdida silenciosa para la tienda. Ahora `configurar_espana()`
	 * la crea y siembra igual que `CLASE_TASA_REDUCIDA`/`CLASE_TASA_CERO`.
	 */
	public const CLASE_TASA_SUPERREDUCIDA = 'tasa-superreducida';

	/**
	 * Clave que representa el tipo ESTÁNDAR (21%) en `creadas`/`existentes`/
	 * `diagnostico()`. No es una clase real de `WC_Tax` (el estándar se
	 * guarda con `tax_rate_class = ''` en la tabla de tipos) — se le da un
	 * nombre explícito aquí para que el resumen no tenga que exponer la
	 * cadena vacía de WooCommerce a la UI.
	 */
	private const CLAVE_ESTANDAR = 'standard';

	/** País al que se siembran todos los tipos: el configurador es, deliberadamente, solo para España. */
	private const PAIS = 'ES';

	/**
	 * Activa el IVA español completo en un clic.
	 *
	 * Decisión de envío (shipping) documentada: el recargo de equivalencia es
	 * un régimen de MERCANCÍAS, no de servicios — el transporte sigue
	 * llevando el IVA normal aunque el producto vendido esté en recargo. Por
	 * eso, dentro de cada clase de recargo, la fila de IVA (prioridad 1)
	 * lleva `tax_rate_shipping = 1` y la fila de recargo (prioridad 2) lleva
	 * `tax_rate_shipping = 0`: al no ser compound, WooCommerce evalúa cada
	 * fila por separado, así que un pedido con productos en recargo cobra
	 * IVA sobre el envío pero nunca recargo sobre el envío. Las clases de
	 * IVA normal (estándar/reducida/cero) llevan `tax_rate_shipping = 1` en
	 * su única fila, el comportamiento estándar de WooCommerce.
	 *
	 * @return array{creadas:string[],existentes:string[],calc_taxes_activado:bool}
	 */
	public static function configurar_espana(): array {
		$calc_taxes_estaba_activo = 'yes' === get_option( 'woocommerce_calc_taxes' );
		if ( ! $calc_taxes_estaba_activo ) {
			update_option( 'woocommerce_calc_taxes', 'yes' );
		}

		self::asegurar_clase( self::CLASE_TASA_REDUCIDA, __( 'Reduced rate', 'entrepo' ) );
		self::asegurar_clase( self::CLASE_TASA_CERO, __( 'Zero rate', 'entrepo' ) );
		self::asegurar_clase( self::CLASE_TASA_SUPERREDUCIDA, __( 'Super-reduced rate', 'entrepo' ) );
		self::asegurar_clase( self::CLASE_RECARGO_GENERAL, __( 'Equivalence surcharge (general)', 'entrepo' ) );
		self::asegurar_clase( self::CLASE_RECARGO_REDUCIDO, __( 'Equivalence surcharge (reduced)', 'entrepo' ) );
		self::asegurar_clase( self::CLASE_RECARGO_SUPERREDUCIDO, __( 'Equivalence surcharge (super-reduced)', 'entrepo' ) );

		$creadas    = array();
		$existentes = array();

		self::sembrar_iva_simple( self::CLAVE_ESTANDAR, '', 21.0, __( 'VAT 21%', 'entrepo' ), $creadas, $existentes );
		self::sembrar_iva_simple( self::CLASE_TASA_REDUCIDA, self::CLASE_TASA_REDUCIDA, 10.0, __( 'VAT 10%', 'entrepo' ), $creadas, $existentes );
		self::sembrar_iva_simple( self::CLASE_TASA_CERO, self::CLASE_TASA_CERO, 0.0, __( 'VAT 0%', 'entrepo' ), $creadas, $existentes );
		self::sembrar_iva_simple( self::CLASE_TASA_SUPERREDUCIDA, self::CLASE_TASA_SUPERREDUCIDA, 4.0, __( 'VAT 4%', 'entrepo' ), $creadas, $existentes );

		self::sembrar_par_recargo( self::CLASE_RECARGO_GENERAL, 21.0, 5.2, __( 'VAT 21%', 'entrepo' ), __( 'Equiv. surcharge 5.2%', 'entrepo' ), $creadas, $existentes );
		self::sembrar_par_recargo( self::CLASE_RECARGO_REDUCIDO, 10.0, 1.4, __( 'VAT 10%', 'entrepo' ), __( 'Equiv. surcharge 1.4%', 'entrepo' ), $creadas, $existentes );
		self::sembrar_par_recargo( self::CLASE_RECARGO_SUPERREDUCIDO, 4.0, 0.5, __( 'VAT 4%', 'entrepo' ), __( 'Equiv. surcharge 0.5%', 'entrepo' ), $creadas, $existentes );

		return array(
			'creadas'             => $creadas,
			'existentes'          => $existentes,
			'calc_taxes_activado' => ! $calc_taxes_estaba_activo,
		);
	}

	/**
	 * Diagnóstico para pintar el estado en el panel (tarjeta «IVA español»):
	 * si el cálculo de impuestos está activo y cuántas filas de España tiene
	 * sembradas cada una de las 7 clases que gestiona el configurador.
	 *
	 * @return array{calc_taxes:bool,clases:array<string,int>}
	 */
	public static function diagnostico(): array {
		return array(
			'calc_taxes' => 'yes' === get_option( 'woocommerce_calc_taxes' ),
			'clases'     => array(
				self::CLAVE_ESTANDAR              => self::filas_es( '' ),
				self::CLASE_TASA_REDUCIDA         => self::filas_es( self::CLASE_TASA_REDUCIDA ),
				self::CLASE_TASA_CERO             => self::filas_es( self::CLASE_TASA_CERO ),
				self::CLASE_TASA_SUPERREDUCIDA    => self::filas_es( self::CLASE_TASA_SUPERREDUCIDA ),
				self::CLASE_RECARGO_GENERAL       => self::filas_es( self::CLASE_RECARGO_GENERAL ),
				self::CLASE_RECARGO_REDUCIDO      => self::filas_es( self::CLASE_RECARGO_REDUCIDO ),
				self::CLASE_RECARGO_SUPERREDUCIDO => self::filas_es( self::CLASE_RECARGO_SUPERREDUCIDO ),
			),
		);
	}

	/**
	 * Crea la clase de impuesto `$slug` si todavía no existe en la tienda.
	 * Idempotente vía `WC_Tax::get_tax_class_slugs()`: si el slug ya está
	 * registrado (p. ej. `tasa-reducida`/`tasa-cero`, creadas a mano en la
	 * tienda de referencia), no se toca — ni se re-crea ni se renombra.
	 *
	 * @param string $slug   Slug de la clase (debe coincidir con las const públicas).
	 * @param string $nombre Nombre legible de la clase para el admin de WooCommerce.
	 */
	private static function asegurar_clase( string $slug, string $nombre ): void {
		if ( in_array( $slug, \WC_Tax::get_tax_class_slugs(), true ) ) {
			return;
		}

		// Resultado (WP_Error si el nombre colisionara) deliberadamente
		// ignorado: el guard de arriba ya cubre la idempotencia por slug, y
		// un fallo de creación aquí solo significa que `filas_es()` seguirá
		// devolviendo 0 para esta clase (se refleja tal cual en el
		// diagnóstico, no hace falta un tratamiento de error aparte).
		\WC_Tax::create_tax_class( $nombre, $slug );
	}

	/**
	 * Siembra la única fila de una clase de IVA "simple" (estándar/reducida/
	 * cero) si esa clase todavía no tiene ninguna fila para España.
	 *
	 * @param string   $clave      Clave de la clase en `creadas`/`existentes` (`CLAVE_ESTANDAR` o el propio slug).
	 * @param string   $clase_bd   Valor de `tax_rate_class` en la tabla (`''` para el estándar).
	 * @param float    $tasa       Porcentaje del tipo.
	 * @param string   $etiqueta   Nombre del tipo tal como lo verá el admin/factura.
	 * @param string[] $creadas    Acumulador de clases sembradas en ESTA llamada (por referencia).
	 * @param string[] $existentes Acumulador de clases que YA tenían filas (por referencia).
	 */
	private static function sembrar_iva_simple( string $clave, string $clase_bd, float $tasa, string $etiqueta, array &$creadas, array &$existentes ): void {
		if ( self::filas_es( $clase_bd ) > 0 ) {
			$existentes[] = $clave;
			return;
		}

		self::insertar_fila( $clase_bd, $tasa, 1, $etiqueta, true );
		$creadas[] = $clave;
	}

	/**
	 * Siembra el par de filas (IVA + recargo, misma prioridad relativa 1/2,
	 * NO compound) de una clase de recargo de equivalencia, si esa clase
	 * todavía no tiene ninguna fila para España.
	 *
	 * @param string   $clase           Slug de la clase de recargo (una de las `CLASE_RECARGO_*`).
	 * @param float    $tasa_iva        Porcentaje del IVA correspondiente (21/10/4).
	 * @param float    $tasa_recargo    Porcentaje del recargo correspondiente (5,2/1,4/0,5).
	 * @param string   $etiqueta_iva    Nombre de la fila de IVA.
	 * @param string   $etiqueta_recargo Nombre de la fila de recargo.
	 * @param string[] $creadas         Acumulador de clases sembradas en ESTA llamada (por referencia).
	 * @param string[] $existentes      Acumulador de clases que YA tenían filas (por referencia).
	 */
	private static function sembrar_par_recargo( string $clase, float $tasa_iva, float $tasa_recargo, string $etiqueta_iva, string $etiqueta_recargo, array &$creadas, array &$existentes ): void {
		if ( self::filas_es( $clase ) > 0 ) {
			$existentes[] = $clase;
			return;
		}

		// Fila 1 (IVA): SÍ afecta al envío. Fila 2 (recargo): NO afecta al
		// envío. Ver el docblock de `configurar_espana()` para la
		// justificación legal de esta decisión.
		self::insertar_fila( $clase, $tasa_iva, 1, $etiqueta_iva, true );
		self::insertar_fila( $clase, $tasa_recargo, 2, $etiqueta_recargo, false );
		$creadas[] = $clase;
	}

	/**
	 * Inserta una fila en `wp_woocommerce_tax_rates` vía la API interna de
	 * `WC_Tax` (misma que usa el propio ajuste de impuestos del admin de
	 * WooCommerce). Nunca compound: el recargo de equivalencia se calcula
	 * sobre la base imponible, no sobre el IVA ya aplicado.
	 *
	 * @param string $clase     Valor de `tax_rate_class` (`''` para el estándar).
	 * @param float  $tasa      Porcentaje del tipo.
	 * @param int    $prioridad Prioridad de la fila (1 = IVA, 2 = recargo, dentro de la misma clase).
	 * @param string $nombre    Nombre del tipo (aparece en factura/admin).
	 * @param bool   $shipping  Si esta fila se aplica también al envío.
	 * @return int ID de la fila insertada.
	 */
	private static function insertar_fila( string $clase, float $tasa, int $prioridad, string $nombre, bool $shipping ): int {
		return \WC_Tax::_insert_tax_rate(
			array(
				'tax_rate_country'  => self::PAIS,
				'tax_rate_state'    => '',
				'tax_rate'          => $tasa,
				'tax_rate_name'     => $nombre,
				'tax_rate_priority' => $prioridad,
				'tax_rate_compound' => 0,
				'tax_rate_shipping' => $shipping ? 1 : 0,
				'tax_rate_order'    => $prioridad,
				'tax_rate_class'    => $clase,
			)
		);
	}

	/**
	 * Número de filas que ya tiene sembradas una clase de impuesto para
	 * España — la comprobación de idempotencia de todo el configurador.
	 *
	 * @param string $clase Valor de `tax_rate_class` (`''` para el estándar).
	 * @return int
	 */
	private static function filas_es( string $clase ): int {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- WC_Tax no expone un "contar filas de una clase+país"; lectura puntual de la tabla de WooCommerce, sin equivalente en su API pública.
		return (int) $wpdb->get_var(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- {$wpdb->prefix} no es entrada de usuario; clase/país ya van por %s en $wpdb->prepare().
				"SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s AND tax_rate_country = %s",
				$clase,
				self::PAIS
			)
		);
	}
}
