<?php

namespace Limb_Chatbot\Includes\Repositories;

use Limb_Chatbot\Includes\Data_Objects\Lead_Field;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;

/**
 * Repository class for managing Lead_Field records.
 *
 * Provides methods for retrieving, creating, and updating lead fields.
 *
 * @package Limb_Chatbot\Includes\Repositories
 * @since 1.0.13
 */
class Lead_Field_Repository {

	/**
	 * Retrieves a collection of lead fields based on given parameters.
	 *
	 * Supports search filtering and pagination.
	 *
	 * @param  array  $params  {
	 *     Optional. Parameters for filtering and pagination.
	 *
	 * @type string $search Search keyword to match against field label or key.
	 * @type string[] $search_fields Fields to apply the search against.
	 * @type int $per_page Number of results per page. Default 10.
	 * @type int $page Current page number. Default 1.
	 * @type string $orderby Field to order by. Default 'id'.
	 * @type string $order Sort order direction. Accepts 'asc' or 'desc'. Default 'desc'.
	 * }
	 *
	 * @return Collection Collection of Lead_Field objects.
	 * @since 1.0.13
	 */
	public function get_items( array $params = [] ): Collection {
		// Handle search
		if ( ! empty( $params['search'] ) && ! empty( $params['search_fields'] ) ) {
			foreach ( $params['search_fields'] as $field ) {
				$params["{$field}LIKE"] = "%{$params['search']}%";
			}
		}

		// Handle ordering
		$order_by = $params['orderby'] ?? 'id';
		$order    = strtoupper( $params['order'] ?? 'DESC' );


		return Lead_Field::where( $params, - 1, - 1, $order_by, $order );
	}

	/**
	 * Creates a new lead field.
	 *
	 * @param  array  $data  Lead field data including field_key, label, and data_type.
	 *
	 * @return Lead_Field|null The created Lead_Field object or null on failure.
	 * @throws Exception
	 * @since 1.0.13
	 */
	public function create( array $data ): ?Lead_Field {
		// Validate required fields
		if ( empty( $data['label'] ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'Label is required.', 'limb-chatbot' ) );
		}

		$key = Helper::text_to_underscore( $data['label'] );
		if ( Lead_Field::find_by_key( $key ) ) {
			throw new Exception( Error_Codes::TECHNICAL_ERROR,
				__( 'A Lead Field with this label already exists', 'limb-chatbot' ) );
		}
		$data['field_key'] = $key;

		return Lead_Field::create( $data );
	}

	/**
	 * Updates an existing lead field.
	 *
	 * @param  int  $id  Lead field ID.
	 * @param  array  $data  Data to update.
	 *
	 * @return Lead_Field|null The updated Lead_Field object or null on failure.
	 * @throws Exception
	 * @since 1.0.13
	 */
	public function update( int $id, array $data ): ?Lead_Field {
		$field = $this->get_item( $id );
		if ( ! $field ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'Lead field not found.', 'limb-chatbot' ) );
		}

		// If field_key is being updated, check for uniqueness
		if ( ! empty( $data['field_key'] ) && $data['field_key'] !== $field->get_field_key() ) {
			$existing = Lead_Field::find_by_key( $data['field_key'] );
			if ( $existing instanceof Lead_Field && $existing->id !== $id ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
					__( 'A lead field with this key already exists.', 'limb-chatbot' ) );
			}
		}

		// Validate data_type if provided
		if ( ! empty( $data['data_type'] ) ) {
			$valid_data_types = [
				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,
			];

			if ( ! in_array( $data['data_type'], $valid_data_types, true ) ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
					__( 'Invalid data type.', 'limb-chatbot' ) );
			}
		}

		return Lead_Field::update( [ 'id' => $id ], $data );
	}

	/**
	 * Retrieves a single lead field by ID.
	 *
	 * @param  int  $id  Lead field ID.
	 *
	 * @return Lead_Field|null The Lead_Field object or null if not found.
	 * @since 1.0.13
	 */
	public function get_item( int $id ): ?Lead_Field {
		return Lead_Field::find( $id );
	}
}
