<?php
/**
 * Import/Export SEO data.
 *
 * Exports all global settings and per-page meta as JSON.
 * Imports with automatic URL replacement (source domain → current domain).
 * Posts are matched by slug during import.
 *
 * @package Dennis_SEO
 */

defined( 'ABSPATH' ) || exit;

class Dennis_SEO_Import_Export {

	/**
	 * Meta keys — references DENNIS_SEO_META_KEYS from dennis-seo.php.
	 */

	/**
	 * Yoast meta keys that can be migrated, mapped to Dennis equivalents.
	 */
	const YOAST_TO_DENNIS = array(
		'_yoast_wpseo_title'              => '_dennis_seo_title',
		'_yoast_wpseo_metadesc'           => '_dennis_seo_description',
		'_yoast_wpseo_focuskw'            => '_dennis_seo_keyphrase',
		'_yoast_wpseo_canonical'          => '_dennis_seo_canonical',
		'_yoast_wpseo_opengraph-title'    => '_dennis_seo_og_title',
		'_yoast_wpseo_opengraph-description' => '_dennis_seo_og_description',
		'_yoast_wpseo_opengraph-image'    => '_dennis_seo_og_image',
	);

	public function __construct() {
		add_action( 'admin_post_dennis_seo_export', array( $this, 'handle_export' ) );
		add_action( 'admin_post_dennis_seo_import', array( $this, 'handle_import' ) );
		add_action( 'admin_post_dennis_seo_yoast_migrate', array( $this, 'handle_yoast_migrate' ) );
		add_action( 'admin_post_dennis_seo_clear_fields', array( $this, 'handle_clear_fields' ) );
	}

	/**
	 * Clear all Dennis GEO per-page meta fields.
	 */
	public function handle_clear_fields(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Unauthorized', 'dennis-geo' ) );
		}

		if ( ! isset( $_POST['dennis_seo_clear_nonce'] ) ||
			! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['dennis_seo_clear_nonce'] ) ), 'dennis_seo_clear_fields' ) ) {
			wp_die( esc_html__( 'Invalid nonce', 'dennis-geo' ) );
		}

		$confirm = isset( $_POST['dseo_clear_confirm'] ) ? sanitize_text_field( wp_unslash( $_POST['dseo_clear_confirm'] ) ) : '';
		if ( 'CLEAR' !== $confirm ) {
			$this->redirect_with_message( 'error', __( 'Type CLEAR to confirm.', 'dennis-geo' ) );
			return;
		}

		$clear_pages  = ! empty( $_POST['dseo_clear_pages'] );
		$clear_global = ! empty( $_POST['dseo_clear_global'] );

		if ( ! $clear_pages && ! $clear_global ) {
			$this->redirect_with_message( 'error', __( 'Select at least one option to clear.', 'dennis-geo' ) );
			return;
		}

		$messages = array();

		if ( $clear_pages ) {
			global $wpdb;
			$deleted = 0;
			foreach ( array_keys( DENNIS_SEO_META_KEYS ) as $key ) {
				$deleted += (int) $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => $key ) );
			}
			/* translators: %d: number of entries removed */
			$messages[] = sprintf( __( 'Per-page fields cleared (%d entries removed).', 'dennis-geo' ), $deleted );
		}

		if ( $clear_global ) {
			delete_option( 'dennis_seo_settings' );
			$messages[] = __( 'Global settings cleared.', 'dennis-geo' );
		}

		delete_transient( 'dennis_seo_llms_txt' );
		delete_transient( 'dennis_seo_sitemap_post' );
		delete_transient( 'dennis_seo_sitemap_page' );

		$this->redirect_with_message( 'success', implode( ' ', $messages ) );
	}

	/**
	 * Export all SEO data as JSON download.
	 */
	public function handle_export(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Unauthorized', 'dennis-geo' ) );
		}
		check_admin_referer( 'dennis_seo_export' );

		$data = array(
			'plugin'      => 'dennis-geo',
			'version'     => DENNIS_SEO_VERSION,
			'exported_at' => gmdate( 'c' ),
			'source_url'  => home_url(),
			'settings'    => Dennis_SEO_Settings::get_all(),
			'posts'       => array(),
		);

		// Get all posts with our meta.
		$post_types = get_post_types( array( 'public' => true ) );
		$posts      = get_posts( array(
			'post_type'      => array_values( $post_types ),
			'post_status'    => 'any',
			'posts_per_page' => -1,
		) );

		foreach ( $posts as $post ) {
			$meta = array();
			$has_data = false;

			foreach ( array_keys( DENNIS_SEO_META_KEYS ) as $key ) {
				$value = get_post_meta( $post->ID, $key, true );
				if ( '' !== $value && false !== $value ) {
					$meta[ $key ] = $value;
					$has_data = true;
				}
			}

			// Only include posts that have at least one SEO field set.
			if ( $has_data ) {
				$data['posts'][] = array(
					'post_type'  => $post->post_type,
					'post_name'  => $post->post_name,
					'post_title' => $post->post_title,
					'meta'       => $meta,
				);
			}
		}

		// Output JSON file.
		$filename = 'dennis-seo-export-' . gmdate( 'Y-m-d' ) . '.json';

		header( 'Content-Type: application/json; charset=UTF-8' );
		header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
		header( 'Cache-Control: no-cache, no-store' );

		echo wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
		exit;
	}

	/**
	 * Import SEO data from uploaded JSON file.
	 */
	public function handle_import(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Unauthorized', 'dennis-geo' ) );
		}

		if ( ! isset( $_POST['dennis_seo_import_nonce'] ) ||
			! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['dennis_seo_import_nonce'] ) ), 'dennis_seo_import' ) ) {
			wp_die( esc_html__( 'Invalid nonce', 'dennis-geo' ) );
		}

		if ( ! isset( $_FILES['dseo_import_file'] ) || UPLOAD_ERR_OK !== $_FILES['dseo_import_file']['error'] ) {
			$this->redirect_with_message( 'error', __( 'No file uploaded or upload error.', 'dennis-geo' ) );
			return;
		}

		// Validate file type and size.
		$file_name = sanitize_file_name( wp_unslash( $_FILES['dseo_import_file']['name'] ) );
		if ( 'json' !== pathinfo( $file_name, PATHINFO_EXTENSION ) ) {
			$this->redirect_with_message( 'error', __( 'Only JSON files are accepted.', 'dennis-geo' ) );
			return;
		}
		if ( $_FILES['dseo_import_file']['size'] > 5 * 1024 * 1024 ) {
			$this->redirect_with_message( 'error', __( 'File too large. Maximum 5MB.', 'dennis-geo' ) );
			return;
		}

		$file_path = $_FILES['dseo_import_file']['tmp_name']; // PHP-controlled temp path, no sanitization needed.
		$contents  = file_get_contents( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
		$data      = json_decode( $contents, true );

		if ( null === $data || JSON_ERROR_NONE !== json_last_error() ) {
			$this->redirect_with_message( 'error', __( 'Invalid JSON file.', 'dennis-geo' ) );
			return;
		}

		if ( ! isset( $data['plugin'] ) || 'dennis-geo' !== $data['plugin'] ) {
			$this->redirect_with_message( 'error', __( 'Invalid file. Please upload a Dennis GEO export file.', 'dennis-geo' ) );
			return;
		}

		$source_url  = rtrim( $data['source_url'] ?? '', '/' );
		$current_url = rtrim( home_url(), '/' );
		$stats       = array( 'settings' => false, 'posts_updated' => 0, 'posts_skipped' => 0 );

		// Import settings.
		if ( ! empty( $data['settings'] ) ) {
			$settings = $data['settings'];

			// Replace source domain with current domain in all string values.
			if ( $source_url !== $current_url ) {
				$settings = $this->replace_urls_recursive( $settings, $source_url, $current_url );
			}

			// Sanitize through the same method used by the settings form.
			$sanitizer = new Dennis_SEO_Settings();
			$settings  = $sanitizer->sanitize_settings( $settings );
			update_option( Dennis_SEO_Settings::OPTION_KEY, $settings );
			$stats['settings'] = true;
		}

		// Import per-post meta.
		if ( ! empty( $data['posts'] ) ) {
			foreach ( $data['posts'] as $post_data ) {
				$slug      = $post_data['post_name'] ?? '';
				$post_type = $post_data['post_type'] ?? 'page';

				if ( empty( $slug ) ) {
					$stats['posts_skipped']++;
					continue;
				}

				// Find matching post by slug and post type.
				$existing = get_posts( array(
					'name'           => $slug,
					'post_type'      => $post_type,
					'post_status'    => 'any',
					'posts_per_page' => 1,
				) );

				if ( empty( $existing ) ) {
					// Try matching by slug across all public post types.
					$existing = get_posts( array(
						'name'           => $slug,
						'post_type'      => get_post_types( array( 'public' => true ) ),
						'post_status'    => 'any',
						'posts_per_page' => 1,
					) );
				}

				if ( empty( $existing ) ) {
					$stats['posts_skipped']++;
					continue;
				}

				$target_id = $existing[0]->ID;

				// Textarea fields need sanitize_textarea_field to preserve newlines.
				$textarea_keys = array( '_dennis_seo_description', '_dennis_seo_og_description', '_dennis_seo_llms_description' );
				$url_keys      = array( '_dennis_seo_canonical', '_dennis_seo_og_image' );

				foreach ( $post_data['meta'] as $key => $value ) {
					// Only import our meta keys.
					if ( ! in_array( $key, array_keys( DENNIS_SEO_META_KEYS ), true ) ) {
						continue;
					}

					// Replace URLs in meta values.
					if ( is_string( $value ) && $source_url !== $current_url ) {
						$value = str_replace( $source_url, $current_url, $value );
					}

					// Sanitize per field type.
					if ( in_array( $key, $url_keys, true ) ) {
						$value = esc_url_raw( $value );
					} elseif ( in_array( $key, $textarea_keys, true ) ) {
						$value = sanitize_textarea_field( $value );
					} else {
						$value = sanitize_text_field( $value );
					}

					update_post_meta( $target_id, $key, $value );
				}

				$stats['posts_updated']++;
			}
		}

		// Bust all caches.
		delete_transient( 'dennis_seo_llms_txt' );
		delete_transient( 'dennis_seo_llms_full_txt' );
		delete_transient( 'dennis_seo_sitemap_post' );
		delete_transient( 'dennis_seo_sitemap_page' );

		$message = sprintf(
			/* translators: 1: settings status, 2: posts updated count, 3: posts skipped count */
			__( 'Import complete. Settings: %1$s. Posts updated: %2$d. Posts skipped (not found): %3$d.', 'dennis-geo' ),
			$stats['settings'] ? __( 'imported', 'dennis-geo' ) : __( 'skipped', 'dennis-geo' ),
			$stats['posts_updated'],
			$stats['posts_skipped']
		);

		$this->redirect_with_message( 'success', $message );
	}

	/**
	 * Recursively replace URLs in arrays and strings.
	 */
	private function replace_urls_recursive( $data, string $search, string $replace ) {
		if ( is_string( $data ) ) {
			return str_replace( $search, $replace, $data );
		}
		if ( is_array( $data ) ) {
			foreach ( $data as $key => $value ) {
				$data[ $key ] = $this->replace_urls_recursive( $value, $search, $replace );
			}
		}
		return $data;
	}

	/**
	 * Migrate selected Yoast fields to Dennis SEO for all posts.
	 */
	public function handle_yoast_migrate(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Unauthorized', 'dennis-geo' ) );
		}

		if ( ! isset( $_POST['dennis_seo_yoast_migrate_nonce'] ) ||
			! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['dennis_seo_yoast_migrate_nonce'] ) ), 'dennis_seo_yoast_migrate' ) ) {
			wp_die( esc_html__( 'Invalid nonce', 'dennis-geo' ) );
		}

		$selected = isset( $_POST['dseo_yoast_fields'] ) && is_array( $_POST['dseo_yoast_fields'] )
			? array_map( 'sanitize_text_field', wp_unslash( $_POST['dseo_yoast_fields'] ) )
			: array();

		if ( empty( $selected ) ) {
			$this->redirect_with_message( 'error', __( 'No fields selected.', 'dennis-geo' ) );
			return;
		}

		$post_types = get_post_types( array( 'public' => true ) );
		$posts      = get_posts( array(
			'post_type'      => array_values( $post_types ),
			'post_status'    => 'any',
			'posts_per_page' => 2000,
		) );

		$textarea_keys = array( '_dennis_seo_description', '_dennis_seo_og_description' );
		$url_keys      = array( '_dennis_seo_canonical', '_dennis_seo_og_image' );
		$migrated      = 0;
		$fields_copied = 0;

		foreach ( $posts as $post ) {
			$post_touched = false;
			foreach ( $selected as $yoast_key ) {
				if ( ! isset( self::YOAST_TO_DENNIS[ $yoast_key ] ) ) {
					continue;
				}
				$dennis_key = self::YOAST_TO_DENNIS[ $yoast_key ];
				$yoast_val  = get_post_meta( $post->ID, $yoast_key, true );

				if ( '' === $yoast_val || false === $yoast_val ) {
					continue; // No Yoast data for this field.
				}

				// Sanitize per field type.
				if ( in_array( $dennis_key, $url_keys, true ) ) {
					$yoast_val = esc_url_raw( $yoast_val );
				} elseif ( in_array( $dennis_key, $textarea_keys, true ) ) {
					$yoast_val = sanitize_textarea_field( $yoast_val );
				} else {
					$yoast_val = sanitize_text_field( $yoast_val );
				}

				update_post_meta( $post->ID, $dennis_key, $yoast_val );
				$fields_copied++;
				$post_touched = true;
			}
			if ( $post_touched ) {
				$migrated++;
			}
		}

		// Bust caches.
		delete_transient( 'dennis_seo_llms_txt' );
		delete_transient( 'dennis_seo_sitemap_post' );
		delete_transient( 'dennis_seo_sitemap_page' );

		$message = sprintf(
		/* translators: 1: fields count, 2: posts count */
			__( 'Yoast migration complete. %1$d fields copied across %2$d posts.', 'dennis-geo' ),
			$fields_copied,
			$migrated
		);

		$this->redirect_with_message( 'success', $message );
	}

	/**
	 * Redirect back to settings with a message.
	 */
	private function redirect_with_message( string $type, string $message ): void {
		set_transient( 'dseo_admin_notice_' . get_current_user_id(), array(
			'type'    => $type,
			'message' => $message,
		), 60 );
		$url = add_query_arg( 'page', Dennis_SEO_Settings::MENU_SLUG, admin_url( 'admin.php' ) );
		wp_safe_redirect( $url );
		exit;
	}
}
