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

namespace WP_PowerSuite\CLI;

use WP_PowerSuite\CLI\Support\Cli;

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

/**
 * List, export, and import redirects.
 *
 * Reuses the Redirect Manager module's own CSV import/export engine.
 *
 * @since 1.0.7
 */
final class RedirectsCommand {

	/**
	 * List configured redirects.
	 *
	 * ## OPTIONS
	 *
	 * [--fields=<fields>]
	 * : Comma-separated list of fields to display.
	 * ---
	 * default: source,target,type,match_type,active,hits
	 * ---
	 *
	 * [--format=<format>]
	 * : Render output in a particular format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - json
	 *   - yaml
	 *   - count
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp powersuite redirects list
	 *     $ wp powersuite redirects list --format=json
	 *
	 * @subcommand list
	 * @when after_wp_load
	 *
	 * @param array<int, string>   $args       Positional arguments (unused).
	 * @param array<string, mixed> $assoc_args Associative arguments.
	 * @return void
	 */
	public function list_( array $args, array $assoc_args ): void {
		$module    = $this->module();
		$redirects = $module->getAllRedirects();

		$rows = [];
		foreach ( $redirects as $r ) {
			$rows[] = [
				'source'     => (string) ( $r['source'] ?? '' ),
				'target'     => (string) ( $r['target'] ?? '' ),
				'type'       => (string) ( $r['type'] ?? '301' ),
				'match_type' => (string) ( $r['match_type'] ?? 'exact' ),
				'active'     => Cli::yesNo( ! empty( $r['active'] ) ),
				'hits'       => (int) ( $r['hits'] ?? 0 ),
			];
		}

		$fields    = [ 'source', 'target', 'type', 'match_type', 'active', 'hits' ];
		$formatter = new \WP_CLI\Formatter( $assoc_args, $fields );
		$formatter->display_items( $rows );
	}

	/**
	 * Export redirects to a CSV file.
	 *
	 * ## OPTIONS
	 *
	 * <file>
	 * : Destination CSV path.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp powersuite redirects export redirects.csv
	 *
	 * @when after_wp_load
	 *
	 * @param array<int, string>   $args       Positional arguments.
	 * @param array<string, mixed> $assoc_args Associative arguments.
	 * @return void
	 */
	public function export( array $args, array $assoc_args ): void {
		$file = isset( $args[0] ) ? (string) $args[0] : '';
		if ( $file === '' ) {
			\WP_CLI::error( __( 'Provide a destination CSV path.', 'wp-powersuite' ) );
		}

		$dir = dirname( $file );
		if ( ! is_dir( $dir ) || ! wp_is_writable( $dir ) ) {
			\WP_CLI::error(
				sprintf(
					/* translators: %s: directory path */
					__( 'Directory is not writable: %s', 'wp-powersuite' ),
					$dir
				)
			);
		}

		$csv = $this->module()->exportRedirectsToCsvString();

		// Owner-only create before write (same hardening as settings export).
		$handle = @fopen( $file, 'cb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- intentional private create.
		if ( $handle === false ) {
			\WP_CLI::error(
				sprintf(
					/* translators: %s: file path */
					__( 'Failed to write CSV to: %s', 'wp-powersuite' ),
					$file
				)
			);
		}
		if ( function_exists( 'fchmod' ) ) {
			@fchmod( $handle, 0600 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- best-effort; not all filesystems support fchmod.
		} elseif ( function_exists( 'chmod' ) ) {
			@chmod( $file, 0600 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- best-effort hardening.
		}
		if ( ! flock( $handle, LOCK_EX ) || ftruncate( $handle, 0 ) === false || fwrite( $handle, $csv ) === false ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- CLI export to a user-specified path.
			flock( $handle, LOCK_UN );
			fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- pairs with fopen() above.
			\WP_CLI::error(
				sprintf(
					/* translators: %s: file path */
					__( 'Failed to write CSV to: %s', 'wp-powersuite' ),
					$file
				)
			);
		}
		fflush( $handle );
		flock( $handle, LOCK_UN );
		fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- pairs with fopen() above.

		\WP_CLI::success(
			sprintf(
				/* translators: %s: file path */
				__( 'Redirects exported to %s', 'wp-powersuite' ),
				$file
			)
		);
	}

	/**
	 * Import redirects from a CSV file.
	 *
	 * Columns: source,target,type,match_type,notes (header row optional).
	 * Invalid, unsafe-regex, and duplicate rows are skipped.
	 *
	 * ## OPTIONS
	 *
	 * <file>
	 * : Path to the CSV file to import.
	 *
	 * [--dry-run]
	 * : Validate and count without writing any redirects.
	 *
	 * ## EXAMPLES
	 *
	 *     $ wp powersuite redirects import redirects.csv --dry-run
	 *     $ wp powersuite redirects import redirects.csv
	 *
	 * @when after_wp_load
	 *
	 * @param array<int, string>   $args       Positional arguments.
	 * @param array<string, mixed> $assoc_args Associative arguments.
	 * @return void
	 */
	public function import( array $args, array $assoc_args ): void {
		$file = isset( $args[0] ) ? (string) $args[0] : '';
		if ( $file === '' ) {
			\WP_CLI::error( __( 'Provide the path to a CSV file.', 'wp-powersuite' ) );
		}
		if ( ! is_file( $file ) || ! is_readable( $file ) ) {
			\WP_CLI::error(
				sprintf(
					/* translators: %s: file path */
					__( 'File not found or not readable: %s', 'wp-powersuite' ),
					$file
				)
			);
		}

		$size = filesize( $file );
		if ( $size === false || $size > 10 * MB_IN_BYTES ) {
			\WP_CLI::error(
				$size === false
					? __( 'Unable to determine CSV file size. Import aborted.', 'wp-powersuite' )
					: __( 'CSV file is too large (limit is 10 MB). Split the import into smaller files.', 'wp-powersuite' )
			);
		}

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_get_contents
		$csv = file_get_contents( $file );
		if ( $csv === false ) {
			\WP_CLI::error( __( 'Unable to read the CSV file.', 'wp-powersuite' ) );
		}

		$dry_run = (bool) \WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run', false );
		$result  = $this->module()->importRedirectsFromCsvString( (string) $csv, $dry_run );

		if ( ! empty( $result['error'] ) ) {
			$code     = (string) $result['error'];
			$messages = [
				'too_many' => __( 'Too many rows in the CSV (limit is 1000). Split the import into smaller files.', 'wp-powersuite' ),
				'empty'    => __( 'The CSV file is empty.', 'wp-powersuite' ),
			];
			\WP_CLI::error(
				$messages[ $code ] ?? sprintf(
					/* translators: %s: error code */
					__( 'Import failed: %s', 'wp-powersuite' ),
					$code
				)
			);
		}

		$message = sprintf(
			/* translators: 1: imported count, 2: skipped count */
			__( '%1$d redirect(s) %3$s, %2$d skipped.', 'wp-powersuite' ),
			(int) ( $result['imported'] ?? 0 ),
			(int) ( $result['skipped'] ?? 0 ),
			$dry_run ? __( 'would be imported', 'wp-powersuite' ) : __( 'imported', 'wp-powersuite' )
		);

		if ( $dry_run ) {
			\WP_CLI::log( $message );
			\WP_CLI::success( __( 'Dry run complete. No changes were made.', 'wp-powersuite' ) );
			return;
		}

		\WP_CLI::success( $message );
	}

	/**
	 * Load and return the Redirect Manager module instance.
	 *
	 * @return \RedirectManager\Module
	 */
	private function module(): \RedirectManager\Module {
		if ( ! Cli::isEnabled( 'redirect-manager' ) ) {
			\WP_CLI::error( __( 'The Redirect Manager module is not enabled. Enable it first: wp powersuite module enable redirect-manager', 'wp-powersuite' ) );
		}

		if ( ! class_exists( '\\RedirectManager\\Module' ) ) {
			$module_file = WP_POWERSUITE_MODULES_DIR . 'redirect-manager' . DIRECTORY_SEPARATOR . 'module.php';
			if ( is_readable( $module_file ) ) {
				require_once $module_file;
			}
		}

		if ( ! class_exists( '\\RedirectManager\\Module' ) ) {
			\WP_CLI::error( __( 'Unable to load the Redirect Manager module.', 'wp-powersuite' ) );
		}

		return new \RedirectManager\Module();
	}
}
