<?php
/**
 * Regenerate Thumbnails Module
 *
 * @package RegenerateThumbnails
 * @since 1.0.0
 */

namespace RegenerateThumbnails;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;

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

class Module extends AbstractModule {

	private const MODULE_ID      = 'regenerate-thumbnails';
	private const MODULE_VERSION = '1.0.0';
	private const NONCE_ACTION   = 'wpps_regenerate_thumbnails';
	private const CURSOR_ATTEMPTS  = 500;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Regenerate Thumbnails', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Regenerate all image thumbnails in your media library. Fix missing or broken thumbnails, generate new custom sizes for existing images, and update metadata.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return 'ajax';
	}

	protected function registerHooks(): void {
		add_action( 'wp_ajax_wpps_rt_get_data', [ $this, 'ajaxGetData' ] );
		add_action( 'wp_ajax_wpps_rt_regenerate', [ $this, 'ajaxRegenerate' ] );
	}

	public function ajaxGetData(): void {
		$this->verifyRequest();

		$all_sizes      = $this->getRegisteredImageSizes();
		$sizes          = $this->filterSizesByDisableThumbnailGeneration( $all_sizes );
		$sizes_filtered = count( $sizes ) < count( $all_sizes );

		wp_send_json_success(
			[
				'image_count'    => $this->countImageAttachments(),
				'sizes'          => $sizes,
				'sizes_filtered' => $sizes_filtered,
			]
		);
	}

	public function ajaxRegenerate(): void {
		$this->verifyRequest();

		$cursor        = isset( $_POST['cursor'] ) ? max( 0, (int) wp_unslash( $_POST['cursor'] ) ) : 0;
		$attachment_id = $this->findNextRegeneratableImageId( $cursor );
		if ( $attachment_id <= 0 ) {
			wp_send_json_success( [ 'done' => true ] );
		}

		$attachment = get_post( $attachment_id );
		if ( ! $attachment instanceof \WP_Post || $attachment->post_type !== 'attachment' ) {
			wp_send_json_error(
				[
					'message'       => __( 'Attachment not found.', 'wp-powersuite' ),
					'attachment_id' => $attachment_id,
					'next_cursor'   => $attachment_id,
				]
			);
		}

		if ( ! current_user_can( 'edit_post', $attachment_id ) ) {
			wp_send_json_error(
				[
					'message'       => __( 'Permission denied.', 'wp-powersuite' ),
					'attachment_id' => $attachment_id,
					'next_cursor'   => $attachment_id,
				]
			);
		}

		$mime = get_post_mime_type( $attachment_id );
		if ( ! is_string( $mime ) || ! str_starts_with( $mime, 'image/' ) ) {
			wp_send_json_error(
				[
					'message'       => __( 'Not an image.', 'wp-powersuite' ),
					'attachment_id' => $attachment_id,
					'next_cursor'   => $attachment_id,
				]
			);
		}

		$file = get_attached_file( $attachment_id );
		if ( ! is_string( $file ) || $file === '' || ! is_readable( $file ) ) {
			wp_send_json_error(
				[
					'message'       => __( 'File not found on disk.', 'wp-powersuite' ),
					'attachment_id' => $attachment_id,
					'next_cursor'   => $attachment_id,
				]
			);
		}

		$metadata = wp_generate_attachment_metadata( $attachment_id, $file );
		if ( is_wp_error( $metadata ) ) {
			wp_send_json_error(
				[
					'message'       => $metadata->get_error_message(),
					'attachment_id' => $attachment_id,
					'next_cursor'   => $attachment_id,
				]
			);
		}

		wp_update_attachment_metadata( $attachment_id, $metadata );

		wp_send_json_success(
			[
				'attachment_id' => $attachment_id,
				'filename'      => basename( $file ),
				'next_cursor'   => $attachment_id,
				'done'          => false,
			]
		);
	}

	private function verifyRequest(): void {
		if ( ! check_ajax_referer( self::NONCE_ACTION, 'nonce', false ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid security token.', 'wp-powersuite' ) ] );
		}
		if ( ! $this->canManagePlugin() ) {
			wp_send_json_error( [ 'message' => __( 'Permission denied.', 'wp-powersuite' ) ] );
		}
	}

	/**
	 * @param array<int, array{name: string, width: int, height: int, crop: bool}> $sizes
	 * @return array<int, array{name: string, width: int, height: int, crop: bool}>
	 */
	private function filterSizesByDisableThumbnailGeneration( array $sizes ): array {
		if ( ! OptionStoreModuleSettingsRepository::isModuleEnabled( 'disable-thumbnail-generation' ) ) {
			return $sizes;
		}

		$settings = OptionStoreModuleSettingsRepository::forModule( 'disable-thumbnail-generation' )->all( [] );
		if ( ! empty( $settings['disable_all'] ) ) {
			return [];
		}

		$disabled = isset( $settings['disabled_sizes'] ) && is_array( $settings['disabled_sizes'] )
			? $settings['disabled_sizes']
			: [];
		if ( $disabled === [] ) {
			return $sizes;
		}

		return array_values(
			array_filter(
				$sizes,
				static function ( array $size ) use ( $disabled ): bool {
					return ! in_array( $size['name'], $disabled, true );
				}
			)
		);
	}

	private function countImageAttachments(): int {
		$query = new \WP_Query(
			[
				'post_type'              => 'attachment',
				'post_status'            => 'inherit',
				'post_mime_type'         => 'image',
				'posts_per_page'         => 1,
				'fields'                 => 'ids',
				'no_found_rows'          => false,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			]
		);

		return (int) ( $query->found_posts ?? 0 );
	}

	private function findNextRegeneratableImageId( int $cursor ): int {
		global $wpdb;

		$search_cursor = $cursor;
		$attempts      = 0;

		while ( $attempts < self::CURSOR_ATTEMPTS ) {
			$id = (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT ID FROM {$wpdb->posts}
					WHERE post_type = 'attachment'
					AND post_status = 'inherit'
					AND post_mime_type LIKE %s
					AND ID > %d
					ORDER BY ID ASC
					LIMIT 1",
					$wpdb->esc_like( 'image/' ) . '%',
					$search_cursor
				)
			);

			if ( $id <= 0 ) {
				return 0;
			}

			if ( current_user_can( 'edit_post', $id ) ) {
				return $id;
			}

			$search_cursor = $id;
			++$attempts;
		}

		return 0;
	}

	/**
	 * @return array<int, array{name: string, width: int, height: int, crop: bool}>
	 */
	private function getRegisteredImageSizes(): array {
		static $cached = null;
		if ( is_array( $cached ) ) {
			return $cached;
		}

		global $_wp_additional_image_sizes;

		$sizes   = [];
		$default = [ 'thumbnail', 'medium', 'medium_large', 'large' ];

		foreach ( $default as $name ) {
			$sizes[] = [
				'name'   => $name,
				'width'  => (int) get_option( "{$name}_size_w", 0 ),
				'height' => (int) get_option( "{$name}_size_h", 0 ),
				'crop'   => (bool) get_option( "{$name}_crop", false ),
			];
		}

		if ( is_array( $_wp_additional_image_sizes ) ) {
			foreach ( $_wp_additional_image_sizes as $name => $data ) {
				$sizes[] = [
					'name'   => (string) $name,
					'width'  => (int) ( $data['width'] ?? 0 ),
					'height' => (int) ( $data['height'] ?? 0 ),
					'crop'   => (bool) ( $data['crop'] ?? false ),
				];
			}
		}

		$cached = $sizes;

		return $cached;
	}
}
