<?php
/**
 * Historial de impresión de rótulos de rollos (parciales / consecutivos).
 *
 * @package Logistica
 */

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

/**
 * Persistencia del historial de rotulación por ancho de catálogo.
 */
class Logistica_Rotulo_Rollos_Print_History {

	/**
	 * Máximo de impresiones guardadas / mostradas por rótulo.
	 */
	public const MAX_ENTRIES = 2;

	/**
	 * @param mixed $value Valor crudo.
	 * @return string
	 */
	public static function normalize_text( $value ) {
		return trim( (string) $value );
	}

	/**
	 * @param mixed $value Cantidad.
	 * @return int
	 */
	public static function normalize_positive_int( $value ) {
		$number = (int) $value;
		return $number > 0 ? $number : 1;
	}

	/**
	 * @param mixed $value Lista de pesos.
	 * @return list<string>
	 */
	public static function normalize_pesos( $value ) {
		if ( is_string( $value ) ) {
			$decoded = json_decode( $value, true );
			if ( is_array( $decoded ) ) {
				$value = $decoded;
			} else {
				$value = preg_split( '/[\n,;]+/', $value );
			}
		}

		if ( ! is_array( $value ) ) {
			return array();
		}

		$pesos = array();
		foreach ( $value as $item ) {
			$text = trim( (string) $item );
			if ( '' !== $text ) {
				$pesos[] = $text;
			}
		}

		return array_values( $pesos );
	}

	/**
	 * Incrementa el componente numérico del lote (misma lógica que el cliente).
	 *
	 * @param string $lote   Lote base.
	 * @param int    $offset Desplazamiento.
	 * @return string
	 */
	public static function increment_lote( $lote, $offset ) {
		$text   = self::normalize_text( $lote );
		$offset = (int) $offset;

		if ( '' === $text || 0 === $offset ) {
			return $text;
		}

		if ( ! preg_match( '/^(\D*)(\d+)(\D*)$/', $text, $match ) ) {
			return $text;
		}

		$prefix  = $match[1];
		$num_str = $match[2];
		$suffix  = $match[3];
		$number  = (int) $num_str + $offset;

		if ( $number < 0 ) {
			return $text;
		}

		$padded = (string) $number;
		if ( strlen( $num_str ) > strlen( $padded ) ) {
			$padded = str_pad( $padded, strlen( $num_str ), '0', STR_PAD_LEFT );
		}

		return $prefix . $padded . $suffix;
	}

	/**
	 * @param array<string, mixed> $row Fila DB.
	 * @return array<string, mixed>
	 */
	public static function format_entry_for_client( $row ) {
		if ( ! is_array( $row ) ) {
			return array();
		}

		$pesos = self::normalize_pesos( $row['pesos_json'] ?? array() );

		return array(
			'id'               => (int) ( $row['id'] ?? 0 ),
			'rotulo_id'        => (int) ( $row['rotulo_id'] ?? 0 ),
			'lote_inicio'      => (string) ( $row['lote_inicio'] ?? '' ),
			'lote_fin'         => (string) ( $row['lote_fin'] ?? '' ),
			'num_consecutivo'  => (int) ( $row['num_consecutivo'] ?? 1 ),
			'num_copias'       => (int) ( $row['num_copias'] ?? 1 ),
			'gramaje'          => (string) ( $row['gramaje'] ?? '' ),
			'pesos'            => $pesos,
			'printed_at'       => (string) ( $row['printed_at'] ?? '' ),
			'created_by'       => (int) ( $row['created_by'] ?? 0 ),
		);
	}

	/**
	 * @param int $rotulo_id ID del catálogo.
	 * @param int $limit     Máximo de filas.
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_for_rotulo( $rotulo_id, $limit = self::MAX_ENTRIES ) {
		global $wpdb;

		$rotulo_id = (int) $rotulo_id;
		$limit     = max( 1, min( (int) $limit, self::MAX_ENTRIES ) );

		if ( $rotulo_id <= 0 || ! Logistica_Rotulo_Rollos_Catalog::get_by_id( $rotulo_id ) ) {
			return array();
		}

		Logistica_Database::ensure_rotulo_rollos_print_historial_table();
		$table = Logistica_Database::table( 'rotulo_rollos_print_historial' );
		if ( ! $table ) {
			return array();
		}

		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM {$table} WHERE rotulo_id = %d ORDER BY printed_at DESC, id DESC LIMIT %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				$rotulo_id,
				$limit
			),
			ARRAY_A
		);

		if ( ! is_array( $rows ) ) {
			return array();
		}

		return array_values(
			array_filter(
				array_map( array( __CLASS__, 'format_entry_for_client' ), $rows )
			)
		);
	}

	/**
	 * Guarda una impresión y devuelve el historial actualizado.
	 *
	 * @param array<string, mixed> $raw Datos del formulario/impresión.
	 * @param int                  $user_id Usuario.
	 * @return array<string, mixed>|WP_Error
	 */
	public static function save_entry( $raw, $user_id = 0 ) {
		global $wpdb;

		if ( ! is_array( $raw ) ) {
			$raw = array();
		}

		$rotulo_id       = (int) ( $raw['rotulo_id'] ?? 0 );
		$lote_inicio     = self::normalize_text( $raw['lote_inicio'] ?? '' );
		$num_consecutivo = self::normalize_positive_int( $raw['num_consecutivo'] ?? 1 );
		$num_copias      = self::normalize_positive_int( $raw['num_copias'] ?? 1 );
		$gramaje         = self::normalize_text( $raw['gramaje'] ?? '' );
		$pesos           = self::normalize_pesos( $raw['pesos'] ?? array() );
		$user_id         = max( 0, (int) $user_id );

		if ( $rotulo_id <= 0 || ! Logistica_Rotulo_Rollos_Catalog::get_by_id( $rotulo_id ) ) {
			return new WP_Error( 'invalid_rotulo', __( 'Registro de rótulo no válido.', 'logistica' ) );
		}

		if ( '' === $lote_inicio ) {
			return new WP_Error( 'invalid_lote', __( 'Indica el lote inicial para guardar el historial.', 'logistica' ) );
		}

		$lote_fin = self::normalize_text( $raw['lote_fin'] ?? '' );
		if ( '' === $lote_fin ) {
			$lote_fin = self::increment_lote( $lote_inicio, max( 0, $num_consecutivo - 1 ) );
		}

		Logistica_Database::ensure_rotulo_rollos_print_historial_table();
		$table = Logistica_Database::table( 'rotulo_rollos_print_historial' );
		if ( ! $table ) {
			return new WP_Error( 'missing_table', __( 'No se pudo acceder al historial de rotulación.', 'logistica' ) );
		}

		$inserted = $wpdb->insert(
			$table,
			array(
				'rotulo_id'       => $rotulo_id,
				'lote_inicio'     => $lote_inicio,
				'lote_fin'        => $lote_fin,
				'num_consecutivo' => $num_consecutivo,
				'num_copias'      => $num_copias,
				'gramaje'         => $gramaje,
				'pesos_json'      => wp_json_encode( $pesos ),
				'printed_at'      => current_time( 'mysql', true ),
				'created_by'      => $user_id > 0 ? $user_id : get_current_user_id(),
			),
			array( '%d', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d' )
		);

		if ( false === $inserted ) {
			return new WP_Error( 'db_error', __( 'No se pudo guardar el historial de rotulación.', 'logistica' ) );
		}

		self::trim_rotulo_history( $rotulo_id );

		$entries = self::get_for_rotulo( $rotulo_id );

		return array(
			'entry'   => ! empty( $entries ) ? $entries[0] : array(),
			'entries' => $entries,
		);
	}

	/**
	 * Conserva solo las impresiones más recientes por rótulo.
	 *
	 * @param int $rotulo_id ID del catálogo.
	 * @return void
	 */
	private static function trim_rotulo_history( $rotulo_id ) {
		global $wpdb;

		$rotulo_id = (int) $rotulo_id;
		if ( $rotulo_id <= 0 ) {
			return;
		}

		$table = Logistica_Database::table( 'rotulo_rollos_print_historial' );
		if ( ! $table ) {
			return;
		}

		$keep_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT id FROM {$table} WHERE rotulo_id = %d ORDER BY printed_at DESC, id DESC LIMIT %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				$rotulo_id,
				self::MAX_ENTRIES
			)
		);

		if ( ! is_array( $keep_ids ) || empty( $keep_ids ) ) {
			return;
		}

		$keep_ids     = array_map( 'intval', $keep_ids );
		$placeholders = implode( ',', array_fill( 0, count( $keep_ids ), '%d' ) );

		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$table} WHERE rotulo_id = %d AND id NOT IN ({$placeholders})", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPlaceholder
				array_merge( array( $rotulo_id ), $keep_ids )
			)
		);
	}
}
