<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Lead;
use Limb_Chatbot\Includes\Repositories\Lead_Repository;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for managing Leads via REST API.
 *
 * Provides endpoints for retrieving lead records with advanced filtering and search:
 * - GET /leads - List all leads with filtering, search, and pagination
 * - GET /leads/{id} - Get a single lead by ID
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.13
 */
class Leads_Controller extends Rest_Controller {

	/**
	 * REST route base.
	 *
	 * @var string
	 * @since 1.0.13
	 */
	protected $rest_base = 'leads';

	/**
	 * Lead repository instance.
	 *
	 * @var Lead_Repository
	 * @since 1.0.13
	 */
	protected Lead_Repository $repository;

	/**
	 * Leads_Controller constructor.
	 *
	 * @since 1.0.13
	 */
	public function __construct() {
		$this->repository = new Lead_Repository();
	}

	/**
	 * Registers REST routes for leads.
	 *
	 * @return void
	 * @since 1.0.13
	 */
	public function register_routes() {
		// GET /leads - List all leads with filtering and search
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_collection_params(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// GET /leads/{id} - Get single lead
		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' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'required'          => true,
						'validate_callback' => function ( $value ) {
							return ! empty( Lead::find( $value ) );
						},
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Returns query parameters for the collection endpoint.
	 *
	 * These parameters allow filtering, searching, and sorting of lead results.
	 *
	 * @return array Array of query parameters accepted by the collection route.
	 * @since 1.0.13
	 */
	public function get_collection_params() {
		return array_merge( parent::get_collection_params(), array(
			'orderby'          => array(
				'description'       => __( 'Sort the collection by attribute.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'created_at',
				'enum'              => array( 'id', 'created_at', 'source_type', 'status', 'is_qualified', 'source_title' ),
				'validate_callback' => 'rest_validate_request_arg',
			),
			'search'           => array(
				'description' => __( 'Search leads by source title, source ID, or UUIDs.', 'limb-chatbot' ),
				'type'        => 'string',
			),
			'search_fields'    => array(
				'description' => __( 'Fields to search in (e.g., source_title, source, chatbot_user_uuid, chatbot_uuid).', 'limb-chatbot' ),
				'type'        => 'array',
				'default'     => array( 'source_title', 'source' ),
			),
			'source_type'      => array(
				'description'       => __( 'Filter by source type.', 'limb-chatbot' ),
				'type'              => 'string',
				'enum'              => array( Lead::SOURCE_TYPE_ACTION, Lead::SOURCE_TYPE_WIDGET ),
				'sanitize_callback' => 'sanitize_text_field',
			),
			'source'           => array(
				'description'       => __( 'Filter by source ID (action ID or widget ID).', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'status'           => array(
				'description'       => __( 'Filter by status.', 'limb-chatbot' ),
				'type'              => 'string',
				'enum'              => array( Lead::STATUS_NEW, Lead::STATUS_ARCHIVED ),
				'sanitize_callback' => 'sanitize_text_field',
			),
			'is_qualified'    => array(
				'description'       => __( 'Filter by qualification status.', 'limb-chatbot' ),
				'type'              => 'integer',
				'enum'              => array( 0, 1 ),
				'sanitize_callback' => 'absint',
			),
			'chatbot_user_uuid' => array(
				'description'       => __( 'Filter by chatbot user UUID.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'chatbot_uuid'     => array(
				'description'       => __( 'Filter by chatbot UUID.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'start_date'       => array(
				'description'       => __( 'Filter leads created on or after this date (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'end_date'         => array(
				'description'       => __( 'Filter leads created on or before this date (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'created_at>='     => array(
				'description'       => __( 'Filter leads created on or after this datetime.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'created_at<='     => array(
				'description'       => __( 'Filter leads created on or before this datetime.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'value_search'     => array(
				'description'       => __( 'General search across all lead field values (searches value_string, value_number, value_date).', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'include'          => array(
				'description' => __( 'Include extra data with leads (e.g., values).', 'limb-chatbot' ),
				'type'        => 'array',
			),
		) );
	}

	/**
	 * Returns a collection of lead items.
	 *
	 * Supports advanced filtering, search, and pagination.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.13
	 */
	public function get_items( $request ) {
		try {
			$query_params = $request->get_query_params();

			// Handle lead_values parameter for filtering (associative array: lead_values[field_key]=value)
			if ( ! empty( $query_params['lead_values'] ) && is_array( $query_params['lead_values'] ) ) {
				// Check if it's an associative array (for filtering) or indexed array (for including)
				$is_associative = array_keys( $query_params['lead_values'] ) !== range( 0, count( $query_params['lead_values'] ) - 1 );
				
				if ( $is_associative ) {
					// Associative array: lead_values[email]=john@example.com (for filtering)
					foreach ( $query_params['lead_values'] as $field_key => $value ) {
						$query_params["lead_values[{$field_key}]"] = $value;
					}
					unset( $query_params['lead_values'] );
				} else {
					// Indexed array: lead_values[]=email&lead_values[]=phone (for including in response)
					// Store for use in prepare_item
					$query_params['lead_values_include'] = $query_params['lead_values'];
					unset( $query_params['lead_values'] );
				}
			}

			$leads = $this->repository->get_items( $query_params );
			$items = $this->prepare_collection( $leads, $request );

			return rest_ensure_response( $items ?? [] );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Prepares a collection of lead items for REST response.
	 *
	 * Ensures each lead includes chatbot_user and lead_values with lead_field.
	 *
	 * @param  Collection  $collection  The collection of leads.
	 * @param  WP_REST_Request  $request  The REST request object.
	 *
	 * @return Collection Prepared collection with full data.
	 * @since 1.0.13
	 */
	public function prepare_collection( $collection, $request ) {
		$collection = parent::prepare_collection( $collection, $request );

		if ( $collection->is_empty() ) {
			return $collection;
		}

		// Prepare each item with full data
		foreach ( $collection->get() as $lead ) {
			$this->prepare_item( $lead, $request );
		}

		return $collection;
	}

	/**
	 * Prepares a single lead item for REST response.
	 *
	 * Always includes chatbot_user and lead_values (with lead_field) for full data access.
	 *
	 * @param  mixed  $item  The item to prepare (Lead instance).
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Lead|WP_Error The prepared item or WP_Error if invalid.
	 * @throws \Exception
	 * @since 1.0.13
	 */
	public function prepare_item( $item, $request ) {
		if ( is_a( $item, WP_Error::class ) ) {
			return $item;
		}
		if ( ! is_a( $item, Lead::class ) ) {
			throw new \Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}

		// Always include chatbot_user
		if ( method_exists( $item, 'chatbot_user' ) ) {
			$item->included['chatbot_user'] = $item->chatbot_user();
		}

		// Always include lead_values with lead_field
		if ( method_exists( $item, 'lead_values' ) ) {
			$values = $item->lead_values();
			$values_with_fields = [];
			
			// Get field keys to include (if specified via lead_values[] indexed array)
			// This was converted to lead_values_include in get_items
			$include_field_keys = $request->get_param( 'lead_values_include' );
			// Also check if lead_values[] was passed directly (indexed array)
			$lead_values_param = $request->get_param( 'lead_values' );
			if ( ! empty( $lead_values_param ) && is_array( $lead_values_param ) ) {
				$is_indexed = array_keys( $lead_values_param ) === range( 0, count( $lead_values_param ) - 1 );
				if ( $is_indexed ) {
					$include_field_keys = $lead_values_param;
				}
			}
			$include_field_keys = ! empty( $include_field_keys ) && is_array( $include_field_keys ) 
				? array_map( 'sanitize_key', $include_field_keys ) 
				: null;
			
			foreach ( $values as $value ) {
				// Get the field to check its field_key
				$field = null;
				if ( method_exists( $value, 'lead_field' ) ) {
					$field = $value->lead_field();
				}
				
				// If lead_values_include is specified, filter by field_key
				if ( $include_field_keys !== null && $field ) {
					if ( ! in_array( $field->field_key, $include_field_keys, true ) ) {
						continue; // Skip this value if not in include list
					}
				}
				
				$value_data = [
					'id'           => $value->id,
					'lead_id'      => $value->lead_id,
					'field_id'     => $value->field_id,
					'value_string' => $value->value_string,
					'value_number' => $value->value_number,
					'value_date'   => $value->value_date,
					'value'        => $value->get_value(),
				];
				
				// Include lead_field object
				if ( $field ) {
					$value_data['lead_field'] = [
						'id'        => $field->id,
						'field_key' => $field->field_key,
						'label'     => $field->label,
						'data_type' => $field->data_type,
					];
				}
				
				$values_with_fields[] = $value_data;
			}
			
			$item->included['lead_values'] = $values_with_fields;
		}

		// Handle additional included relations
		$include = $request->get_param( 'include' );
		if ( ! empty( $include ) && is_array( $include ) ) {
			foreach ( $include as $relation ) {
				if ( method_exists( $item, $relation ) && ! isset( $item->included[ $relation ] ) ) {
					$item->included[ $relation ] = $item->{$relation}();
				}
			}
		}

		return $item;
	}

	/**
	 * Returns a single lead item by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.13
	 */
	public function get_item( $request ) {
		try {
			$lead = $this->repository->get_item( $request->get_param( 'id' ) );

			return rest_ensure_response( $this->prepare_item( $lead, $request ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Returns the JSON schema for a lead item.
	 *
	 * Defines structure and validation rules for the lead resource.
	 *
	 * @return array JSON schema array.
	 * @since 1.0.13
	 */
	public function get_item_schema() {
		if ( $this->schema ) {
			return $this->schema;
		}

		$this->schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'lead',
			'type'       => 'object',
			'properties' => array(
				'id'                 => array(
					'description' => __( 'Unique identifier for the lead.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'chatbot_user_uuid'  => array(
					'description' => __( 'Chatbot user UUID who submitted the lead.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'chatbot_uuid'       => array(
					'description' => __( 'Chatbot UUID (optional).', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'source_type'        => array(
					'description' => __( 'Source type (action or widget).', 'limb-chatbot' ),
					'type'        => 'string',
					'enum'        => array( Lead::SOURCE_TYPE_ACTION, Lead::SOURCE_TYPE_WIDGET ),
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'source'             => array(
					'description' => __( 'Source ID (action ID or widget ID).', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'source_title'       => array(
					'description' => __( 'Source title (action title or widget title).', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'created_at'         => array(
					'description' => __( 'When the lead was captured.', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'status'             => array(
					'description' => __( 'Lead status.', 'limb-chatbot' ),
					'type'        => 'string',
					'enum'        => array( Lead::STATUS_NEW, Lead::STATUS_ARCHIVED ),
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'is_qualified'       => array(
					'description' => __( 'Whether the lead is qualified.', 'limb-chatbot' ),
					'type'        => 'integer',
					'enum'        => array( 0, 1 ),
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'include'            => array(
					'description' => __( 'Include extra data with lead (e.g., values).', 'limb-chatbot' ),
					'type'        => 'array',
					'context'     => array( 'view' ),
				),
			),
		);

		return $this->schema;
	}
}
