<?php
/**
 * `wp powersuite broken-links` commands.
 *
 * @package WP_PowerSuite\CLI
 * @since 1.0.7
 */

namespace WP_PowerSuite\CLI;

use WP_PowerSuite\CLI\Support\Cli;

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

/**
 * Scan published content for broken links.
 *
 * Reuses the module's SSRF-safe HTTP checker and batching engine; no scanning
 * logic is duplicated here.
 *
 * @since 1.0.7
 */
final class BrokenLinksCommand {

	/**
	 * Run a broken-link scan to completion.
	 *
	 * ## OPTIONS
	 *
	 * [--post-type=<types>]
	 * : Comma-separated post types to scan (default: the module's configured types).
	 *
	 * [--limit=<n>]
	 * : Stop after scanning approximately this many posts.
	 *
	 * [--format=<format>]
	 * : Output format for the broken-link report.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - json
	 *   - yaml
	 *   - count
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp powersuite broken-links scan
	 *     $ wp powersuite broken-links scan --post-type=post,page --limit=200
	 *
	 * @when after_wp_load
	 *
	 * @param array<int, string>   $args       Positional arguments (unused).
	 * @param array<string, mixed> $assoc_args Associative arguments.
	 * @return void
	 */
	public function scan( array $args, array $assoc_args ): void {
		$module = $this->module();

		$post_types = null;
		$raw_types  = \WP_CLI\Utils\get_flag_value( $assoc_args, 'post-type', '' );
		if ( is_string( $raw_types ) && $raw_types !== '' ) {
			$post_types = array_values( array_filter( array_map( 'trim', explode( ',', $raw_types ) ) ) );
		}

		$limit = (int) \WP_CLI\Utils\get_flag_value( $assoc_args, 'limit', 0 );

		$result = $module->runScanAction( 'start', $post_types );
		if ( is_wp_error( $result ) ) {
			\WP_CLI::error( $result->get_error_message() );
		}

		$scan_id  = (string) ( $result['scan_id'] ?? '' );
		$total    = (int) ( $result['total_posts'] ?? 0 );
		$progress = \WP_CLI\Utils\make_progress_bar( __( 'Scanning links', 'wp-powersuite' ), max( 1, $total ) );

		$guard = 0;
		while ( ! empty( $result['has_more'] ) && $guard < 100000 ) {
			++$guard;

			$scanned = (int) ( $result['posts_scanned'] ?? 0 );
			if ( $limit > 0 && $scanned >= $limit ) {
				$module->runScanAction( 'cancel', null, $scan_id );
				/* translators: %d: number value. */
				\WP_CLI::log( sprintf( __( 'Reached --limit=%d; stopping early.', 'wp-powersuite' ), $limit ) );
				break;
			}

			$result = $module->runScanAction( 'continue', null, $scan_id );
			if ( is_wp_error( $result ) ) {
				$progress->finish();
				\WP_CLI::error( $result->get_error_message() );
			}
			$progress->tick();
		}

		$progress->finish();

		$broken = is_array( $result['broken_links'] ?? null ) ? $result['broken_links'] : [];
		$rows   = [];
		foreach ( $broken as $entry ) {
			$rows[] = [
				'url'    => (string) ( $entry['url'] ?? '' ),
				'status' => (string) ( $entry['status'] ?? ( $entry['code'] ?? '' ) ),
				'post'   => (string) ( $entry['post_title'] ?? '' ),
				'post_id'=> (int) ( $entry['post_id'] ?? 0 ),
			];
		}

		\WP_CLI::log(
			sprintf(
				/* translators: 1: posts scanned, 2: urls checked, 3: broken count */
				__( 'Scanned %1$d post(s), checked %2$d URL(s), found %3$d broken link(s).', 'wp-powersuite' ),
				(int) ( $result['posts_scanned'] ?? 0 ),
				(int) ( $result['total_urls'] ?? 0 ),
				count( $rows )
			)
		);

		if ( $rows === [] ) {
			\WP_CLI::success( __( 'No broken links found.', 'wp-powersuite' ) );
			return;
		}

		$format = Cli::format( $assoc_args, [ 'table', 'csv', 'json', 'yaml', 'count' ], 'table' );
		\WP_CLI\Utils\format_items( $format, $rows, [ 'url', 'status', 'post', 'post_id' ] );
	}

	/**
	 * Report whether a broken-link scan is currently in progress.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp powersuite broken-links status
	 *
	 * @when after_wp_load
	 *
	 * @param array<int, string>   $args       Positional arguments (unused).
	 * @param array<string, mixed> $assoc_args Associative arguments.
	 * @return void
	 */
	public function status( array $args, array $assoc_args ): void {
		if ( ! Cli::isEnabled( 'broken-link-checker' ) ) {
			\WP_CLI::error( __( 'The Broken Link Checker module is not enabled.', 'wp-powersuite' ) );
		}

		global $wpdb;
		$like = $wpdb->esc_like( '_transient_wpps_blc_scan_lock_' ) . '%';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$locks = (int) $wpdb->get_var(
			$wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s", $like )
		);

		if ( $locks > 0 ) {
			\WP_CLI::log( __( 'A broken-link scan is currently in progress.', 'wp-powersuite' ) );
		} else {
			\WP_CLI::log( __( 'No broken-link scan is currently in progress.', 'wp-powersuite' ) );
		}

		\WP_CLI::success( __( 'OK', 'wp-powersuite' ) );
	}

	/**
	 * Load and return the Broken Link Checker module instance.
	 *
	 * @return \BrokenLinkChecker\Module
	 */
	private function module(): \BrokenLinkChecker\Module {
		if ( ! Cli::isEnabled( 'broken-link-checker' ) ) {
			\WP_CLI::error( __( 'The Broken Link Checker module is not enabled. Enable it first: wp powersuite module enable broken-link-checker', 'wp-powersuite' ) );
		}

		if ( ! class_exists( '\\BrokenLinkChecker\\Module' ) ) {
			$module_file = WP_POWERSUITE_MODULES_DIR . 'broken-link-checker' . DIRECTORY_SEPARATOR . 'module.php';
			if ( is_readable( $module_file ) ) {
				require_once $module_file;
			}
		}

		if ( ! class_exists( '\\BrokenLinkChecker\\Module' ) ) {
			\WP_CLI::error( __( 'Unable to load the Broken Link Checker module.', 'wp-powersuite' ) );
		}

		return new \BrokenLinkChecker\Module();
	}
}

