<?php
/**
 * Applies fixes to post content: replace a URL, unlink, dismiss.
 *
 * @package 404Zen
 */

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

class LGBF_Fixer {

	/**
	 * Replaces a link's URL inside the source post and re-queues it for checking.
	 *
	 * @param int    $link_id Row ID in the links table.
	 * @param string $new_url Replacement URL.
	 * @return true|WP_Error
	 */
	public static function edit_url( $link_id, $new_url ) {
		$row = LGBF_Links_Store::get( $link_id );
		if ( ! $row ) {
			return new WP_Error( 'lgbf_not_found', __( 'Link not found.', '404zen-broken-link-fixer' ) );
		}

		$new_url = esc_url_raw( trim( $new_url ) );
		if ( ! $new_url || ! preg_match( '#^https?://#i', $new_url ) ) {
			return new WP_Error( 'lgbf_bad_url', __( 'Please enter a valid http(s) URL.', '404zen-broken-link-fixer' ) );
		}

		$post = get_post( (int) $row['post_id'] );
		if ( ! $post ) {
			return new WP_Error( 'lgbf_no_post', __( 'The source post no longer exists.', '404zen-broken-link-fixer' ) );
		}

		$updated_content = self::replace_url_in_content( $post->post_content, $row['url'], $new_url );
		if ( $updated_content === $post->post_content ) {
			return new WP_Error( 'lgbf_not_replaced', __( 'The URL could not be found in the post content.', '404zen-broken-link-fixer' ) );
		}

		$result = wp_update_post(
			array(
				'ID'           => $post->ID,
				'post_content' => $updated_content,
			),
			true
		);
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		// save_post re-syncs the link table for this post; the new URL enters as pending.
		add_option( 'lgbf_first_fix_at', time(), '', false );
		return true;
	}

	/**
	 * Replaces every occurrence of a URL in href/src attributes.
	 *
	 * Handles both the raw URL and its entity-encoded form (&amp;).
	 *
	 * @param string $content Post content.
	 * @param string $old_url URL currently in the content.
	 * @param string $new_url Replacement.
	 * @return string
	 */
	public static function replace_url_in_content( $content, $old_url, $new_url ) {
		$old_encoded = str_replace( '&', '&amp;', $old_url );
		$new_encoded = str_replace( '&', '&amp;', $new_url );

		$variants = array( $old_url => $new_url );
		if ( $old_encoded !== $old_url ) {
			$variants[ $old_encoded ] = $new_encoded;
		}

		foreach ( $variants as $needle => $replacement ) {
			foreach ( array( '"', "'" ) as $quote ) {
				$content = str_replace(
					array( 'href=' . $quote . $needle . $quote, 'src=' . $quote . $needle . $quote ),
					array( 'href=' . $quote . $replacement . $quote, 'src=' . $quote . $replacement . $quote ),
					$content
				);
			}
		}

		return $content;
	}

	/**
	 * Removes the <a> wrapper (keeping the anchor text) or the <img> tag.
	 *
	 * @param int $link_id Row ID in the links table.
	 * @return true|WP_Error
	 */
	public static function unlink( $link_id ) {
		$row = LGBF_Links_Store::get( $link_id );
		if ( ! $row ) {
			return new WP_Error( 'lgbf_not_found', __( 'Link not found.', '404zen-broken-link-fixer' ) );
		}

		$post = get_post( (int) $row['post_id'] );
		if ( ! $post ) {
			return new WP_Error( 'lgbf_no_post', __( 'The source post no longer exists.', '404zen-broken-link-fixer' ) );
		}

		$updated_content = self::remove_link_from_content( $post->post_content, $row['url'], $row['link_type'] );
		if ( $updated_content === $post->post_content ) {
			return new WP_Error( 'lgbf_not_replaced', __( 'The link could not be found in the post content.', '404zen-broken-link-fixer' ) );
		}

		$result = wp_update_post(
			array(
				'ID'           => $post->ID,
				'post_content' => $updated_content,
			),
			true
		);
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}

	/**
	 * Strips <a href="url">text</a> down to "text", or removes <img src="url">.
	 *
	 * @param string $content   Post content.
	 * @param string $url       Target URL.
	 * @param string $link_type link|image.
	 * @return string
	 */
	public static function remove_link_from_content( $content, $url, $link_type ) {
		$candidates = array( $url );
		$encoded    = str_replace( '&', '&amp;', $url );
		if ( $encoded !== $url ) {
			$candidates[] = $encoded;
		}

		foreach ( $candidates as $candidate ) {
			$quoted = preg_quote( $candidate, '#' );

			if ( 'image' === $link_type ) {
				$content = preg_replace( '#<img\s[^>]*?src\s*=\s*(["\'])' . $quoted . '\1[^>]*/?>#is', '', $content );
			} else {
				$content = preg_replace( '#<a\s[^>]*?href\s*=\s*(["\'])' . $quoted . '\1[^>]*>(.*?)</a>#is', '$2', $content );
			}
		}

		return $content;
	}

	/**
	 * Marks a link dismissed so it is skipped by future cycles and digests.
	 *
	 * @param int $link_id Row ID.
	 * @return true|WP_Error
	 */
	public static function dismiss( $link_id ) {
		global $wpdb;

		$row = LGBF_Links_Store::get( $link_id );
		if ( ! $row ) {
			return new WP_Error( 'lgbf_not_found', __( 'Link not found.', '404zen-broken-link-fixer' ) );
		}

		$wpdb->update(
			LGBF_Links_Store::table(),
			array( 'status' => 'dismissed' ),
			array( 'id' => (int) $link_id ),
			array( '%s' ),
			array( '%d' )
		);

		return true;
	}

	/**
	 * Reverses a dismissal: requeues the link and rechecks it immediately so
	 * it reappears with its real status (used by the inline Undo).
	 *
	 * @param int $link_id Row ID.
	 * @return array|WP_Error Check result.
	 */
	public static function undismiss( $link_id ) {
		global $wpdb;

		$row = LGBF_Links_Store::get( $link_id );
		if ( ! $row ) {
			return new WP_Error( 'lgbf_not_found', __( 'Link not found.', '404zen-broken-link-fixer' ) );
		}

		$wpdb->update(
			LGBF_Links_Store::table(),
			array( 'status' => 'pending' ),
			array( 'id' => (int) $link_id ),
			array( '%s' ),
			array( '%d' )
		);

		return self::recheck( $link_id );
	}

	/**
	 * Rechecks a single link immediately.
	 *
	 * @param int $link_id Row ID.
	 * @return array|WP_Error Check result.
	 */
	public static function recheck( $link_id ) {
		$row = LGBF_Links_Store::get( $link_id );
		if ( ! $row ) {
			return new WP_Error( 'lgbf_not_found', __( 'Link not found.', '404zen-broken-link-fixer' ) );
		}

		$result = LGBF_Checker::check( $row['url'] );
		LGBF_Links_Store::apply_check_result( $row['url_hash'], $result );

		return $result;
	}
}
