<?php
/**
 * Refresh alt text for images embedded in posts and pages.
 *
 * @package AIAltTextGenerator
 * @since 2.4.1
 */

namespace AIAltTextGenerator;

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

/**
 * Post/page bulk refresh for in-content images.
 *
 * @since 2.4.1
 */
class PostRefresh {

	/**
	 * @param list<int> $post_ids
	 * @return array{processed: int, skipped: int, failed: int, message: string}
	 */
	public static function refreshPosts( array $post_ids ): array {
		$post_ids = array_values(
			array_filter(
				array_map( 'intval', $post_ids ),
				static fn( int $id ): bool => $id > 0 && current_user_can( 'edit_post', $id )
			)
		);

		if ( $post_ids === [] ) {
			return [
				'processed' => 0,
				'skipped'   => 0,
				'failed'    => 0,
				'message'   => __( 'No posts selected.', 'wp-powersuite' ),
			];
		}

		$settings       = Settings::get();
		$force          = ! empty( $settings['sync_overwrite_content_alt'] ) || ! empty( $settings['overwrite_existing'] );
		$attachment_ids = [];

		foreach ( $post_ids as $post_id ) {
			$post = get_post( $post_id );
			if ( ! $post instanceof \WP_Post ) {
				continue;
			}

			foreach ( self::collectAttachmentIdsFromPost( $post ) as $attachment_id ) {
				$attachment_ids[ $attachment_id ] = true;
			}
		}

		$ids = array_map( 'intval', array_keys( $attachment_ids ) );
		if ( $ids === [] ) {
			return [
				'processed' => 0,
				'skipped'   => 0,
				'failed'    => 0,
				'message'   => __( 'No library images found in the selected posts.', 'wp-powersuite' ),
			];
		}

		if ( ! empty( $settings['queue_enabled'] ) ) {
			$added = Queue::enqueue( $ids, $force );
			return [
				'processed' => $added,
				'skipped'   => 0,
				'failed'    => 0,
				'message'   => sprintf(
					/* translators: %d: number of images */
					_n( '%d image queued for refresh.', '%d images queued for refresh.', $added, 'wp-powersuite' ),
					$added
				),
			];
		}

		$processed = 0;
		$skipped   = 0;
		$failed    = 0;

		foreach ( $ids as $attachment_id ) {
			if ( ! Module::checkRateLimit() ) {
				break;
			}

			$result = GenerationService::generateForAttachment( $attachment_id, $force );
			if ( $result['status'] === 'success' ) {
				++$processed;
				if ( ! empty( $settings['sync_content_alt'] ) ) {
					ContentSync::syncAttachmentAlt( $attachment_id, (string) ( $result['alt_text'] ?? '' ) );
				}
			} elseif ( $result['status'] === 'skipped' ) {
				++$skipped;
			} else {
				++$failed;
			}
		}

		return [
			'processed' => $processed,
			'skipped'   => $skipped,
			'failed'    => $failed,
			'message'   => sprintf(
				/* translators: 1: processed, 2: skipped, 3: failed */
				__( 'Refreshed %1$d images. Skipped %2$d. Failed %3$d.', 'wp-powersuite' ),
				$processed,
				$skipped,
				$failed
			),
		];
	}

	/**
	 * @return list<int>
	 */
	private static function collectAttachmentIdsFromPost( \WP_Post $post ): array {
		$ids      = [];
		$settings = Settings::get();

		if ( has_blocks( $post->post_content ) ) {
			$ids = array_merge( $ids, self::attachmentIdsFromBlocks( parse_blocks( $post->post_content ) ) );
		}

		$ids = array_merge( $ids, self::attachmentIdsFromClassicHtml( $post->post_content, $settings ) );
		$ids = array_merge( $ids, self::attachmentIdsFromElementor( (int) $post->ID ) );

		return array_values( array_unique( array_filter( array_map( 'intval', $ids ) ) ) );
	}

	/**
	 * @param list<array<string, mixed>> $blocks
	 * @return list<int>
	 */
	private static function attachmentIdsFromBlocks( array $blocks ): array {
		$ids = [];

		foreach ( $blocks as $block ) {
			if ( ! is_array( $block ) ) {
				continue;
			}

			$name  = (string) ( $block['blockName'] ?? '' );
			$attrs = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : [];

			if ( in_array( $name, [ 'core/image', 'core/cover', 'core/media-text' ], true ) ) {
				$id = (int) ( $attrs['id'] ?? 0 );
				if ( $id > 0 ) {
					$ids[] = $id;
				}
			}

			if ( $name === 'core/gallery' && ! empty( $attrs['ids'] ) && is_array( $attrs['ids'] ) ) {
				foreach ( $attrs['ids'] as $id ) {
					$id = (int) $id;
					if ( $id > 0 ) {
						$ids[] = $id;
					}
				}
			}

			if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
				$ids = array_merge( $ids, self::attachmentIdsFromBlocks( $block['innerBlocks'] ) );
			}
		}

		return $ids;
	}

	/**
	 * @return list<int>
	 */
	private static function attachmentIdsFromClassicHtml( string $content, array $settings ): array {
		if ( $content === '' || stripos( $content, '<img' ) === false ) {
			return [];
		}

		$ids = [];
		if ( ! preg_match_all( '/<img\b[^>]*>/i', $content, $matches ) ) {
			return [];
		}

		foreach ( $matches[0] as $tag ) {
			$attachment_id = 0;
			if ( preg_match( '/\bclass=["\'][^"\']*\bwp-image-(\d+)\b/i', $tag, $class_match ) ) {
				$attachment_id = (int) $class_match[1];
			}

			if ( $attachment_id <= 0 ) {
				$src = self::imageSrcFromTag( $tag, $settings );
				if ( $src !== '' ) {
					$attachment_id = ContentScanner::attachmentIdFromUrl( $src );
				}
			}

			if ( $attachment_id > 0 ) {
				$ids[] = $attachment_id;
			}
		}

		return $ids;
	}

	/** @return list<int> */
	private static function attachmentIdsFromElementor( int $post_id ): array {
		$raw = get_post_meta( $post_id, '_elementor_data', true );
		if ( ! is_string( $raw ) || $raw === '' ) {
			return [];
		}

		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			return [];
		}

		return self::attachmentIdsFromElementorNodes( $data );
	}

	/**
	 * @param list<array<string, mixed>> $nodes
	 * @return list<int>
	 */
	private static function attachmentIdsFromElementorNodes( array $nodes ): array {
		$ids = [];

		foreach ( $nodes as $node ) {
			if ( ! is_array( $node ) ) {
				continue;
			}

			$widget   = (string) ( $node['widgetType'] ?? '' );
			$settings = is_array( $node['settings'] ?? null ) ? $node['settings'] : [];

			if ( in_array( $widget, [ 'image', 'image-box', 'image-gallery', 'image-carousel', 'media-carousel' ], true ) ) {
				$id = (int) ( $settings['image']['id'] ?? $settings['image_id'] ?? 0 );
				if ( $id > 0 ) {
					$ids[] = $id;
				}
			}

			if ( ! empty( $node['elements'] ) && is_array( $node['elements'] ) ) {
				$ids = array_merge( $ids, self::attachmentIdsFromElementorNodes( $node['elements'] ) );
			}
		}

		return $ids;
	}

	private static function imageSrcFromTag( string $tag, array $settings ): string {
		$attrs = [ 'src' ];
		$lazy  = trim( (string) ( $settings['lazy_load_src_attr'] ?? '' ) );
		if ( $lazy !== '' ) {
			$attrs[] = $lazy;
		} else {
			$attrs = array_merge( $attrs, [ 'data-src', 'data-lazy-src', 'data-original' ] );
		}

		foreach ( $attrs as $attr ) {
			if ( preg_match( '/\b' . preg_quote( $attr, '/' ) . '=["\']([^"\']+)["\']/i', $tag, $match ) ) {
				$url = trim( (string) $match[1] );
				if ( $url !== '' ) {
					return $url;
				}
			}
		}

		return '';
	}
}
