<?php
/**
 * PostScanner — finds candidate post IDs whose content or Elementor data
 * mentions the host of any target URL.
 *
 * Uses $wpdb with prepare() and esc_like(). Coarse host-only LIKE search
 * keeps the query cheap; precise matching happens later via the DOM stripper
 * per candidate post.
 *
 * @package Platform\Helm\Modules\LinkAssassin
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\LinkAssassin;

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

final class PostScanner {

	private const MAX_POSTS_PER_HOST = 5000;

	/**
	 * @param string[] $urls
	 * @return int[]
	 */
	public function find_candidate_post_ids( array $urls ): array {
		global $wpdb;

		$post_types = $this->get_target_post_types();
		if ( empty( $post_types ) ) {
			return [];
		}

		$hosts = $this->extract_unique_hosts( $urls );
		if ( empty( $hosts ) ) {
			return [];
		}

		$candidate_ids = [];

		// Build the post_type IN clause with placeholders.
		$type_placeholders = implode( ',', array_fill( 0, count( $post_types ), '%s' ) );

		foreach ( $hosts as $host ) {
			$like = '%' . $wpdb->esc_like( $host ) . '%';

			// post_content scan.
			$sql_content = $wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT ID FROM {$wpdb->posts}
				 WHERE post_status IN ('publish','private','draft','future','pending')
				   AND post_type IN ({$type_placeholders})
				   AND post_content LIKE %s
				 LIMIT %d",
				...array_merge( $post_types, [ $like, self::MAX_POSTS_PER_HOST ] )
			);
			$ids = $wpdb->get_col( $sql_content );
			foreach ( (array) $ids as $id ) {
				$candidate_ids[ (int) $id ] = true;
			}

			// _elementor_data scan.
			$sql_meta = $wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT pm.post_id FROM {$wpdb->postmeta} pm
				 INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
				 WHERE pm.meta_key = '_elementor_data'
				   AND p.post_type IN ({$type_placeholders})
				   AND pm.meta_value LIKE %s
				 LIMIT %d",
				...array_merge( $post_types, [ $like, self::MAX_POSTS_PER_HOST ] )
			);
			$ids = $wpdb->get_col( $sql_meta );
			foreach ( (array) $ids as $id ) {
				$candidate_ids[ (int) $id ] = true;
			}
		}

		$ids = array_keys( $candidate_ids );
		sort( $ids );
		return $ids;
	}

	/**
	 * @param string[] $urls
	 * @return string[]
	 */
	private function extract_unique_hosts( array $urls ): array {
		$hosts = [];
		foreach ( $urls as $url ) {
			$host = wp_parse_url( $url, PHP_URL_HOST );
			if ( ! empty( $host ) && is_string( $host ) ) {
				$hosts[ strtolower( $host ) ] = true;
			}
		}
		return array_keys( $hosts );
	}

	/**
	 * @return string[]
	 */
	public function get_target_post_types(): array {
		$public = get_post_types( [ 'public' => true ], 'names' );
		unset( $public['attachment'] );
		/**
		 * Filter the post types Link Assassin scans.
		 *
		 * @param string[] $types Default: public post types except attachment.
		 */
		$filtered = apply_filters( 'platform_helm_la_target_post_types', array_values( $public ) );
		if ( ! is_array( $filtered ) ) {
			return [];
		}
		return array_values( array_filter( $filtered, 'is_string' ) );
	}

	/**
	 * Parse a textarea of one-URL-per-line input into a clean URL array.
	 *
	 * @return string[]
	 */
	public function parse_urls_input( string $raw ): array {
		$lines = preg_split( '/\r\n|\r|\n/', $raw );
		if ( ! is_array( $lines ) ) {
			return [];
		}
		$urls = [];
		foreach ( $lines as $line ) {
			$line = trim( $line );
			if ( '' === $line ) {
				continue;
			}
			$clean = esc_url_raw( $line );
			if ( '' !== $clean ) {
				$urls[] = $clean;
			}
		}
		return array_values( array_unique( $urls ) );
	}
}
