<?php
/**
 * Generación de código de barras EAN-13 para etiquetas de producción.
 *
 * @package Logistica
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Estructura: 770 + mercado (1) + tipo producto (2) + código producto (6) + dígito verificación.
 */
class Logistica_Label_Barcode {

	public const COUNTRY_PREFIX = '770';

	/**
	 * @return array<string, string>
	 */
	public static function base_product_type_options() {
		return array(
			'NO FABRICADO' => __( 'No fabricado', 'logistica' ),
			'VASO'         => __( 'Vaso', 'logistica' ),
			'TAPA'         => __( 'Tapa', 'logistica' ),
			'TAZON'        => __( 'Tazón', 'logistica' ),
			'POCKET'       => __( 'Pocket', 'logistica' ),
			'CHAROL'       => __( 'Charol', 'logistica' ),
			'ACETATO'      => __( 'Acetato', 'logistica' ),
			'PLATO'        => __( 'Plato', 'logistica' ),
			'BOLSA'        => __( 'Bolsa', 'logistica' ),
			'CAJA'         => __( 'Caja', 'logistica' ),
			'ANTIGRASO'    => __( 'Antigraso', 'logistica' ),
		);
	}

	/**
	 * @return array<string, string>
	 */
	public static function product_type_options() {
		return Logistica_Label_Etiqueta_Tipos::get_option_labels();
	}

	/**
	 * @return array<string, string>
	 */
	public static function market_options() {
		return Logistica_Label_Etiqueta_Mercados::get_option_labels();
	}

	/**
	 * @return list<array{key:string,label:string,market_digit:string,built_in:bool,can_delete:bool}>
	 */
	public static function get_market_admin_entries() {
		return Logistica_Label_Etiqueta_Mercados::get_admin_entries();
	}

	/**
	 * @param mixed $value Tipo desde formulario o cierre.
	 * @return string
	 */
	public static function normalize_product_type( $value ) {
		$text    = remove_accents( strtoupper( trim( (string) $value ) ) );
		$text    = preg_replace( '/\s+/', ' ', $text );
		$options = self::product_type_options();

		if ( isset( $options[ $text ] ) ) {
			return $text;
		}

		foreach ( $options as $key => $label ) {
			if ( 0 === strcasecmp( (string) $label, $text ) ) {
				return (string) $key;
			}
		}

		$aliases = array(
			'VASO'         => 'VASO',
			'TAPA'         => 'TAPA',
			'TAZON'        => 'TAZON',
			'TAZON/BOWL'   => 'TAZON',
			'BOWL'         => 'TAZON',
			'TAZÓN'        => 'TAZON',
			'POCKET'       => 'POCKET',
			'CHAROL'       => 'CHAROL',
			'ACETATO'      => 'ACETATO',
			'PLATO'        => 'PLATO',
			'BOLSA'        => 'BOLSA',
			'CAJA'         => 'CAJA',
			'CAJAS'        => 'CAJA',
			'ANTIGRASO'    => 'ANTIGRASO',
			'NO FABRICADO' => 'NO FABRICADO',
			'COLLARIN'     => 'TAPA',
			'COLLARÍN'     => 'TAPA',
			'OTRO'         => '',
		);

		if ( isset( $aliases[ $text ] ) ) {
			return $aliases[ $text ];
		}

		return '';
	}

	/**
	 * @param mixed $value Variante de referencia (Exportación, Nacional, etc.).
	 * @return string EXP|NAC|''
	 */
	public static function normalize_market( $value ) {
		return Logistica_Label_Etiqueta_Mercados::normalize_market( $value );
	}

	/**
	 * Primer dígito del tipo (0 salvo caja = 1).
	 *
	 * @param string $tipo Tipo normalizado.
	 * @return int|null
	 */
	public static function type_prefix_digit( $tipo ) {
		$tipo = self::normalize_product_type( $tipo );
		if ( '' === $tipo ) {
			return null;
		}

		return 'CAJA' === $tipo ? 1 : 0;
	}

	/**
	 * Segundo dígito del tipo según catálogo EAN (00–10 reservados).
	 *
	 * NO FABRICADO=00, VASO=01, TAPA=02, TAZON=03, POCKET=04, CHAROL=05,
	 * ACETATO=06, PLATO=07, BOLSA=08, ANTIGRASO=09, CAJA=10.
	 *
	 * @param string $tipo Tipo normalizado.
	 * @return int|null
	 */
	public static function type_suffix_digit( $tipo ) {
		$tipo = self::normalize_product_type( $tipo );
		$map  = array(
			'NO FABRICADO' => 0,
			'VASO'         => 1,
			'TAPA'         => 2,
			'TAZON'        => 3,
			'POCKET'       => 4,
			'CHAROL'       => 5,
			'ACETATO'      => 6,
			'PLATO'        => 7,
			'BOLSA'        => 8,
			'CAJA'         => 0,
			'ANTIGRASO'    => 9,
		);

		return isset( $map[ $tipo ] ) ? (int) $map[ $tipo ] : null;
	}

	/**
	 * @param string $tipo Tipo normalizado.
	 * @return string Dos dígitos (01, 03, 10…).
	 */
	public static function type_code( $tipo ) {
		$tipo = self::normalize_product_type( $tipo );
		if ( '' === $tipo ) {
			return '';
		}

		$custom = Logistica_Label_Etiqueta_Tipos::type_code_for( $tipo );
		if ( '' !== $custom ) {
			return $custom;
		}

		return self::built_in_type_code( $tipo );
	}

	/**
	 * @param string $tipo Tipo normalizado del catálogo base.
	 * @return string Dos dígitos (01, 03, 10…).
	 */
	public static function built_in_type_code( $tipo ) {
		$prefix = self::type_prefix_digit( $tipo );
		$suffix = self::type_suffix_digit( $tipo );

		if ( null === $prefix || null === $suffix ) {
			return '';
		}

		return (string) $prefix . (string) $suffix;
	}

	/**
	 * @param string $mercado EXP|NAC.
	 * @return int|null
	 */
	public static function market_digit( $mercado ) {
		$digit = Logistica_Label_Etiqueta_Mercados::market_digit_for( $mercado );

		return null === $digit ? null : (int) $digit;
	}

	/**
	 * @param mixed $value Código numérico de 6 dígitos.
	 * @return string
	 */
	public static function normalize_product_code( $value ) {
		$digits = preg_replace( '/\D+/', '', (string) $value );

		if ( '' === $digits ) {
			return '';
		}

		if ( strlen( $digits ) > 6 ) {
			$digits = substr( $digits, -6 );
		}

		return str_pad( $digits, 6, '0', STR_PAD_LEFT );
	}

	/**
	 * @param string $twelve_digits Exactamente 12 dígitos.
	 * @return int
	 */
	public static function ean13_check_digit( $twelve_digits ) {
		$digits = preg_replace( '/\D+/', '', (string) $twelve_digits );
		if ( 12 !== strlen( $digits ) ) {
			return 0;
		}

		$sum = 0;
		for ( $i = 0; $i < 12; $i++ ) {
			$num    = (int) $digits[11 - $i ];
			$sum   += ( 0 === $i % 2 ) ? $num * 3 : $num;
		}

		$mod = $sum % 10;

		return ( 10 - $mod ) % 10;
	}

	/**
	 * @param string $tipo          Tipo producto.
	 * @param string $mercado       EXP|NAC.
	 * @param string $product_code  6 dígitos.
	 * @return string|\WP_Error EAN-13 completo.
	 */
	public static function build_ean13( $tipo, $mercado, $product_code ) {
		$tipo         = self::normalize_product_type( $tipo );
		$mercado      = strtoupper( trim( (string) $mercado ) );
		$product_code = self::normalize_product_code( $product_code );

		if ( '' === $tipo ) {
			return new WP_Error( 'invalid_type', __( 'Tipo de producto no válido para el código de barras.', 'logistica' ) );
		}

		$market_digit = self::market_digit( $mercado );
		$type_code    = self::type_code( $tipo );

		if ( null === $market_digit || '' === $type_code ) {
			return new WP_Error( 'invalid_segments', __( 'No se pudo calcular mercado o tipo para el código de barras.', 'logistica' ) );
		}

		if ( 6 !== strlen( $product_code ) ) {
			return new WP_Error( 'invalid_product_code', __( 'El código de producto debe tener 6 dígitos.', 'logistica' ) );
		}

		$body = self::COUNTRY_PREFIX . $market_digit . $type_code . $product_code;
		if ( 12 !== strlen( $body ) || ! ctype_digit( $body ) ) {
			return new WP_Error( 'invalid_body', __( 'Estructura del código de barras inválida.', 'logistica' ) );
		}

		return $body . (string) self::ean13_check_digit( $body );
	}

	/**
	 * Mapea tipo_producto de cierre de órdenes al tipo de etiqueta.
	 *
	 * @param mixed $tipo_producto Valor del selector de cierre.
	 * @return string
	 */
	public static function closing_type_to_label_type( $tipo_producto ) {
		return self::normalize_product_type( $tipo_producto );
	}
}
