<?php

namespace WPForms\Pro\Migrations;

use WPForms\Migrations\UpgradeBase;
use WPForms\Helpers\File;
use WPForms\Pro\Robots;

/**
 * Class upgrade for 1.9.1 release.
 *
 * @since 1.9.1
 */
class Upgrade1_9_1 extends UpgradeBase {

	/**
	 * The "Disallow" line of WPForms rule.
	 *
	 * @since 1.9.1
	 *
	 * @var string
	 */
	private $disallow_line = '';

	/**
	 * Path to the robots.txt file.
	 *
	 * @since 1.9.1
	 *
	 * @var string
	 */
	const ROBOTS_TXT_PATH = ABSPATH . 'robots.txt';

	/**
	 * Attempt to resolve a possible issue with the physical robots.txt file.
	 *
	 * @since 1.9.1
	 *
	 * @return bool|null Upgrade result:
	 *                    true - the upgrade completed successfully,
	 *                    false - in the case of failure,
	 *                    null - upgrade started but not yet finished (background task).
	 */
	public function run() {

		$this->run_robots_migration();

		return true;
	}

	/**
	 * Attempt to resolve a possible issue with the physical robots.txt file.
	 *
	 * @since 1.9.1
	 *
	 * @return void
	 */
	private function run_robots_migration() {

		// There is no physical robots.txt file.
		// Complete the migration silently.
		if ( ! File::exists( self::ROBOTS_TXT_PATH ) ) {
			return;
		}

		// Get the robots.txt content.
		$content = File::get_contents( self::ROBOTS_TXT_PATH );

		// The file cannot be read.
		if ( $content === false ) {
			wpforms_log(
				'robots.txt',
				'The file could not be read. This is usually due to file permissions.',
				[
					'type'  => 'log',
					'force' => true,
				]
			);

			return;
		}

		// The file is empty.
		// Complete the migration silently.
		if ( empty( $content ) ) {
			return;
		}

		$robots_instance = new Robots();

		// Everything is okay.
		// The User-agent line exists above the Disallow line.
		if ( strpos( $content, $robots_instance->get_rule_block( false ) ) !== false ) {
			return;
		}

		$this->disallow_line = 'Disallow: ' . $robots_instance->get_upload_root();

		/**
		 * Caused by third-party plugin.
		 * There are a couple of cases when the WPForms rule makes the robots.txt file invalid.
		 * We attempt to resolve them through this migration.
		 */

		// 1) The Disallow line is placed in the first line on the file.
		if ( strpos( $content, $this->disallow_line ) === 0 ) {
			$success = $this->replace_rule( ltrim( $robots_instance->get_rule_block(), PHP_EOL ), $content );

			wpforms_log(
				'robots.txt',
				$success ? 'WPForms rule block has been corrected.' : 'WPForms rule block could not be corrected.',
				[
					'type'  => 'log',
					'force' => true,
				]
			);

			return;
		}

		// This is a content of the line above the Disallow line.
		$above_line = $this->get_above_disallow_line( $content );

		// 2) The line above the Disallow line is empty, or it's a comment.
		if (
			is_string( $above_line ) &&
			( $above_line === '' || strpos( $above_line, '#' ) === 0 )
		) {
			$success = $this->replace_rule( $robots_instance->get_rule_block(), $content );

			wpforms_log(
				'robots.txt',
				$success ? 'WPForms rule block has been corrected.' : 'WPForms rule block could not be corrected.',
				[
					'type'  => 'log',
					'force' => true,
				]
			);
		}
	}

	/**
	 * Replace a single WPForms disallow line by the whole rule block.
	 *
	 * @since 1.9.1
	 *
	 * @param string $replace The replacement value that replaces found search values.
	 * @param string $content The string being searched and replaced on.
	 *
	 * @return bool True on success, false on failure.
	 */
	private function replace_rule( string $replace, string $content ): bool {

		// Create a backup for the current version of the robots.txt file.
		// Better safe than sorry.
		File::copy( self::ROBOTS_TXT_PATH, ABSPATH . sprintf( 'robots-backup-%s.txt', time() ) );

		// Replace a single disallowed line by the whole rule block.
		$new_content = str_replace( $this->disallow_line, $replace, $content );

		// Update the robots.txt file.
		return File::put_contents( self::ROBOTS_TXT_PATH, $new_content );
	}

	/**
	 * Retrieve a line above the Disallow line.
	 *
	 * @since 1.9.1
	 *
	 * @param string $content File content.
	 *
	 * @return false|string
	 */
	private function get_above_disallow_line( string $content ) {

		$lines                  = array_map( 'trim', explode( PHP_EOL, $content ) );
		$disallow_line_position = array_search( $this->disallow_line, $lines, true );

		// There is no WPForms rule in the robots.txt file.
		// Even though it's an unexpected case, most likely the user decided to remove WPForms rule, for some reason.
		// We respect it and won't do anything in this case.
		if ( ! $disallow_line_position ) {
			return false;
		}

		// This is a content of the line above the Disallow line.
		return $lines[ $disallow_line_position - 1 ];
	}
}
