<?php
/**
 * Papelera centralizada (soft delete).
 *
 * @package Logistica
 */

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

/**
 * Persistencia y restauración de elementos eliminados.
 */
class Logistica_Trash {

	/**
	 * @param array<string, mixed> $entry Datos del elemento eliminado.
	 * @return int ID en papelera o 0.
	 */
	public static function add( $entry ) {
		global $wpdb;

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

		$module_key  = sanitize_key( (string) ( $entry['module_key'] ?? '' ) );
		$entity_type = sanitize_key( (string) ( $entry['entity_type'] ?? '' ) );
		$entity_id   = sanitize_text_field( (string) ( $entry['entity_id'] ?? '' ) );

		if ( '' === $module_key || '' === $entity_type || '' === $entity_id ) {
			return 0;
		}

		$row = array(
			'module_key'   => $module_key,
			'entity_type'  => $entity_type,
			'entity_id'    => $entity_id,
			'entity_label' => sanitize_text_field( (string) ( $entry['entity_label'] ?? $entity_id ) ),
			'payload'      => self::encode_payload( $entry['payload'] ?? null ),
			'status'       => 'deleted',
			'deleted_by'   => max( 0, (int) ( $entry['deleted_by'] ?? get_current_user_id() ) ),
			'deleted_at'   => current_time( 'mysql', true ),
			'restored_by'  => 0,
			'restored_at'  => null,
		);

		$inserted = $wpdb->insert( $table, $row );
		return $inserted ? (int) $wpdb->insert_id : 0;
	}

	/**
	 * @param int $trash_id ID en papelera.
	 * @return array<string, mixed>|WP_Error
	 */
	public static function restore( $trash_id ) {
		global $wpdb;

		$table = Logistica_Database::table( 'papelera' );
		$trash_id = (int) $trash_id;

		if ( ! $table || $trash_id <= 0 ) {
			return new WP_Error( 'invalid_trash', __( 'Registro de papelera no válido.', 'logistica' ) );
		}

		$row = $wpdb->get_row(
			$wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $trash_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			ARRAY_A
		);

		if ( ! is_array( $row ) || 'deleted' !== ( $row['status'] ?? '' ) ) {
			return new WP_Error( 'not_in_trash', __( 'El elemento no está en la papelera o ya fue restaurado.', 'logistica' ) );
		}

		$payload = json_decode( (string) ( $row['payload'] ?? '' ), true );
		if ( ! is_array( $payload ) ) {
			$payload = array();
		}

		/**
		 * Permite a cada módulo restaurar su entidad sin acoplar la papelera al dominio.
		 *
		 * @param string               $module_key  Módulo.
		 * @param string               $entity_type Tipo.
		 * @param string               $entity_id   ID.
		 * @param array<string, mixed> $payload     Snapshot guardado.
		 * @param array<string, mixed> $row         Fila completa de papelera.
		 */
		do_action( 'logistica_lifecycle_restore', (string) $row['module_key'], (string) $row['entity_type'], (string) $row['entity_id'], $payload, $row );

		$updated = $wpdb->update(
			$table,
			array(
				'status'      => 'restored',
				'restored_by' => get_current_user_id(),
				'restored_at' => current_time( 'mysql', true ),
			),
			array( 'id' => $trash_id ),
			array( '%s', '%d', '%s' ),
			array( '%d' )
		);

		if ( false === $updated ) {
			return new WP_Error( 'restore_failed', __( 'No se pudo marcar el elemento como restaurado.', 'logistica' ) );
		}

		return self::format_row(
			array_merge(
				$row,
				array(
					'status'      => 'restored',
					'restored_by' => get_current_user_id(),
					'restored_at' => current_time( 'mysql', true ),
				)
			)
		);
	}

	/**
	 * Elimina definitivamente un elemento de la papelera y su registro origen.
	 *
	 * @param int $trash_id ID en papelera.
	 * @return true|WP_Error
	 */
	public static function purge( $trash_id ) {
		if ( ! Logistica_Entity_Lifecycle::can_purge() ) {
			return new WP_Error( 'forbidden', __( 'No tienes permiso para eliminar definitivamente.', 'logistica' ) );
		}

		global $wpdb;

		$table    = Logistica_Database::table( 'papelera' );
		$trash_id = (int) $trash_id;

		if ( ! $table || $trash_id <= 0 ) {
			return new WP_Error( 'invalid_trash', __( 'Registro de papelera no válido.', 'logistica' ) );
		}

		$row = $wpdb->get_row(
			$wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $trash_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			ARRAY_A
		);

		if ( ! is_array( $row ) ) {
			return new WP_Error( 'not_found', __( 'Elemento no encontrado en la papelera.', 'logistica' ) );
		}

		$payload = json_decode( (string) ( $row['payload'] ?? '' ), true );
		if ( ! is_array( $payload ) ) {
			$payload = array();
		}

		$module_key  = (string) ( $row['module_key'] ?? '' );
		$entity_type = (string) ( $row['entity_type'] ?? '' );
		$entity_id   = (string) ( $row['entity_id'] ?? '' );
		$table_key   = Logistica_Soft_Delete::table_key_for_entity( $module_key, $entity_type );

		if ( $table_key ) {
			$existing = Logistica_Soft_Delete::get_row( $table_key, (int) $entity_id, true );
			if ( $existing ) {
				$hard = Logistica_Soft_Delete::hard_delete( $table_key, (int) $entity_id );
				if ( is_wp_error( $hard ) && 'forbidden' === $hard->get_error_code() ) {
					return $hard;
				}
			} else {
				Logistica_Entity_Lifecycle::record_purge( $module_key, $entity_type, $entity_id, $payload );
			}
		} else {
			do_action( 'logistica_lifecycle_purge_entity', $module_key, $entity_type, $entity_id, $payload );
			Logistica_Entity_Lifecycle::record_purge( $module_key, $entity_type, $entity_id, $payload );
		}

		$deleted = $wpdb->delete( $table, array( 'id' => $trash_id ), array( '%d' ) );
		if ( ! $deleted ) {
			return new WP_Error( 'purge_failed', __( 'No se pudo eliminar el registro de la papelera.', 'logistica' ) );
		}

		return true;
	}

	/**
	 * @param array<string, mixed> $args Filtros y paginación.
	 * @return array{items: array<int, array<string, mixed>>, total: int}
	 */
	public static function query( $args = array() ) {
		global $wpdb;

		$table = Logistica_Database::table( 'papelera' );
		if ( ! $table ) {
			return array(
				'items' => array(),
				'total' => 0,
			);
		}

		$where  = array( '1=1' );
		$params = array();

		$status = sanitize_key( (string) ( $args['status'] ?? 'deleted' ) );
		if ( in_array( $status, array( 'deleted', 'restored', 'all' ), true ) && 'all' !== $status ) {
			$where[]  = 'status = %s';
			$params[] = $status;
		}

		if ( ! empty( $args['module_key'] ) ) {
			$where[]  = 'module_key = %s';
			$params[] = sanitize_key( (string) $args['module_key'] );
		}

		if ( ! empty( $args['deleted_by'] ) ) {
			$where[]  = 'deleted_by = %d';
			$params[] = max( 0, (int) $args['deleted_by'] );
		}

		if ( ! empty( $args['date_from'] ) ) {
			$where[]  = 'deleted_at >= %s';
			$params[] = sanitize_text_field( (string) $args['date_from'] ) . ' 00:00:00';
		}

		if ( ! empty( $args['date_to'] ) ) {
			$where[]  = 'deleted_at <= %s';
			$params[] = sanitize_text_field( (string) $args['date_to'] ) . ' 23:59:59';
		}

		if ( ! empty( $args['search'] ) ) {
			$like     = '%' . $wpdb->esc_like( sanitize_text_field( (string) $args['search'] ) ) . '%';
			$where[]  = '(entity_label LIKE %s OR entity_id LIKE %s OR module_key LIKE %s)';
			$params[] = $like;
			$params[] = $like;
			$params[] = $like;
		}

		$where_sql = implode( ' AND ', $where );
		$count_sql = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		if ( $params ) {
			$count_sql = $wpdb->prepare( $count_sql, $params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		}
		$total = (int) $wpdb->get_var( $count_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		$page      = max( 1, (int) ( $args['page'] ?? 1 ) );
		$page_size = max( 1, min( 100, (int) ( $args['page_size'] ?? 25 ) ) );
		$offset    = ( $page - 1 ) * $page_size;

		$list_sql = "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY deleted_at DESC, id DESC LIMIT %d OFFSET %d"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$list_params = array_merge( $params, array( $page_size, $offset ) );
		$rows = $wpdb->get_results( $wpdb->prepare( $list_sql, $list_params ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		$items = array();
		if ( is_array( $rows ) ) {
			foreach ( $rows as $row ) {
				$items[] = self::format_row( $row );
			}
		}

		return array(
			'items' => $items,
			'total' => $total,
		);
	}

	/**
	 * @param array<string, mixed> $row Fila DB.
	 * @return array<string, mixed>
	 */
	public static function format_row( $row ) {
		$deleted_by  = (int) ( $row['deleted_by'] ?? 0 );
		$restored_by = (int) ( $row['restored_by'] ?? 0 );
		$deleted_user  = $deleted_by > 0 ? get_userdata( $deleted_by ) : false;
		$restored_user = $restored_by > 0 ? get_userdata( $restored_by ) : false;

		return array(
			'id'                => (int) ( $row['id'] ?? 0 ),
			'module_key'        => (string) ( $row['module_key'] ?? '' ),
			'module_label'      => Logistica_Module_Registry::module_label( (string) ( $row['module_key'] ?? '' ) ),
			'entity_type'       => (string) ( $row['entity_type'] ?? '' ),
			'entity_label'      => (string) ( $row['entity_label'] ?? '' ),
			'entity_id'         => (string) ( $row['entity_id'] ?? '' ),
			'status'            => (string) ( $row['status'] ?? '' ),
			'deleted_by'        => $deleted_by,
			'deleted_by_name'   => $deleted_user ? (string) $deleted_user->display_name : '',
			'deleted_at'        => (string) ( $row['deleted_at'] ?? '' ),
			'restored_by'       => $restored_by,
			'restored_by_name'  => $restored_user ? (string) $restored_user->display_name : '',
			'restored_at'       => (string) ( $row['restored_at'] ?? '' ),
			'can_restore'       => 'deleted' === ( $row['status'] ?? '' ) && Logistica_Entity_Lifecycle::can_restore(),
			'can_purge'         => Logistica_Entity_Lifecycle::can_purge(),
		);
	}

	/**
	 * @param mixed $payload Datos a serializar.
	 * @return string|null
	 */
	private static function encode_payload( $payload ) {
		if ( null === $payload || '' === $payload ) {
			return null;
		}

		if ( is_string( $payload ) ) {
			return $payload;
		}

		return wp_json_encode( $payload );
	}
}
