<?php
/**
 * BackupManager — stores and restores per-post backups for Link Assassin.
 *
 * Each backup is a postmeta entry keyed by
 * `_platform_helm_la_backup_{timestamp}_{rand}` containing:
 *   - post_content
 *   - _elementor_data (string or '')
 *   - urls (list of URLs the run targeted)
 *   - timestamp (unix)
 *
 * The double-underscore prefix means WordPress hides them from the postmeta
 * UI in the editor by default.
 *
 * @package Platform\Helm\Modules\LinkAssassin
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\LinkAssassin;

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

final class BackupManager {

	/**
	 * Persist a backup row for the given post BEFORE modifications.
	 *
	 * @param string[] $urls
	 */
	public function create_backup( int $post_id, string $post_content, string $elementor_data, array $urls ): string {
		$backup_key = LinkAssassinModule::BACKUP_META_PREFIX
			. time() . '_'
			. wp_generate_password( 4, false, false );

		$payload = wp_json_encode( [
			'post_content'    => $post_content,
			'_elementor_data' => $elementor_data,
			'urls'            => array_values( $urls ),
			'timestamp'       => time(),
		], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );

		update_post_meta( $post_id, $backup_key, wp_slash( $payload ) );
		return $backup_key;
	}

	/**
	 * List all backup rows across all posts.
	 *
	 * @param int $limit Hard cap to avoid huge result sets.
	 * @return array<int,array<string,mixed>>
	 */
	public function list_backups( int $limit = 200 ): array {
		global $wpdb;

		$like = $wpdb->esc_like( LinkAssassinModule::BACKUP_META_PREFIX ) . '%';

		$rows = $wpdb->get_results( $wpdb->prepare(
			"SELECT pm.meta_id, pm.post_id, pm.meta_key, p.post_title, p.post_type
			 FROM {$wpdb->postmeta} pm
			 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
			 WHERE pm.meta_key LIKE %s
			 ORDER BY pm.meta_id DESC
			 LIMIT %d",
			$like,
			$limit
		), ARRAY_A );

		if ( ! is_array( $rows ) ) {
			return [];
		}

		$out = [];
		foreach ( $rows as $row ) {
			$ts = $this->extract_timestamp_from_key( (string) $row['meta_key'] );
			$out[] = [
				'meta_id'    => (int) $row['meta_id'],
				'post_id'    => (int) $row['post_id'],
				'meta_key'   => (string) $row['meta_key'],
				'post_title' => (string) $row['post_title'],
				'post_type'  => (string) $row['post_type'],
				'timestamp'  => $ts,
			];
		}
		return $out;
	}

	/**
	 * Restore a single backup. Returns true if the post was rolled back.
	 */
	public function restore( int $post_id, string $meta_key ): bool {
		if ( 0 !== strpos( $meta_key, LinkAssassinModule::BACKUP_META_PREFIX ) ) {
			return false;
		}
		$raw = get_post_meta( $post_id, $meta_key, true );
		if ( ! is_string( $raw ) || '' === $raw ) {
			return false;
		}
		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			return false;
		}

		$post_content    = (string) ( $data['post_content']    ?? '' );
		$elementor_data  = (string) ( $data['_elementor_data'] ?? '' );

		$updated = wp_update_post( [
			'ID'           => $post_id,
			'post_content' => $post_content,
		], true );
		if ( is_wp_error( $updated ) ) {
			return false;
		}

		if ( '' !== $elementor_data ) {
			update_post_meta( $post_id, '_elementor_data', wp_slash( $elementor_data ) );
		} else {
			delete_post_meta( $post_id, '_elementor_data' );
		}

		// Backup served its purpose — remove it so the post isn't littered.
		delete_post_meta( $post_id, $meta_key );

		$this->maybe_clear_elementor_cache();
		return true;
	}

	/**
	 * Delete a backup without restoring (cleanup utility).
	 */
	public function delete( int $post_id, string $meta_key ): bool {
		if ( 0 !== strpos( $meta_key, LinkAssassinModule::BACKUP_META_PREFIX ) ) {
			return false;
		}
		return (bool) delete_post_meta( $post_id, $meta_key );
	}

	private function extract_timestamp_from_key( string $key ): int {
		$suffix = substr( $key, strlen( LinkAssassinModule::BACKUP_META_PREFIX ) );
		$parts  = explode( '_', $suffix, 2 );
		return ctype_digit( $parts[0] ?? '' ) ? (int) $parts[0] : 0;
	}

	public function maybe_clear_elementor_cache(): void {
		if ( ! class_exists( '\Elementor\Plugin' ) ) {
			return;
		}
		try {
			$elementor = \Elementor\Plugin::$instance;
			if ( isset( $elementor->files_manager ) && method_exists( $elementor->files_manager, 'clear_cache' ) ) {
				$elementor->files_manager->clear_cache();
			}
		} catch ( \Throwable $e ) {
			// Best-effort.
		}
	}
}
