<?php
/**
 * Upgrade saved placement attrs when library field shapes change.
 *
 * @package JOCBM
 */

namespace JOCBM\Services;

use JOCBM\Database\Schema;

defined( 'ABSPATH' ) || exit;

/**
 * Class PlacementAttrMigrator
 */
final class PlacementAttrMigrator {

	public const CARD_SLUGS = array( 'cards', 'blog-cards', 'simple-cards' );

	/**
	 * Slugs whose Gutenberg / shortcode placement attrs may need upgrades.
	 *
	 * @return array<int, string>
	 */
	public static function placement_slugs(): array {
		return self::CARD_SLUGS;
	}

	/**
	 * Migrate catalog rows (system_blocks + user_blocks defaults + shortcode content).
	 *
	 * @return int Rows updated.
	 */
	public static function migrate_catalog_rows(): int {
		global $wpdb;

		$updated      = 0;
		$system_table = Schema::table( 'system_blocks' );
		$user_table   = Schema::table( 'user_blocks' );
		$content_table = Schema::table( 'user_contents' );

		foreach ( self::CARD_SLUGS as $slug ) {
			$rows = $wpdb->get_results(
				$wpdb->prepare(
					'SELECT id, default_attrs FROM %i WHERE slug = %s',
					$system_table,
					$slug
				),
				ARRAY_A
			);
			if ( is_array( $rows ) ) {
				foreach ( $rows as $row ) {
					$attrs = json_decode( (string) ( $row['default_attrs'] ?? '' ), true );
					if ( ! is_array( $attrs ) ) {
						continue;
					}
					[ $next, $changed ] = self::upgrade_cards_link_to_buttons( $attrs );
					if ( ! $changed ) {
						continue;
					}
					$wpdb->update(
						$system_table,
						array( 'default_attrs' => wp_json_encode( $next ) ),
						array( 'id' => (int) $row['id'] ),
						array( '%s' ),
						array( '%d' )
					);
					++$updated;
				}
			}
		}

		$system_ids = $wpdb->get_col(
			$wpdb->prepare(
				'SELECT id FROM %i WHERE slug IN (%s, %s, %s)',
				$system_table,
				'cards',
				'blog-cards',
				'simple-cards'
			)
		);
		if ( is_array( $system_ids ) && $system_ids ) {
			$placeholders = implode( ',', array_fill( 0, count( $system_ids ), '%d' ) );
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
			$user_rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT id, default_attrs FROM {$user_table} WHERE system_block_id IN ({$placeholders})",
					...array_map( 'intval', $system_ids )
				),
				ARRAY_A
			);
			if ( is_array( $user_rows ) ) {
				foreach ( $user_rows as $row ) {
					$attrs = json_decode( (string) ( $row['default_attrs'] ?? '' ), true );
					if ( ! is_array( $attrs ) ) {
						continue;
					}
					[ $next, $changed ] = self::upgrade_cards_link_to_buttons( $attrs );
					if ( ! $changed ) {
						continue;
					}
					$wpdb->update(
						$user_table,
						array( 'default_attrs' => wp_json_encode( $next ) ),
						array( 'id' => (int) $row['id'] ),
						array( '%s' ),
						array( '%d' )
					);
					++$updated;
				}
			}

			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
			$content_rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT c.id, c.content_attrs FROM {$content_table} c
					INNER JOIN {$user_table} u ON u.id = c.user_block_id
					WHERE u.system_block_id IN ({$placeholders})",
					...array_map( 'intval', $system_ids )
				),
				ARRAY_A
			);
			if ( is_array( $content_rows ) ) {
				foreach ( $content_rows as $row ) {
					$attrs = json_decode( (string) ( $row['content_attrs'] ?? '' ), true );
					if ( ! is_array( $attrs ) ) {
						continue;
					}
					[ $next, $changed ] = self::upgrade_cards_link_to_buttons( $attrs );
					if ( ! $changed ) {
						continue;
					}
					$wpdb->update(
						$content_table,
						array( 'content_attrs' => wp_json_encode( $next ) ),
						array( 'id' => (int) $row['id'] ),
						array( '%s' ),
						array( '%d' )
					);
					++$updated;
				}
			}
		}

		return $updated;
	}

	/**
	 * Upgrade Gutenberg placements stored in post_content.
	 *
	 * @return int Posts updated.
	 */
	public static function migrate_gutenberg_posts(): int {
		global $wpdb;

		$updated = 0;
		$like_lib = '%' . $wpdb->esc_like( '<!-- wp:joc-blockora-morph-lib/' ) . '%';
		$like_mb  = '%' . $wpdb->esc_like( '<!-- wp:joc-blockora-morph/' ) . '%';

		$post_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts}
				WHERE post_type NOT IN ('revision','auto-draft','trash')
				AND post_status IN ('publish','draft','pending','future','private')
				AND (post_content LIKE %s OR post_content LIKE %s)",
				$like_lib,
				$like_mb
			)
		);

		if ( ! is_array( $post_ids ) ) {
			return 0;
		}

		foreach ( $post_ids as $post_id ) {
			$post = get_post( (int) $post_id );
			if ( ! $post instanceof \WP_Post || '' === trim( $post->post_content ) ) {
				continue;
			}

			$blocks = parse_blocks( $post->post_content );
			if ( ! is_array( $blocks ) || ! $blocks ) {
				continue;
			}

			[ $next_blocks, $changed ] = self::upgrade_blocks_tree( $blocks );
			if ( ! $changed ) {
				continue;
			}

			$content = serialize_blocks( $next_blocks );
			if ( $content === $post->post_content ) {
				continue;
			}

			$wpdb->update(
				$wpdb->posts,
				array( 'post_content' => $content ),
				array( 'ID' => (int) $post->ID ),
				array( '%s' ),
				array( '%d' )
			);
			clean_post_cache( (int) $post->ID );
			++$updated;
		}

		return $updated;
	}

	/**
	 * @param array<int, array<string, mixed>> $blocks Parsed blocks.
	 * @return array{0: array<int, array<string, mixed>>, 1: bool}
	 */
	public static function upgrade_blocks_tree( array $blocks ): array {
		$changed = false;
		$next    = array();

		foreach ( $blocks as $block ) {
			if ( ! is_array( $block ) ) {
				$next[] = $block;
				continue;
			}

			$name = (string) ( $block['blockName'] ?? '' );
			$attrs = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : array();

			if ( self::is_placement_block_name( $name ) ) {
				[ $attrs, $block_changed ] = self::upgrade_block_attrs( $name, $attrs );
				if ( $block_changed ) {
					$block['attrs'] = $attrs;
					$changed        = true;
				}
			}

			if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
				[ $inner, $inner_changed ] = self::upgrade_blocks_tree( $block['innerBlocks'] );
				$block['innerBlocks'] = $inner;
				$changed              = $changed || $inner_changed;
			}

			$next[] = $block;
		}

		return array( $next, $changed );
	}

	/**
	 * @param string               $block_name Block name.
	 * @param array<string, mixed> $attrs      Placement attrs.
	 * @return array{0: array<string, mixed>, 1: bool}
	 */
	public static function upgrade_block_attrs( string $block_name, array $attrs ): array {
		$slug = self::slug_from_block_name( $block_name );
		if ( in_array( $slug, self::CARD_SLUGS, true ) ) {
			return self::upgrade_cards_link_to_buttons( $attrs );
		}

		return array( $attrs, false );
	}

	/**
	 * @param string $block_name Block name.
	 */
	public static function is_placement_block_name( string $block_name ): bool {
		if ( '' === $block_name ) {
			return false;
		}
		if ( str_starts_with( $block_name, LibraryBlocks::BLOCK_NAMESPACE . '/' ) ) {
			return true;
		}
		if ( str_starts_with( $block_name, 'joc-blockora-morph/' ) ) {
			$slug = self::slug_from_block_name( $block_name );
			return in_array( $slug, self::placement_slugs(), true );
		}
		return false;
	}

	/**
	 * @param string $block_name Block name.
	 */
	public static function slug_from_block_name( string $block_name ): string {
		if ( str_starts_with( $block_name, LibraryBlocks::BLOCK_NAMESPACE . '/' ) ) {
			$parsed = LibraryBlocks::parse_block_name( $block_name );
			return $parsed ? sanitize_title( $parsed[0] ) : '';
		}
		if ( str_starts_with( $block_name, 'joc-blockora-morph/' ) ) {
			return sanitize_title( substr( $block_name, strlen( 'joc-blockora-morph/' ) ) );
		}
		return '';
	}

	/**
	 * Features card rows: single link → buttons[].
	 *
	 * @param array<string, mixed> $attrs Block attrs.
	 * @return array{0: array<string, mixed>, 1: bool}
	 */
	public static function upgrade_cards_link_to_buttons( array $attrs ): array {
		$cards = $attrs['cards'] ?? null;
		if ( ! is_array( $cards ) || ! $cards ) {
			return array( $attrs, false );
		}

		$changed = false;
		foreach ( $cards as $i => $card ) {
			if ( ! is_array( $card ) ) {
				continue;
			}
			$buttons     = $card['buttons'] ?? null;
			$has_buttons = is_array( $buttons ) && $buttons && array_keys( $buttons ) === range( 0, count( $buttons ) - 1 );
			$has_link    = isset( $card['link'] ) && is_array( $card['link'] );

			if ( $has_buttons ) {
				if ( isset( $card['link'] ) ) {
					unset( $card['link'] );
					$cards[ $i ] = $card;
					$changed     = true;
				}
				continue;
			}

			if ( ! $has_link ) {
				continue;
			}

			$link = $card['link'];
			$card['buttons'] = array(
				array(
					'link' => array(
						'title'         => isset( $link['title'] ) ? (string) $link['title'] : '',
						'url'           => isset( $link['url'] ) ? (string) $link['url'] : '',
						'opensInNewTab' => ! empty( $link['opensInNewTab'] ),
					),
					'bg'            => '#3525cd',
					'color'         => '#ffffff',
					'bgHover'       => '#2a1ea4',
					'colorHover'    => '#ffffff',
					'size'          => 'medium',
					'padY'          => '0.625rem',
					'padX'          => '1.25rem',
					'borderRadius'  => '8px',
					'icon'          => '',
					'iconPosition'  => 'right',
				),
			);
			unset( $card['link'] );
			$cards[ $i ] = $card;
			$changed     = true;
		}

		if ( ! $changed ) {
			return array( $attrs, false );
		}
		$attrs['cards'] = $cards;
		return array( $attrs, true );
	}

	/**
	 * @deprecated Use upgrade_cards_link_to_buttons.
	 *
	 * @param array<string, mixed> $attrs Block attrs.
	 * @return array{0: array<string, mixed>, 1: bool}
	 */
	public static function upgrade_cards_buttons_to_link( array $attrs ): array {
		return self::upgrade_cards_link_to_buttons( $attrs );
	}
}
