<?php
/**
 * Enlace entre cuadro de pedidos y planeación formado / impresión.
 *
 * @package Logistica
 */

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

/**
 * Obtiene líneas de OC vigentes sin programar y las convierte en tareas de planeación.
 */
class Logistica_Pedidos_Planeacion_Bridge {

	const MODULE_IMPRESION = 'impresion';
	const MODULE_FORMADO   = 'formado';

	/** @var list<string> */
	const TERMINAL_ESTADOS = array(
		'cancelado',
		'entregado',
		'finalizado',
		'despacho',
	);

	/**
	 * @param string $module Módulo de planeación.
	 * @return bool
	 */
	public static function is_valid_module( $module ) {
		$module = sanitize_key( (string) $module );

		return in_array( $module, array( self::MODULE_IMPRESION, self::MODULE_FORMADO ), true );
	}

	/**
	 * @param string $module Módulo de planeación.
	 * @return string Nonce action.
	 */
	public static function nonce_action_for_module( $module ) {
		return self::MODULE_FORMADO === sanitize_key( (string) $module )
			? 'logistica_planeacion_formado'
			: 'logistica_planeacion_impresion';
	}

	/**
	 * IDs de líneas de OC ya vinculadas a tareas de planeación.
	 *
	 * @param string $module Módulo de planeación.
	 * @return array<int, int>
	 */
	public static function get_scheduled_linea_ids( $module ) {
		global $wpdb;

		if ( ! self::is_valid_module( $module ) ) {
			return array();
		}

		$table = self::MODULE_FORMADO === $module
			? Logistica_Forming_Planning::table_name()
			: Logistica_Print_Planning::table_name();

		$not_deleted = Logistica_Soft_Delete::sql_not_deleted(
			self::MODULE_FORMADO === $module ? 'planeacion_formado' : 'planeacion_impresion'
		);

		$rows = $wpdb->get_col(
			"SELECT tasks FROM {$table} WHERE {$not_deleted}" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		);

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

		$ids = array();

		foreach ( $rows as $tasks_json ) {
			$tasks = json_decode( (string) $tasks_json, true );
			if ( ! is_array( $tasks ) ) {
				continue;
			}

			foreach ( $tasks as $task ) {
				if ( ! is_array( $task ) ) {
					continue;
				}

				$linea_id = (int) ( $task['pedido_orden_linea_id'] ?? 0 );
				if ( $linea_id > 0 ) {
					$ids[ $linea_id ] = $linea_id;
				}
			}
		}

		return array_values( $ids );
	}

	/**
	 * Líneas de OC pendientes de programar en el módulo indicado.
	 *
	 * @param string $module Módulo de planeación.
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_pending_lineas( $module ) {
		if ( ! self::is_valid_module( $module ) ) {
			return array();
		}

		$scheduled_ids = array_flip( self::get_scheduled_linea_ids( $module ) );
		$pending       = array();

		foreach ( Logistica_Pedidos_Board::get_implemented_board_slugs() as $board_slug ) {
			if ( ! self::board_applies_to_module( $board_slug, $module ) ) {
				continue;
			}

			foreach ( Logistica_Pedidos_Board::get_ordenes_compra( $board_slug ) as $orden ) {
				if ( ! is_array( $orden ) ) {
					continue;
				}

				foreach ( (array) ( $orden['lineas'] ?? array() ) as $linea ) {
					if ( ! is_array( $linea ) ) {
						continue;
					}

					$linea_id = (int) ( $linea['id'] ?? 0 );
					if ( $linea_id <= 0 || isset( $scheduled_ids[ $linea_id ] ) ) {
						continue;
					}

					if ( ! self::linea_is_eligible( $linea, $orden, $module ) ) {
						continue;
					}

					$pending[] = self::format_pending_linea( $linea, $orden, $module );
				}
			}
		}

		usort(
			$pending,
			static function ( $a, $b ) {
				$date_a = (string) ( $a['fecha_entrega'] ?? '' );
				$date_b = (string) ( $b['fecha_entrega'] ?? '' );
				if ( $date_a !== $date_b ) {
					if ( '' === $date_a ) {
						return 1;
					}
					if ( '' === $date_b ) {
						return -1;
					}
					return strcmp( $date_a, $date_b );
				}

				return strcasecmp(
					(string) ( $a['cliente'] ?? '' ),
					(string) ( $b['cliente'] ?? '' )
				);
			}
		);

		return $pending;
	}

	/**
	 * @param string $board_slug Tablero.
	 * @param string $module     Módulo.
	 * @return bool
	 */
	private static function board_applies_to_module( $board_slug, $module ) {
		$board_slug = sanitize_title( (string) $board_slug );

		if ( 'hornitos' === $board_slug ) {
			return false;
		}

		if ( self::MODULE_IMPRESION === $module ) {
			return 'sin-impresion' !== $board_slug;
		}

		return true;
	}

	/**
	 * @param array<string, mixed> $linea Línea hidratada.
	 * @param array<string, mixed> $orden Orden de compra.
	 * @param string               $module Módulo.
	 * @return bool
	 */
	private static function linea_is_eligible( $linea, $orden, $module ) {
		$estado = sanitize_key( (string) ( $linea['estado'] ?? 'sin_confirmar' ) );

		if ( in_array( $estado, self::TERMINAL_ESTADOS, true ) ) {
			return false;
		}

		$pendiente = Logistica_Pedidos_Board::compute_orden_linea_pendiente( $linea );
		if ( $pendiente <= 0 ) {
			return false;
		}

		$board_slug = sanitize_title( (string) ( $orden['board_slug'] ?? '' ) );
		$eligible   = self::eligible_estados_for_module( $module, $board_slug );

		return in_array( $estado, $eligible, true );
	}

	/**
	 * @param string $module     Módulo.
	 * @param string $board_slug Tablero.
	 * @return list<string>
	 */
	private static function eligible_estados_for_module( $module, $board_slug ) {
		if ( self::MODULE_IMPRESION === $module ) {
			return array( 'aprobado', 'programado', 'conversion' );
		}

		if ( 'sin-impresion' === $board_slug ) {
			return array( 'aprobado', 'programado', 'conversion' );
		}

		return array( 'impresion', 'troquelado', 'descartone', 'conversion', 'programado' );
	}

	/**
	 * @param array<string, mixed> $linea Línea hidratada.
	 * @param array<string, mixed> $orden Orden.
	 * @param string               $module Módulo.
	 * @return array<string, mixed>
	 */
	private static function format_pending_linea( $linea, $orden, $module ) {
		$estado_options = Logistica_Pedidos_Board::orden_estado_options();
		$estado         = sanitize_key( (string) ( $linea['estado'] ?? 'sin_confirmar' ) );
		$board_slug     = sanitize_title( (string) ( $orden['board_slug'] ?? '' ) );
		$board_label    = $board_slug;

		foreach ( Logistica_Pedidos_Board::get_boards() as $board ) {
			if ( (string) ( $board['slug'] ?? '' ) === $board_slug ) {
				$board_label = (string) ( $board['label'] ?? $board_slug );
				break;
			}
		}

		$client = Logistica_Pedidos_Board::format_orden_linea_client( $linea );

		return array(
			'linea_id'           => (int) ( $linea['id'] ?? 0 ),
			'orden_id'           => (int) ( $orden['id'] ?? 0 ),
			'board_slug'         => $board_slug,
			'board_label'        => $board_label,
			'oc_numero'          => (string) ( $orden['oc_numero'] ?? '' ),
			'cliente'            => (string) ( $orden['cliente'] ?? '' ),
			'fecha_oc'           => Logistica_Pedidos_Board::format_date_input( $orden['fecha_oc'] ?? '' ),
			'articulo'           => (string) ( $linea['articulo'] ?? '' ),
			'descripcion'        => (string) ( $linea['descripcion'] ?? '' ),
			'cantidad'           => (float) ( $linea['cantidad'] ?? 0 ),
			'cantidad_fmt'       => (string) ( $client['cantidad_fmt'] ?? '' ),
			'cantidad_pendiente' => (float) ( $client['cantidad_pendiente'] ?? 0 ),
			'cantidad_pendiente_fmt' => (string) ( $client['cantidad_pendiente_fmt'] ?? '' ),
			'fecha_entrega'      => (string) ( $client['fecha_entrega'] ?? '' ),
			'fecha_prorroga'     => (string) ( $client['fecha_prorroga'] ?? '' ),
			'op'                 => (string) ( $linea['op'] ?? '' ),
			'estado'             => $estado,
			'estado_label'       => (string) ( $estado_options[ $estado ] ?? $estado ),
			'observaciones'      => (string) ( $linea['observaciones'] ?? '' ),
			'formado_referencia' => self::MODULE_FORMADO === $module
				? self::resolve_formado_referencia_key( (string) ( $linea['articulo'] ?? '' ) )
				: '',
			'formado_referencia_label' => (string) ( $linea['articulo'] ?? '' ),
		);
	}

	/**
	 * @param string $articulo Referencia de la línea de OC.
	 * @return string
	 */
	public static function resolve_formado_referencia_key( $articulo ) {
		$articulo = trim( (string) $articulo );
		if ( '' === $articulo ) {
			return '';
		}

		foreach ( Logistica_Forming_Yield::get_entries() as $entry ) {
			$key   = sanitize_key( (string) ( $entry['key'] ?? '' ) );
			$label = trim( (string) ( $entry['label'] ?? '' ) );

			if ( $key === sanitize_key( $articulo ) ) {
				return $key;
			}

			if ( strcasecmp( $label, $articulo ) === 0 ) {
				return $key;
			}
		}

		return Logistica_Forming_Yield::key_from_label( $articulo );
	}

	/**
	 * Programa líneas seleccionadas en la semana y máquina actuales.
	 *
	 * @param string $module        Módulo.
	 * @param string $week_start    Lunes (Y-m-d).
	 * @param string $machine       Máquina.
	 * @param int    $day           Día 0–6.
	 * @param list<int> $linea_ids  IDs de líneas.
	 * @return array<string, mixed>|\WP_Error
	 */
	public static function programar_lineas( $module, $week_start, $machine, $day, $linea_ids ) {
		if ( ! self::is_valid_module( $module ) ) {
			return new WP_Error( 'invalid_module', __( 'Módulo de planeación no válido.', 'logistica' ) );
		}

		$linea_ids = array_values(
			array_unique(
				array_filter(
					array_map( 'intval', (array) $linea_ids ),
					static function ( $id ) {
						return $id > 0;
					}
				)
			)
		);

		if ( empty( $linea_ids ) ) {
			return new WP_Error( 'empty_selection', __( 'Seleccione al menos una línea de pedido.', 'logistica' ) );
		}

		$day = max( 0, min( 6, (int) $day ) );

		$planning_class = self::MODULE_FORMADO === $module
			? 'Logistica_Forming_Planning'
			: 'Logistica_Print_Planning';

		$schedule = call_user_func( array( $planning_class, 'get_schedule' ), $week_start, $machine );
		if ( ! is_array( $schedule ) ) {
			return new WP_Error( 'invalid_schedule', __( 'No se pudo cargar la planeación.', 'logistica' ) );
		}

		$tasks         = is_array( $schedule['tasks'] ?? null ) ? $schedule['tasks'] : array();
		$scheduled_ids = array_flip( self::get_scheduled_linea_ids( $module ) );
		$created       = 0;
		$skipped       = array();

		foreach ( $linea_ids as $linea_id ) {
			if ( isset( $scheduled_ids[ $linea_id ] ) ) {
				$skipped[] = $linea_id;
				continue;
			}

			$context = self::get_linea_context( $linea_id );
			if ( ! $context ) {
				$skipped[] = $linea_id;
				continue;
			}

			$linea = $context['linea'];
			$orden = $context['orden'];

			if ( ! self::linea_is_eligible( $linea, $orden, $module ) ) {
				$skipped[] = $linea_id;
				continue;
			}

			$tasks[] = self::build_task_from_linea( $linea, $orden, $module, $day );
			++$created;
			self::maybe_update_linea_estado( $linea_id, $linea, $module );
		}

		if ( $created <= 0 ) {
			return new WP_Error(
				'nothing_scheduled',
				__( 'Ninguna línea pudo programarse. Verifique que sigan vigentes y no estén ya en planeación.', 'logistica' ),
				array( 'skipped' => $skipped )
			);
		}

		$payload = array(
			'jornada_inicio' => (int) ( $schedule['jornada_inicio'] ?? 420 ),
			'jornada_fin'    => (int) ( $schedule['jornada_fin'] ?? 1020 ),
			'day_hours'      => $schedule['day_hours'] ?? array(),
			'include_lunch'  => ! empty( $schedule['include_lunch'] ),
			'lunch_start'    => $schedule['lunch_start'] ?? null,
			'lunch_end'      => $schedule['lunch_end'] ?? null,
			'tasks'          => $tasks,
		);

		$result = call_user_func( array( $planning_class, 'save_schedule' ), $week_start, $machine, $payload );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return array(
			'schedule' => $result,
			'created'  => $created,
			'skipped'  => $skipped,
		);
	}

	/**
	 * @param int $linea_id ID de línea.
	 * @return array{linea:array<string,mixed>,orden:array<string,mixed>}|null
	 */
	private static function get_linea_context( $linea_id ) {
		global $wpdb;

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

		$lineas_table  = Logistica_Database::table( 'pedidos_ordenes_lineas' );
		$ordenes_table = Logistica_Database::table( 'pedidos_ordenes_compra' );
		if ( ! $lineas_table || ! $ordenes_table ) {
			return null;
		}

		$lineas_not_deleted  = Logistica_Soft_Delete::sql_not_deleted( 'pedidos_ordenes_lineas', 'l' );
		$ordenes_not_deleted = Logistica_Soft_Delete::sql_not_deleted( 'pedidos_ordenes_compra', 'o' );

		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT l.*, o.board_slug, o.oc_numero, o.cliente, o.fecha_oc
				FROM {$lineas_table} l
				INNER JOIN {$ordenes_table} o ON o.id = l.orden_id
				WHERE l.id = %d AND {$lineas_not_deleted} AND {$ordenes_not_deleted}
				LIMIT 1",
				$linea_id
			),
			ARRAY_A
		);

		if ( ! is_array( $row ) ) {
			return null;
		}

		$orden = array(
			'id'          => (int) ( $row['orden_id'] ?? 0 ),
			'board_slug'  => (string) ( $row['board_slug'] ?? '' ),
			'oc_numero'   => (string) ( $row['oc_numero'] ?? '' ),
			'cliente'     => (string) ( $row['cliente'] ?? '' ),
			'fecha_oc'    => (string) ( $row['fecha_oc'] ?? '' ),
		);

		unset( $row['board_slug'], $row['oc_numero'], $row['cliente'], $row['fecha_oc'] );

		$linea = Logistica_Pedidos_Board::hydrate_orden_linea( $row );

		return array(
			'linea' => $linea,
			'orden' => $orden,
		);
	}

	/**
	 * @param array<string, mixed> $linea Línea hidratada.
	 * @param array<string, mixed> $orden Orden.
	 * @param string               $module Módulo.
	 * @param int                  $day Día.
	 * @return array<string, mixed>
	 */
	private static function build_task_from_linea( $linea, $orden, $module, $day ) {
		$op = trim( (string) ( $linea['op'] ?? '' ) );
		if ( '' === $op ) {
			$op = trim( (string) ( $orden['oc_numero'] ?? '' ) );
		}
		if ( '' === $op ) {
			$op = 'SIN OP';
		}

		$cantidad_fmt = Logistica_Pedidos_Board::format_quantity( $linea['cantidad'] ?? 0 );
		$notes        = array();

		if ( '' !== trim( (string) ( $orden['cliente'] ?? '' ) ) ) {
			$notes[] = sprintf(
				/* translators: %s: customer name */
				__( 'Cliente: %s', 'logistica' ),
				trim( (string) $orden['cliente'] )
			);
		}

		if ( '' !== trim( (string) ( $orden['oc_numero'] ?? '' ) ) ) {
			$notes[] = sprintf(
				/* translators: %s: purchase order number */
				__( 'OC: %s', 'logistica' ),
				trim( (string) $orden['oc_numero'] )
			);
		}

		if ( '' !== trim( (string) ( $linea['descripcion'] ?? '' ) ) ) {
			$notes[] = trim( (string) $linea['descripcion'] );
		}

		$fecha_entrega = Logistica_Pedidos_Board::format_date_input( $linea['fecha_entrega'] ?? '' );
		if ( '' !== $fecha_entrega ) {
			$notes[] = sprintf(
				/* translators: %s: delivery date */
				__( 'Entrega: %s', 'logistica' ),
				$fecha_entrega
			);
		}

		if ( '' !== trim( (string) ( $linea['observaciones'] ?? '' ) ) ) {
			$notes[] = trim( (string) $linea['observaciones'] );
		}

		$task = array(
			'id'                   => 'task_' . wp_generate_password( 8, false, false ),
			'day'                  => max( 0, min( 6, (int) $day ) ),
			'status'               => 'pending',
			'op'                   => $op,
			'tamanos'              => $cantidad_fmt . ' unidades',
			'complexity'           => 'normal',
			'notes'                => implode( "\n", $notes ),
			'pedido_orden_linea_id' => (int) ( $linea['id'] ?? 0 ),
			'pedido_board_slug'    => sanitize_title( (string) ( $orden['board_slug'] ?? '' ) ),
			'pedido_oc_numero'     => sanitize_text_field( (string) ( $orden['oc_numero'] ?? '' ) ),
			'pedido_cliente'       => sanitize_text_field( (string) ( $orden['cliente'] ?? '' ) ),
			'pedido_estado'        => self::resolve_task_pedido_estado( $linea ),
		);

		if ( self::MODULE_FORMADO === $module ) {
			$task['labor_type']         = 'formado';
			$task['labor']              = Logistica_Forming_Planning::LABOR_TYPES['formado'] ?? 'Formado';
			$task['formado_referencia'] = self::resolve_formado_referencia_key( (string) ( $linea['articulo'] ?? '' ) );
		} else {
			$task['labor_type'] = 'impresion';
			$task['labor']      = Logistica_Print_Planning::LABOR_TYPES['impresion'] ?? 'Impresión';
		}

		return $task;
	}

	/**
	 * Actualiza pedido_estado en tareas de planeación vinculadas a la línea.
	 *
	 * @param int    $linea_id ID de línea OC.
	 * @param string $estado   Nuevo estado.
	 * @return void
	 */
	public static function sync_linea_estado_to_planeacion_tasks( $linea_id, $estado ) {
		$linea_id = (int) $linea_id;
		$estado   = sanitize_key( (string) $estado );

		if ( $linea_id <= 0 || '' === $estado ) {
			return;
		}

		if ( class_exists( 'Logistica_Forming_Planning', false ) ) {
			$estado = Logistica_Forming_Planning::sanitize_pedido_estado( $estado );
		}

		self::patch_planeacion_tasks_pedido_estado( 'formado', $linea_id, $estado );
		self::patch_planeacion_tasks_pedido_estado( 'impresion', $linea_id, $estado );
	}

	/**
	 * @param string $module   formado|impresion.
	 * @param int    $linea_id ID línea.
	 * @param string $estado   Estado.
	 * @return void
	 */
	private static function patch_planeacion_tasks_pedido_estado( $module, $linea_id, $estado ) {
		global $wpdb;

		if ( ! self::is_valid_module( $module ) ) {
			return;
		}

		$table = self::MODULE_FORMADO === $module
			? Logistica_Forming_Planning::table_name()
			: Logistica_Print_Planning::table_name();

		$entity = self::MODULE_FORMADO === $module ? 'planeacion_formado' : 'planeacion_impresion';
		$not_deleted = Logistica_Soft_Delete::sql_not_deleted( $entity );

		$rows = $wpdb->get_results(
			"SELECT id, tasks FROM {$table} WHERE {$not_deleted}", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			ARRAY_A
		);

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

		$now = class_exists( 'Logistica_User_Panel', false )
			? Logistica_User_Panel::current_datetime()
			: gmdate( 'Y-m-d H:i:s' );

		foreach ( $rows as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}

			$tasks = json_decode( (string) ( $row['tasks'] ?? '[]' ), true );
			if ( ! is_array( $tasks ) || empty( $tasks ) ) {
				continue;
			}

			$changed = false;
			foreach ( $tasks as &$task ) {
				if ( ! is_array( $task ) ) {
					continue;
				}
				if ( (int) ( $task['pedido_orden_linea_id'] ?? 0 ) !== $linea_id ) {
					continue;
				}
				if ( sanitize_key( (string) ( $task['pedido_estado'] ?? '' ) ) === $estado ) {
					continue;
				}
				$task['pedido_estado'] = $estado;
				$changed = true;
			}
			unset( $task );

			if ( ! $changed ) {
				continue;
			}

			$wpdb->update(
				$table,
				array(
					'tasks'      => wp_json_encode( array_values( $tasks ) ),
					'updated_at' => $now,
				),
				array( 'id' => (int) $row['id'] ),
				array( '%s', '%s' ),
				array( '%d' )
			);
		}
	}

	/**
	 * Estado de pedido para una tarea nueva (al programar desde el cuadro).
	 *
	 * @param array<string, mixed> $linea Línea.
	 * @return string
	 */
	private static function resolve_task_pedido_estado( $linea ) {
		$estado = sanitize_key( (string) ( $linea['estado'] ?? 'programado' ) );

		// Al programar desde aprobado, el puente sube la línea a «programado».
		if ( 'aprobado' === $estado ) {
			$estado = 'programado';
		}

		if ( class_exists( 'Logistica_Forming_Planning', false ) ) {
			return Logistica_Forming_Planning::sanitize_pedido_estado( $estado );
		}

		$options = class_exists( 'Logistica_Pedidos_Board', false )
			? Logistica_Pedidos_Board::orden_estado_options()
			: array();

		return isset( $options[ $estado ] ) ? $estado : 'programado';
	}

	/**
	 * Rellena pedido_estado desde la línea OC vinculada (prioridad: BD pedidos).
	 *
	 * @param list<array<string, mixed>> $tasks Tareas.
	 * @return list<array<string, mixed>>
	 */
	public static function hydrate_tasks_pedido_estado( $tasks ) {
		if ( ! is_array( $tasks ) || empty( $tasks ) ) {
			return is_array( $tasks ) ? $tasks : array();
		}

		$ids = array();
		foreach ( $tasks as $task ) {
			if ( ! is_array( $task ) ) {
				continue;
			}
			$linea_id = (int) ( $task['pedido_orden_linea_id'] ?? 0 );
			if ( $linea_id > 0 ) {
				$ids[ $linea_id ] = $linea_id;
			}
		}

		if ( empty( $ids ) ) {
			return $tasks;
		}

		global $wpdb;

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

		$id_list = implode( ',', array_map( 'intval', array_values( $ids ) ) );
		$rows    = $wpdb->get_results(
			"SELECT id, estado FROM {$table} WHERE id IN ({$id_list})", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			ARRAY_A
		);

		$by_id = array();
		if ( is_array( $rows ) ) {
			foreach ( $rows as $row ) {
				if ( ! is_array( $row ) ) {
					continue;
				}
				$by_id[ (int) ( $row['id'] ?? 0 ) ] = sanitize_key( (string) ( $row['estado'] ?? '' ) );
			}
		}

		foreach ( $tasks as &$task ) {
			if ( ! is_array( $task ) ) {
				continue;
			}
			$linea_id = (int) ( $task['pedido_orden_linea_id'] ?? 0 );
			if ( $linea_id <= 0 || ! isset( $by_id[ $linea_id ] ) ) {
				continue;
			}
			$estado = $by_id[ $linea_id ];
			if ( '' === $estado ) {
				continue;
			}
			$task['pedido_estado'] = class_exists( 'Logistica_Forming_Planning', false )
				? Logistica_Forming_Planning::sanitize_pedido_estado( $estado )
				: $estado;
		}
		unset( $task );

		return $tasks;
	}

	/**
	 * Propaga pedido_estado de tareas vinculadas hacia el cuadro de pedidos.
	 *
	 * @param list<array<string, mixed>> $tasks Tareas.
	 * @return void
	 */
	public static function sync_tasks_pedido_estado_to_lineas( $tasks ) {
		if ( ! is_array( $tasks ) || empty( $tasks ) ) {
			return;
		}

		$options = class_exists( 'Logistica_Pedidos_Board', false )
			? Logistica_Pedidos_Board::orden_estado_options()
			: array();

		if ( empty( $options ) ) {
			return;
		}

		global $wpdb;

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

		$now = class_exists( 'Logistica_User_Panel', false )
			? Logistica_User_Panel::current_datetime()
			: gmdate( 'Y-m-d H:i:s' );

		foreach ( $tasks as $task ) {
			if ( ! is_array( $task ) ) {
				continue;
			}

			$linea_id = (int) ( $task['pedido_orden_linea_id'] ?? 0 );
			if ( $linea_id <= 0 ) {
				continue;
			}

			$estado = sanitize_key( (string) ( $task['pedido_estado'] ?? '' ) );
			if ( '' === $estado || ! isset( $options[ $estado ] ) ) {
				continue;
			}

			$current = sanitize_key(
				(string) $wpdb->get_var(
					$wpdb->prepare( "SELECT estado FROM {$table} WHERE id = %d LIMIT 1", $linea_id )
				)
			);

			if ( $current === $estado ) {
				continue;
			}

			$wpdb->update(
				$table,
				array(
					'estado'     => $estado,
					'updated_at' => $now,
				),
				array( 'id' => $linea_id ),
				array( '%s', '%s' ),
				array( '%d' )
			);
		}
	}

	/**
	 * @param int                  $linea_id ID de línea.
	 * @param array<string, mixed> $linea    Línea.
	 * @param string               $module   Módulo.
	 * @return void
	 */
	private static function maybe_update_linea_estado( $linea_id, $linea, $module ) {
		$estado = sanitize_key( (string) ( $linea['estado'] ?? 'sin_confirmar' ) );

		if ( 'aprobado' !== $estado ) {
			return;
		}

		global $wpdb;

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

		$wpdb->update(
			$table,
			array(
				'estado'     => 'programado',
				'updated_at' => Logistica_User_Panel::current_datetime(),
			),
			array( 'id' => (int) $linea_id ),
			array( '%s', '%s' ),
			array( '%d' )
		);
	}
}
