<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Lead_Field;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Repositories\Lead_Field_Repository;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for managing Lead Fields via REST API.
 *
 * Provides endpoints for CRU operations on lead fields including:
 * - GET /lead-fields - List all lead fields with filtering and pagination
 * - POST /lead-fields - Create a new lead field
 * - GET /lead-fields/{id} - Get a single lead field by ID
 * - PUT /lead-fields/{id} - Update an existing lead field
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.13
 */
class Lead_Fields_Controller extends Rest_Controller {

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

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

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

	/**
	 * Registers REST routes for lead fields.
	 *
	 * Defines routes for CRU operations (no delete).
	 *
	 * @return void
	 * @since 1.0.13
	 */
	public function register_routes() {
		// GET /lead-fields - List all lead fields
		// POST /lead-fields - Create a new lead field
		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(),
			),
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'create_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// GET /lead-fields/{id} - Get single lead field
		// PUT /lead-fields/{id} - Update lead field
		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_Field::find( $value ) );
						},
					),
				),
			),
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'update_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge(
					array(
						'id' => array(
							'required'          => true,
							'validate_callback' => function ( $value ) {
								return ! empty( Lead_Field::find( $value ) );
							},
						),
					),
					$this->get_endpoint_args_for_item_schema()
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Returns query parameters for the collection endpoint.
	 *
	 * These parameters allow filtering and sorting of lead field 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'           => 'id',
				'enum'              => array( 'id', 'field_key', 'label', 'data_type' ),
				'validate_callback' => 'rest_validate_request_arg',
			),
			'search'       => array(
				'description' => __( 'Search lead fields by label or field key.', 'limb-chatbot' ),
				'type'        => 'string',
			),
			'search_fields' => array(
				'description' => __( 'Fields to search in (e.g., label, field_key).', 'limb-chatbot' ),
				'type'        => 'array',
				'default'     => array( 'label', 'field_key' ),
			),
		) );
	}

	/**
	 * Returns a collection of lead field items.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.13
	 */
	public function get_items( $request ) {
		try {
			$fields = $this->repository->get_items( $request->get_query_params() );
			$items  = $this->prepare_collection( $fields, $request );

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

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

	/**
	 * Creates a new lead field.
	 *
	 * @param  WP_REST_Request  $request  REST request with lead field data.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.13
	 */
	public function create_item( $request ) {
		try {
			$field = $this->repository->create( $request->get_json_params() );

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

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

	/**
	 * Prepares a single lead field item for REST response.
	 *
	 * @param  mixed  $item  The item to prepare (Lead_Field instance).
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Lead_Field|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_Field::class ) ) {
			throw new \Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}

		return $item;
	}

	/**
	 * Returns a single lead field 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 {
			$field = $this->repository->get_item( $request->get_param( 'id' ) );

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

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

	/**
	 * Updates an existing lead field by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request with update data.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.13
	 */
	public function update_item( $request ) {
		try {
			$field = $this->repository->update( $request->get_param( 'id' ), $request->get_json_params() );

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

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

	/**
	 * Returns the JSON schema for a lead field item.
	 *
	 * Defines structure and validation rules for the lead field 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-field',
			'type'       => 'object',
			'properties' => array(
				'id'        => array(
					'description' => __( 'Unique identifier for the lead field.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'field_key' => array(
					'description' => __( 'Unique field key/identifier.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_key',
						'validate_callback' => function ( $value, $request ) {
							if ( ! is_string( $value ) || empty( $value ) ) {
								return false;
							}

							// Check uniqueness (skip if updating same field)
							$existing = Lead_Field::find_by_key( $value );
							if ( $existing instanceof Lead_Field ) {
								$id = $request->get_param( 'id' );
								if ( empty( $id ) || $existing->id !== (int) $id ) {
									return new WP_Error(
										Error_Codes::VALIDATION_INVALID_VALUE,
										__( 'A lead field with this key already exists.', 'limb-chatbot' )
									);
								}
							}

							return true;
						},
					),
				),
				'label'     => array(
					'description' => __( 'Display label for the field.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => true,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							return is_string( $value ) && ! empty( $value );
						},
					),
				),
				'data_type' => array(
					'description' => __( 'Data type for value storage.', 'limb-chatbot' ),
					'type'        => 'string',
					'enum'        => array(
						Lead_Field::DATA_TYPE_TEXT,
						Lead_Field::DATA_TYPE_NUMBER,
						Lead_Field::DATA_TYPE_EMAIL,
						Lead_Field::DATA_TYPE_PHONE,
						Lead_Field::DATA_TYPE_DATE,
						Lead_Field::DATA_TYPE_ATTACHMENT,
					),
					'default'     => Lead_Field::DATA_TYPE_TEXT,
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							$valid_types = array(
								Lead_Field::DATA_TYPE_TEXT,
								Lead_Field::DATA_TYPE_NUMBER,
								Lead_Field::DATA_TYPE_EMAIL,
								Lead_Field::DATA_TYPE_PHONE,
								Lead_Field::DATA_TYPE_DATE,
								Lead_Field::DATA_TYPE_ATTACHMENT,
							);

							return in_array( $value, $valid_types, true );
						},
					),
				),
			),
		);

		return $this->schema;
	}
}
