<?php
namespace DuplicatePostType;

use WP_PowerSuite\Core\AbstractModule;

class Module extends AbstractModule {

	private const MODULE_ID           = 'duplicate-post-type';
	private const MODULE_VERSION      = '1.0.2';
	private const MAX_DUPLICATE_COUNT = 20;
	private const MAX_BULK_DUPLICATE  = 50;

	/** @var array{enabled_post_types: list<string>, enable_bulk_duplicate: bool} */
	private array $settings = [];

	/** @var list<string> */
	private array $enabled_post_types = [];

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

	public function getName(): string {
		return __( 'Duplicate Post Type', 'wp-powersuite' );
	}

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

	public function getDescription(): string {
		return __( 'One-click duplication for posts, pages, and custom content types.', 'wp-powersuite' );
	}

	public function getContext(): string {
		return 'admin';
	}

	public function getDependencies(): array {
		return [];
	}

	protected function registerHooks(): void {
		$this->settings            = $this->getSettings();
		$this->enabled_post_types  = $this->settings['enabled_post_types'];

		if ( $this->enabled_post_types === [] ) {
			return;
		}

		add_action( 'admin_action_wpps_duplicate_post', [ $this, 'handleDuplicate' ] );
		add_action( 'admin_notices', [ $this, 'showDuplicateNotice' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueueScripts' ] );

		if ( in_array( 'post', $this->enabled_post_types, true ) ) {
			add_filter( 'post_row_actions', [ $this, 'addDuplicateLink' ], 10, 2 );
		}
		if ( in_array( 'page', $this->enabled_post_types, true ) ) {
			add_filter( 'page_row_actions', [ $this, 'addDuplicateLink' ], 10, 2 );
		}

		foreach ( $this->enabled_post_types as $post_type ) {
			if ( $post_type === 'post' || $post_type === 'page' ) {
				continue;
			}
			add_filter( "{$post_type}_row_actions", [ $this, 'addDuplicateLink' ], 10, 2 );
		}

		if ( ! empty( $this->settings['enable_bulk_duplicate'] ) ) {
			foreach ( $this->enabled_post_types as $post_type ) {
				add_filter( "bulk_actions-edit-{$post_type}", [ $this, 'addBulkAction' ] );
				add_filter( "handle_bulk_actions-edit-{$post_type}", [ $this, 'handleBulkDuplicate' ], 10, 3 );
			}
		}
	}

	public function enqueueScripts( string $hook = '' ): void {
		$screen = get_current_screen();
		if ( ! $screen || $screen->base !== 'edit' ) {
			return;
		}

		$post_type = is_string( $screen->post_type ) ? $screen->post_type : '';
		if ( $post_type === '' || ! $this->isPostTypeEnabled( $post_type ) ) {
			return;
		}

		wp_enqueue_script( 'jquery' );
		add_action( 'admin_footer', [ $this, 'addDuplicateModal' ] );
		wp_add_inline_script( 'jquery', $this->getInlineJS() );
		wp_add_inline_style( 'wp-admin', $this->getInlineCSS() );
	}

	public function addDuplicateModal(): void {
		?>
		<div id="wpps-duplicate-modal" class="wpps-duplicate-modal" style="display: none;">
			<div class="wpps-duplicate-modal__overlay"></div>
			<div class="wpps-duplicate-modal__content">
				<div class="wpps-duplicate-modal__header">
					<h3><?php esc_html_e( 'Duplicate Post', 'wp-powersuite' ); ?></h3>
					<button type="button" class="wpps-duplicate-modal__close" aria-label="<?php esc_attr_e( 'Close', 'wp-powersuite' ); ?>">
						<span class="dashicons dashicons-no-alt"></span>
					</button>
				</div>
				<div class="wpps-duplicate-modal__body">
					<label for="wpps-duplicate-count">
						<?php
						printf(
							/* translators: %d: maximum duplicate count */
							esc_html__( 'How many duplicates? (1-%d)', 'wp-powersuite' ),
							self::MAX_DUPLICATE_COUNT
						);
						?>
					</label>
					<input
						type="number"
						id="wpps-duplicate-count"
						class="wpps-duplicate-modal__input"
						min="1"
						max="<?php echo (int) self::MAX_DUPLICATE_COUNT; ?>"
						value="1"
					/>
					<p class="wpps-duplicate-modal__error" style="display: none; color: #d63638; margin: 10px 0 0 0;"></p>
				</div>
				<div class="wpps-duplicate-modal__footer">
					<button type="button" class="button wpps-duplicate-modal__cancel">
						<?php esc_html_e( 'Cancel', 'wp-powersuite' ); ?>
					</button>
					<button type="button" class="button button-primary wpps-duplicate-modal__confirm">
						<?php esc_html_e( 'Duplicate', 'wp-powersuite' ); ?>
					</button>
				</div>
			</div>
		</div>
		<?php
	}

	private function getInlineJS(): string {
		$max   = self::MAX_DUPLICATE_COUNT;
		$error = esc_js(
			sprintf(
				/* translators: %d: maximum duplicate count */
				__( 'Please enter a number between 1 and %d.', 'wp-powersuite' ),
				$max
			)
		);

		return "
		(function($) {
			var duplicateLink = null;
			var maxCount = {$max};

			$(document).ready(function() {
				$(document).on('click', 'a.wpps-duplicate-link', function(e) {
					e.preventDefault();
					duplicateLink = $(this).attr('href');
					$('#wpps-duplicate-modal').fadeIn(200);
					$('body').addClass('wp-powersuite-modal-open');
					$('#wpps-duplicate-count').val('1').focus().select();
					$('.wpps-duplicate-modal__error').hide();
				});

				$('.wpps-duplicate-modal__confirm').on('click', function() {
					var count = parseInt($('#wpps-duplicate-count').val(), 10);
					var errorEl = $('.wpps-duplicate-modal__error');

					if (isNaN(count) || count < 1 || count > maxCount) {
						errorEl.text('{$error}').show();
						$('#wpps-duplicate-count').focus().select();
						return;
					}

					if (duplicateLink) {
						var link = duplicateLink + (duplicateLink.indexOf('?') === -1 ? '?' : '&') + 'count=' + count;
						window.location.href = link;
					}
				});

				$('.wpps-duplicate-modal__cancel, .wpps-duplicate-modal__close, .wpps-duplicate-modal__overlay').on('click', function() {
					$('#wpps-duplicate-modal').fadeOut(200);
					$('body').removeClass('wp-powersuite-modal-open');
					duplicateLink = null;
				});

				$('#wpps-duplicate-count').on('keypress', function(e) {
					if (e.which === 13) {
						$('.wpps-duplicate-modal__confirm').click();
					}
				});
			});
		})(jQuery);
		";
	}

	private function getInlineCSS(): string {
		return '
		.wpps-duplicate-modal {
			position: fixed;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			z-index: 100000;
			display: flex;
			align-items: center;
			justify-content: center;
		}
		.wpps-duplicate-modal__overlay {
			position: fixed;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background: rgba(0, 0, 0, 0.7);
			z-index: 100000;
		}
		.wpps-duplicate-modal__content {
			position: relative;
			background: #fff;
			border-radius: 4px;
			box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
			max-width: 400px;
			width: 90%;
			z-index: 100001;
		}
		.wpps-duplicate-modal__header {
			display: flex;
			align-items: center;
			justify-content: space-between;
			padding: 20px;
			border-bottom: 1px solid #ddd;
		}
		.wpps-duplicate-modal__header h3 {
			margin: 0;
			font-size: 18px;
			font-weight: 600;
		}
		.wpps-duplicate-modal__close {
			background: none;
			border: none;
			padding: 0;
			cursor: pointer;
			color: #50575e;
			width: 32px;
			height: 32px;
			display: flex;
			align-items: center;
			justify-content: center;
			border-radius: 3px;
		}
		.wpps-duplicate-modal__close:hover {
			background: #f0f0f1;
			color: #2271b1;
		}
		.wpps-duplicate-modal__close .dashicons {
			font-size: 20px;
			width: 20px;
			height: 20px;
		}
		.wpps-duplicate-modal__body {
			padding: 20px;
		}
		.wpps-duplicate-modal__body label {
			display: block;
			margin-bottom: 10px;
			font-weight: 600;
			color: #1d2327;
		}
		.wpps-duplicate-modal__input {
			width: 100%;
			padding: 8px 12px;
			border: 1px solid #8c8f94;
			border-radius: 4px;
			font-size: 14px;
			box-sizing: border-box;
		}
		.wpps-duplicate-modal__input:focus {
			border-color: #2271b1;
			box-shadow: 0 0 0 1px #2271b1;
			outline: none;
		}
		.wpps-duplicate-modal__footer {
			display: flex;
			justify-content: flex-end;
			gap: 10px;
			padding: 15px 20px;
			border-top: 1px solid #ddd;
		}
		body.wp-powersuite-modal-open {
			overflow: hidden;
		}
		';
	}

	public function addDuplicateLink( array $actions, \WP_Post $post ): array {
		if ( ! $this->isPostTypeEnabled( $post->post_type ) || ! $this->userCanDuplicate( $post ) ) {
			return $actions;
		}

		$duplicate_url = wp_nonce_url(
			admin_url( 'admin.php?action=wpps_duplicate_post&post=' . $post->ID ),
			'wpps_duplicate_post_' . $post->ID,
			'wpps_duplicate_nonce'
		);

		$actions['wpps_duplicate'] = sprintf(
			'<a href="%s" class="wpps-duplicate-link" aria-label="%s">%s</a>',
			esc_url( $duplicate_url ),
			esc_attr( sprintf( __( 'Duplicate "%s"', 'wp-powersuite' ), _draft_or_post_title( $post ) ) ),
			esc_html__( 'Duplicate', 'wp-powersuite' )
		);

		return $actions;
	}

	public function addBulkAction( array $actions ): array {
		$actions['wpps_bulk_duplicate'] = __( 'Duplicate', 'wp-powersuite' );

		return $actions;
	}

	public function handleDuplicate(): void {
		if ( ! isset( $_GET['post'], $_GET['wpps_duplicate_nonce'] ) ) {
			wp_die( esc_html__( 'Invalid request.', 'wp-powersuite' ) );
		}

		$post_id = absint( wp_unslash( $_GET['post'] ) );
		$post    = get_post( $post_id );

		if ( ! $post instanceof \WP_Post ) {
			wp_die( esc_html__( 'Post not found.', 'wp-powersuite' ) );
		}

		$nonce = sanitize_text_field( wp_unslash( (string) $_GET['wpps_duplicate_nonce'] ) );
		if ( $nonce === '' || ! wp_verify_nonce( $nonce, 'wpps_duplicate_post_' . $post_id ) ) {
			wp_die( esc_html__( 'Security check failed.', 'wp-powersuite' ) );
		}

		if ( ! $this->userCanDuplicate( $post ) ) {
			wp_die( esc_html__( 'You do not have permission to duplicate this post.', 'wp-powersuite' ) );
		}

		if ( ! $this->isPostTypeEnabled( $post->post_type ) ) {
			wp_die( esc_html__( 'This post type cannot be duplicated.', 'wp-powersuite' ) );
		}

		$count = isset( $_GET['count'] ) ? absint( wp_unslash( $_GET['count'] ) ) : 1;
		$count = max( 1, min( self::MAX_DUPLICATE_COUNT, $count ) );

		$new_post_ids = [];
		for ( $i = 0; $i < $count; $i++ ) {
			$new_post_id = $this->duplicatePost( $post_id );
			if ( ! is_wp_error( $new_post_id ) ) {
				$new_post_ids[] = (int) $new_post_id;
			}
		}

		if ( $new_post_ids === [] ) {
			wp_die( esc_html__( 'Failed to duplicate post.', 'wp-powersuite' ) );
		}

		wp_safe_redirect(
			admin_url(
				'edit.php?post_type=' . rawurlencode( $post->post_type ) . '&duplicated=' . count( $new_post_ids )
			)
		);
		exit;
	}

	public function handleBulkDuplicate( string $redirect_to, string $action, array $post_ids ): string {
		if ( $action !== 'wpps_bulk_duplicate' || $post_ids === [] ) {
			return $redirect_to;
		}

		$post_ids = array_slice( array_map( 'absint', $post_ids ), 0, self::MAX_BULK_DUPLICATE );
		$duplicated_count = 0;

		foreach ( $post_ids as $post_id ) {
			$post = get_post( $post_id );
			if ( ! $post instanceof \WP_Post || ! $this->userCanDuplicate( $post ) || ! $this->isPostTypeEnabled( $post->post_type ) ) {
				continue;
			}

			$new_post_id = $this->duplicatePost( $post_id );
			if ( ! is_wp_error( $new_post_id ) ) {
				++$duplicated_count;
			}
		}

		return add_query_arg( 'bulk_duplicated', $duplicated_count, $redirect_to );
	}

	private function duplicatePost( int $post_id ): int|\WP_Error {
		$post = get_post( $post_id );

		if ( ! $post instanceof \WP_Post ) {
			return new \WP_Error( 'post_not_found', __( 'Post not found.', 'wp-powersuite' ) );
		}

		if ( ! $this->userCanDuplicate( $post ) ) {
			return new \WP_Error( 'forbidden', __( 'You do not have permission to duplicate this post.', 'wp-powersuite' ) );
		}

		$post_data = [
			'post_title'   => $post->post_title . ' (Copy)',
			'post_content' => $post->post_content,
			'post_excerpt' => $post->post_excerpt,
			'post_status'  => 'draft',
			'post_type'    => $post->post_type,
			'post_author'  => get_current_user_id(),
			'post_parent'  => (int) $post->post_parent,
			'menu_order'   => (int) $post->menu_order,
			'post_name'    => '',
		];

		remove_action( 'save_post', 'wp_save_post_revision', 10 );
		$new_post_id = wp_insert_post( wp_slash( $post_data ), true );
		add_action( 'save_post', 'wp_save_post_revision', 10 );

		if ( is_wp_error( $new_post_id ) ) {
			return $new_post_id;
		}

		$this->duplicatePostMeta( $post_id, (int) $new_post_id );
		$this->duplicateTaxonomies( $post_id, (int) $new_post_id );
		$this->duplicateFeaturedImage( $post_id, (int) $new_post_id );

		return (int) $new_post_id;
	}

	private function duplicatePostMeta( int $old_post_id, int $new_post_id ): void {
		$meta = get_post_meta( $old_post_id );
		if ( ! is_array( $meta ) || $meta === [] ) {
			return;
		}

		$excluded_meta = [
			'_wp_old_slug',
			'_edit_lock',
			'_edit_last',
			'_wp_old_date',
			'_thumbnail_id',
		];

		foreach ( $meta as $key => $values ) {
			if ( ! is_string( $key ) || in_array( $key, $excluded_meta, true ) ) {
				continue;
			}
			if ( str_starts_with( $key, '_oembed_' ) ) {
				continue;
			}

			foreach ( (array) $values as $value ) {
				$value = maybe_unserialize( $value );
				$value = apply_filters( 'wps_duplicate_post_meta', $value, $old_post_id, $key );
				add_post_meta( $new_post_id, $key, $value );
			}
		}
	}

	private function duplicateTaxonomies( int $old_post_id, int $new_post_id ): void {
		$post = get_post( $old_post_id );
		if ( ! $post instanceof \WP_Post ) {
			return;
		}

		$taxonomies = get_object_taxonomies( $post->post_type );
		if ( $taxonomies === [] ) {
			return;
		}

		foreach ( $taxonomies as $taxonomy ) {
			$terms = wp_get_object_terms( $old_post_id, $taxonomy, [ 'fields' => 'ids' ] );
			if ( ! is_wp_error( $terms ) && $terms !== [] ) {
				wp_set_object_terms( $new_post_id, $terms, $taxonomy );
			}
		}
	}

	private function duplicateFeaturedImage( int $old_post_id, int $new_post_id ): void {
		$thumbnail_id = (int) get_post_thumbnail_id( $old_post_id );
		if ( $thumbnail_id > 0 ) {
			set_post_thumbnail( $new_post_id, $thumbnail_id );
		}
	}

	private function isPostTypeEnabled( string $post_type ): bool {
		return in_array( $post_type, $this->enabled_post_types, true );
	}

	private function userCanDuplicate( \WP_Post $post ): bool {
		if ( ! current_user_can( 'edit_post', $post->ID ) ) {
			return false;
		}

		$post_type_object = get_post_type_object( $post->post_type );

		return $post_type_object instanceof \WP_Post_Type
			&& current_user_can( $post_type_object->cap->create_posts );
	}

	public function showDuplicateNotice(): void {
		$screen = get_current_screen();
		if ( ! $screen || $screen->base !== 'edit' ) {
			return;
		}

		$list_post_type = is_string( $screen->post_type ) ? $screen->post_type : '';
		if ( $list_post_type === '' || ! $this->isPostTypeEnabled( $list_post_type ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only admin notice
		if ( isset( $_GET['duplicated'] ) ) {
			$count = absint( wp_unslash( $_GET['duplicated'] ) );
			if ( $count > 0 ) {
				printf(
					'<div class="notice notice-success is-dismissible"><p>%s</p></div>',
					esc_html(
						sprintf(
							/* translators: %d: number of duplicated posts */
							_n( '%d post duplicated successfully.', '%d posts duplicated successfully.', $count, 'wp-powersuite' ),
							$count
						)
					)
				);
			}
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only admin notice
		if ( isset( $_GET['bulk_duplicated'] ) ) {
			$count = absint( wp_unslash( $_GET['bulk_duplicated'] ) );
			if ( $count > 0 ) {
				printf(
					'<div class="notice notice-success is-dismissible"><p>%s</p></div>',
					esc_html(
						sprintf(
							/* translators: %d: number of duplicated posts */
							_n( '%d post duplicated successfully.', '%d posts duplicated successfully.', $count, 'wp-powersuite' ),
							$count
						)
					)
				);
			}
		}
	}

	/**
	 * @return array{enabled_post_types: list<string>, enable_bulk_duplicate: bool}
	 */
	private function getSettings(): array {
		$raw = $this->moduleSettings()->all( [] );

		return self::normalizeSettings( is_array( $raw ) ? $raw : [] );
	}

	/**
	 * @return array{enabled_post_types: list<string>, enable_bulk_duplicate: bool}
	 */
	public static function getDefaultSettings(): array {
		return [
			'enabled_post_types'    => [ 'post', 'page' ],
			'enable_bulk_duplicate' => false,
		];
	}

	/**
	 * @return list<string>
	 */
	public static function getExcludedPostTypes(): array {
		return [ 'attachment', 'revision', 'nav_menu_item' ];
	}

	/**
	 * @return list<string>
	 */
	public static function getAllowedPostTypes(): array {
		return array_values(
			array_diff(
				get_post_types( [ 'public' => true, 'show_ui' => true ], 'names' ),
				self::getExcludedPostTypes()
			)
		);
	}

	/**
	 * @param array<string, mixed> $raw
	 * @return array{enabled_post_types: list<string>, enable_bulk_duplicate: bool}
	 */
	public static function normalizeSettings( array $raw ): array {
		$defaults = self::getDefaultSettings();
		$allow    = array_flip( self::getAllowedPostTypes() );
		$types    = [];

		if ( isset( $raw['post_type_mode'] ) && (string) $raw['post_type_mode'] === 'all' ) {
			$types = self::getAllowedPostTypes();
		} elseif ( isset( $raw['enabled_post_types'] ) && is_array( $raw['enabled_post_types'] ) ) {
			$types = $raw['enabled_post_types'];
		}

		$clean = [];
		foreach ( $types as $slug ) {
			$key = sanitize_key( (string) $slug );
			if ( $key !== '' && isset( $allow[ $key ] ) && post_type_exists( $key ) ) {
				$clean[] = $key;
			}
		}

		if ( $clean === [] ) {
			$clean = $defaults['enabled_post_types'];
		}

		return [
			'enabled_post_types'    => array_values( array_unique( $clean ) ),
			'enable_bulk_duplicate' => ! empty( $raw['enable_bulk_duplicate'] ),
		];
	}
}
