<?php
/**
 * Admin-side visibility for vehicle-specs sync errors.
 *
 * Sits on top of `VehicleSpecsSync` — that class persists errors to the
 * `_trx_ad_sync_last_error` post meta via `recordSyncError`; this class
 * surfaces them in three places the listing-edit user actually sees:
 *
 *   1. Per-listing admin notice on the edit screen.
 *   2. Site-wide summary notice on the listings list-table.
 *   3. "Sync errors (N)" link in the list-table's subsubsub views, plus a
 *      `pre_get_posts` filter on `?trx_ad_sync_status=errors`.
 *
 * Extracted from VehicleSpecsSync in QUALITY-IMPROVEMENT-PLAN T2-2b — the
 * sync engine itself runs on REST + cron paths where the admin classes
 * (`WP_Screen`, list-table filters) aren't even relevant, so co-locating
 * them was just noise.
 *
 * Meta key contract: `_trx_ad_sync_last_error` is an array shape
 * `{code, message, timestamp, ...extra}` written by `VehicleSpecsSync::recordSyncError`.
 * For `code === 'vin_conflict'` the `extra` payload includes `conflict_with => $post_id`.
 *
 * @package ThemeREX\AutoDealer\Database
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Database;

defined( 'ABSPATH' ) || exit;

use ThemeREX\AutoDealer\PostTypes\VehiclePostType;

final class SyncErrorVisibility {

	public function register(): void {
		add_action( 'admin_notices', array( $this, 'showSyncErrorNotices' ) );
		add_filter( 'views_edit-' . VehiclePostType::POST_TYPE, array( $this, 'addSyncErrorView' ) );
		add_action( 'pre_get_posts', array( $this, 'filterListBySyncStatus' ) );
	}

	/**
	 * admin_notices handler:
	 *  - On the edit screen for a single listing: a per-listing notice with
	 *    the stored sync error (rich text for `vin_conflict`, generic for
	 *    everything else).
	 *  - On the listings list page: a site-wide summary notice with a count
	 *    of listings carrying an unresolved sync error and a link that
	 *    filters the list to those rows.
	 */
	public function showSyncErrorNotices(): void {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen instanceof \WP_Screen ) {
			return;
		}

		if ( VehiclePostType::POST_TYPE !== $screen->post_type ) {
			return;
		}

		if ( 'post' === $screen->base ) {
			$this->renderPerListingNotice();
			return;
		}

		if ( 'edit' === $screen->base ) {
			$this->renderListPageSummaryNotice();
		}
	}

	private function renderPerListingNotice(): void {
		$postId = isset( $_GET['post'] ) ? (int) $_GET['post'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( $postId <= 0 ) {
			return;
		}

		$error = get_post_meta( $postId, '_trx_ad_sync_last_error', true );
		if ( ! is_array( $error ) || empty( $error['code'] ) ) {
			return;
		}

		$code    = (string) $error['code'];
		$message = (string) ( $error['message'] ?? '' );

		// VIN conflict: rich UI with a link to the other listing. Auto-clear
		// if the other listing was deleted while this screen was loading.
		if ( 'vin_conflict' === $code ) {
			$conflictId = (int) ( $error['conflict_with'] ?? get_post_meta( $postId, '_trx_ad_vin_conflict_with', true ) );
			$other      = $conflictId > 0 ? get_post( $conflictId ) : null;
			if ( ! $other instanceof \WP_Post || VehiclePostType::POST_TYPE !== $other->post_type ) {
				VehicleSpecsSync::clearSyncError( $postId );
				return;
			}
			printf(
				'<div class="notice notice-warning"><p><strong>%1$s</strong> %2$s <a href="%3$s">%4$s</a>.</p></div>',
				esc_html__( 'VIN conflict:', 'trx-auto-dealer' ),
				esc_html__( 'this listing\'s VIN was not saved because the same VIN is already used by another listing. The rest of the data synced normally. To resolve, edit either this listing or:', 'trx-auto-dealer' ),
				esc_url( get_edit_post_link( $conflictId ) ?: '#' ),
				esc_html( get_the_title( $conflictId ) )
			);
			return;
		}

		printf(
			'<div class="notice notice-error"><p><strong>%1$s</strong> %2$s</p><p class="description">%3$s <code>%4$s</code></p></div>',
			esc_html__( 'Vehicle specs sync failed:', 'trx-auto-dealer' ),
			esc_html( $message !== '' ? $message : __( 'An unknown error occurred during sync.', 'trx-auto-dealer' ) ),
			esc_html__( 'Error code:', 'trx-auto-dealer' ),
			esc_html( $code )
		);
	}

	private function renderListPageSummaryNotice(): void {
		$count = (int) self::countListingsWithSyncError();
		if ( $count <= 0 ) {
			return;
		}

		$filterUrl = add_query_arg(
			array(
				'post_type'          => VehiclePostType::POST_TYPE,
				'trx_ad_sync_status' => 'errors',
			),
			admin_url( 'edit.php' )
		);

		printf(
			'<div class="notice notice-warning"><p>%1$s <a href="%2$s">%3$s</a></p></div>',
			esc_html(
				sprintf(
					/* translators: %d: number of listings */
					_n(
						'%d listing has a vehicle-specs sync error.',
						'%d listings have vehicle-specs sync errors.',
						$count,
						'trx-auto-dealer'
					),
					$count
				)
			),
			esc_url( $filterUrl ),
			esc_html__( 'Show only those listings', 'trx-auto-dealer' )
		);
	}

	/**
	 * Add a "Sync errors (N)" link to the listings list-table views.
	 * Sits next to All / Published / Trash. Hidden when count == 0.
	 *
	 * @param array<string,string> $views Existing list-table views.
	 * @return array<string,string>
	 */
	public function addSyncErrorView( array $views ): array {
		$count = self::countListingsWithSyncError();
		if ( $count <= 0 ) {
			return $views;
		}

		$is_current = isset( $_GET['trx_ad_sync_status'] ) && 'errors' === $_GET['trx_ad_sync_status']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$url        = add_query_arg(
			array(
				'post_type'          => VehiclePostType::POST_TYPE,
				'trx_ad_sync_status' => 'errors',
			),
			admin_url( 'edit.php' )
		);

		$views['trx_ad_sync_errors'] = sprintf(
			'<a href="%1$s" class="%2$s">%3$s <span class="count">(%4$d)</span></a>',
			esc_url( $url ),
			esc_attr( $is_current ? 'current' : '' ),
			esc_html__( 'Sync errors', 'trx-auto-dealer' ),
			(int) $count
		);

		return $views;
	}

	/**
	 * When the list page is filtered with `?trx_ad_sync_status=errors`,
	 * restrict the query to listings carrying `_trx_ad_sync_last_error`.
	 */
	public function filterListBySyncStatus( \WP_Query $query ): void {
		if ( ! is_admin() || ! $query->is_main_query() ) {
			return;
		}
		if ( VehiclePostType::POST_TYPE !== $query->get( 'post_type' ) ) {
			return;
		}
		$status = isset( $_GET['trx_ad_sync_status'] ) ? sanitize_key( (string) $_GET['trx_ad_sync_status'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( 'errors' !== $status ) {
			return;
		}

		$existing   = (array) $query->get( 'meta_query' );
		$existing[] = array(
			'key'     => '_trx_ad_sync_last_error',
			'compare' => 'EXISTS',
		);
		$query->set( 'meta_query', $existing ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
	}

	/**
	 * Count listings carrying `_trx_ad_sync_last_error`. Used by both the
	 * admin-notice summary and the list-table view link counter.
	 */
	public static function countListingsWithSyncError(): int {
		$q = new \WP_Query(
			array(
				'post_type'      => VehiclePostType::POST_TYPE,
				'post_status'    => 'any',
				'meta_query'     => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
					array(
						'key'     => '_trx_ad_sync_last_error',
						'compare' => 'EXISTS',
					),
				),
				'fields'         => 'ids',
				'posts_per_page' => 1, // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
				'no_found_rows'  => false,
			)
		);
		return (int) $q->found_posts;
	}
}
