<?php
/**
 * REST API controller for dealers and the cascading models endpoint.
 *
 * Exposes:
 *   GET /trx-auto-dealer/v1/dealers
 *   GET /trx-auto-dealer/v1/dealers/{id}
 *   GET /trx-auto-dealer/v1/models?make={term_id}
 *
 * @package ThemeREX\AutoDealer
 * @version 1.0.0
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\REST;

use WP_Error;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class DealersController extends WP_REST_Controller {

	/** @var string */
	protected $namespace = 'trx-auto-dealer/v1';

	/** @var string */
	protected $rest_base = 'dealers';

	public function register_routes(): void {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_items' ),
					'permission_callback' => '__return_true',
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => '__return_true',
					'args'                => array(
						'id' => array(
							'type'     => 'integer',
							'required' => true,
						),
					),
				),
			)
		);

		// Cascading dropdown — Phase 3 deliverable.
		register_rest_route(
			$this->namespace,
			'/models',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_models' ),
					'permission_callback' => '__return_true',
					'args'                => array(
						'make' => array(
							'type'        => 'integer',
							'description' => __( 'Make term ID.', 'trx-auto-dealer' ),
						),
					),
				),
			)
		);
	}

	/**
	 * GET /dealers — list published dealers.
	 */
	public function get_items( $request ) {
		$posts = get_posts(
			array(
				'post_type'      => 'trx_ad_dealer',
				'post_status'    => 'publish',
				'posts_per_page' => 100,
				'orderby'        => 'title',
				'order'          => 'ASC',
			)
		);

		$out = array_map( fn( $p ) => $this->prepareDealer( $p ), $posts );
		return new WP_REST_Response( $out, 200 );
	}

	/**
	 * GET /dealers/{id} — single dealer with full meta.
	 */
	public function get_item( $request ) {
		$id   = (int) $request->get_param( 'id' );
		$post = get_post( $id );

		if ( null === $post || 'trx_ad_dealer' !== $post->post_type || 'publish' !== $post->post_status ) {
			return new WP_Error(
				'trx_ad_dealer_not_found',
				__( 'Dealer not found.', 'trx-auto-dealer' ),
				array( 'status' => 404 )
			);
		}

		return new WP_REST_Response( $this->prepareDealer( $post, true ), 200 );
	}

	/**
	 * GET /models?make={term_id} — child Models for a Make (cascading dropdown).
	 *
	 * If `make` is omitted, returns all Model terms (flat list).
	 *
	 * @param \WP_REST_Request<array<string,mixed>> $request
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function get_models( $request ) {
		$make_id = (int) $request->get_param( 'make' );

		if ( $make_id <= 0 ) {
			$terms = get_terms(
				array(
					'taxonomy'   => 'trx_ad_model',
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
				)
			);
			return new WP_REST_Response( $this->shapeTermList( $terms ), 200 );
		}

		// Strategy 1: explicit term-meta linkage (set by the seeder + the
		// "Parent Make" UI in `Taxonomies\ModelMakeLink`).
		$linked = get_terms(
			array(
				'taxonomy'   => 'trx_ad_model',
				'hide_empty' => false,
				'orderby'    => 'name',
				'order'      => 'ASC',
				'meta_query' => array(
					array(
						'key'   => \ThemeREX\AutoDealer\Taxonomies\ModelMakeLink::META_KEY,
						'value' => (string) $make_id,
					),
				),
			)
		);
		if ( ! is_wp_error( $linked ) && ! empty( $linked ) ) {
			// Filter to only models that have at least one published listing
			// also tagged with this make. Without this, a Ford brand with
			// 30 model terms linked but only 2 in-stock cars would surface
			// all 30 in the dropdown — confusing UX.
			$listing_ids = get_posts(
				array(
					'post_type'      => 'trx_ad_listing',
					'post_status'    => 'publish',
					'fields'         => 'ids',
					'posts_per_page' => 500,
					'no_found_rows'  => true,
					'tax_query'      => array(
						array(
							'taxonomy' => 'trx_ad_make',
							'field'    => 'term_id',
							'terms'    => $make_id,
						),
					),
				)
			);
			$in_use      = array();
			foreach ( $listing_ids as $lid ) {
				$ids = wp_get_object_terms( (int) $lid, 'trx_ad_model', array( 'fields' => 'ids' ) );
				if ( ! is_wp_error( $ids ) && is_array( $ids ) ) {
					foreach ( $ids as $id ) {
						$in_use[ (int) $id ] = true;
					}
				}
			}
			if ( ! empty( $in_use ) ) {
				$linked = array_filter(
					$linked,
					static fn( $t ) => isset( $in_use[ (int) $t->term_id ] )
				);
				if ( ! empty( $linked ) ) {
					return new WP_REST_Response( $this->shapeTermList( $linked ), 200 );
				}
			}
			// Linkage exists but no in-use models for this make → fall through
			// to Strategy 2 (which already filters by tagged listings).
		}

		// Strategy 2 (fallback): derive from actual listing co-assignments.
		// Find every published listing tagged with this make, then collect
		// the model terms attached to those listings. Useful when the user
		// hasn't filled in "Parent Make" on their model terms but their
		// listings DO have both make + model assigned.
		$listing_ids = get_posts(
			array(
				'post_type'      => 'trx_ad_listing',
				'post_status'    => 'publish',
				'fields'         => 'ids',
				'posts_per_page' => 500,
				'no_found_rows'  => true,
				'tax_query'      => array(
					array(
						'taxonomy' => 'trx_ad_make',
						'field'    => 'term_id',
						'terms'    => $make_id,
					),
				),
			)
		);
		$model_ids   = array();
		if ( ! empty( $listing_ids ) ) {
			foreach ( $listing_ids as $lid ) {
				$ids = wp_get_object_terms( (int) $lid, 'trx_ad_model', array( 'fields' => 'ids' ) );
				if ( ! is_wp_error( $ids ) && is_array( $ids ) ) {
					foreach ( $ids as $id ) {
						$model_ids[ (int) $id ] = true;
					}
				}
			}
		}
		if ( ! empty( $model_ids ) ) {
			$derived = get_terms(
				array(
					'taxonomy'   => 'trx_ad_model',
					'hide_empty' => false,
					'orderby'    => 'name',
					'order'      => 'ASC',
					'include'    => array_keys( $model_ids ),
				)
			);
			return new WP_REST_Response( $this->shapeTermList( $derived ), 200 );
		}

		// Strategy 3 (last resort): the make has no models linked via term-meta
		// AND no listings co-tagged with model. Return ALL models in the
		// taxonomy so the dropdown isn't empty — empty dropdowns are a UX
		// dead-end on sites where the user hasn't set up "Parent Make"
		// linkage on their model terms yet. Better to over-list than to
		// strand the user.
		$all = get_terms(
			array(
				'taxonomy'   => 'trx_ad_model',
				'hide_empty' => false,
				'orderby'    => 'name',
				'order'      => 'ASC',
			)
		);
		return new WP_REST_Response( $this->shapeTermList( $all ), 200 );
	}

	/**
	 * @param mixed $terms
	 * @return array<int, array<string, mixed>>
	 */
	private function shapeTermList( $terms ): array {
		if ( ! is_array( $terms ) ) {
			return array();
		}
		return array_values(
			array_map(
				static fn( $t ) => array(
					'id'    => (int) $t->term_id,
					'name'  => $t->name,
					'slug'  => $t->slug,
					'count' => (int) $t->count,
				),
				$terms
			)
		);
	}

	/**
	 * Shape a dealer post into the API response payload.
	 *
	 * @param \WP_Post $post Dealer post.
	 * @param bool     $full Include full meta?
	 * @return array<string,mixed>
	 */
	private function prepareDealer( \WP_Post $post, bool $full = false ): array {
		$data = array(
			'id'        => $post->ID,
			'name'      => $post->post_title,
			'slug'      => $post->post_name,
			'permalink' => get_permalink( $post ),
		);

		if ( ! $full ) {
			return $data;
		}

		foreach ( array( 'phone', 'whatsapp', 'email', 'website', 'street_address', 'city', 'state', 'postcode', 'country', 'working_hours', 'lat', 'lng' ) as $key ) {
			$data[ $key ] = (string) get_post_meta( $post->ID, '_trx_ad_' . $key, true );
		}

		$data['description'] = $post->post_content;
		$data['logo_url']    = (string) ( wp_get_attachment_image_url( (int) get_post_thumbnail_id( $post->ID ), 'full' ) ?: '' );

		return $data;
	}
}
