<?php

namespace Limb_Chatbot\Includes\Repositories;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_Meta;
use Limb_Chatbot\Includes\Data_Objects\Setting;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;
use Limb_Chatbot\Includes\Services\Helper;

/**
 * Repository class for managing Chatbot Meta data.
 *
 * @since 1.0.0
 */
class Chatbot_Meta_Repository {

	/**
	 * Creates multiple chatbot meta records in batch.
	 *
	 * @since 1.0.0
	 *
	 * @param array        $data    Array of meta data (associative or list of arrays).
	 * @param bool|null    $preview Whether preview mode is enabled.
	 * @param Chatbot|null $chatbot Optional chatbot object to apply if meta lacks chatbot_id.
	 * @return Chatbot_Meta[] Array of created Chatbot_Meta objects.
	 */
	public function batch_create( array $data, ?bool $preview = false, ?Chatbot $chatbot = null ) {
		foreach ( $this->normalize_array( $data ) as $item ) {
			$item['chatbot_id'] = ! empty( $item['chatbot_id'] ) ? $item['chatbot_id'] : ( $chatbot && ! empty( $chatbot->get_id() ) ? $chatbot->get_id() : null );
			if ( ! isset( $item['chatbot_id'] ) ) {
				$metas = null;
				continue;
			}
			$meta = Chatbot_Meta::create( $item );
			do_action( 'lbaic_parameter_' . ltrim( $meta->get_meta_key(), '_' ) . '_updated', $meta );
			$metas[] = $meta;
		}
		if ( ! empty( $item['chatbot_id'] ) && ! $preview ) {
			$this->remove_preview_metas( $item['chatbot_id'] );
		}

		return $metas ?? [];
	}

	/**
	 * Creates a single chatbot meta record.
	 *
	 * @since 1.0.0
	 *
	 * @param array $data The meta data to create.
	 * @return Chatbot_Meta|null
	 */
	public function create( array $data ): ?Chatbot_Meta {
		return Chatbot_Meta::create( $data );
	}

	/**
	 * Updates or creates chatbot meta data in batch.
	 *
	 * @param  array  $data  Array of meta items.
	 * @param  bool|null  $preview  Whether this is preview-only update.
	 * @param  Chatbot|null  $chatbot  The chatbot object to reference for IDs.
	 *
	 * @return Chatbot_Meta[] Updated or created meta objects.
	 * @throws Exception
	 * @since 1.0.0
	 *
	 */
	public function batch_update( array $data, ?bool $preview = false, ?Chatbot $chatbot = null ): array {
		foreach ( $this->normalize_array( $data ) as $item ) {
			$item['chatbot_id'] = ! empty( $item['chatbot_id'] ) ? $item['chatbot_id'] : ( $chatbot && ! empty( $chatbot->get_id() ) ? $chatbot->get_id() : null );
			if ( ! isset( $item['chatbot_id'] ) ) {
				$metas = null;
				continue;
			}
			$meta = $chatbot->has_meta( $item['meta_key'] ) ? Chatbot_Meta::update( [ 'meta_key' => $item['meta_key'], 'chatbot_id' => $chatbot->get_id() ], [ 'meta_value' => $item['meta_value'] ] )
				: Chatbot_Meta::create( $item );
			if ( ! $preview ) {
				do_action( 'lbaic_parameter_' . ltrim( $meta->get_meta_key(), '_' ) . '_updated', $meta );
			} else {
				$parameter = str_replace( Setting::PREVIEW_POSTFIX, '', $meta->get_meta_key() );
				do_action( 'lbaic_parameter_' . ltrim( $parameter, '_' ) . '_preview_updated', $meta );
			}
			$metas[] = $meta;
		}
		if ( ! empty( $item['chatbot_id'] ) && ! $preview ) {
			$this->remove_preview_metas( $item['chatbot_id'] );
		}

		return $metas ?? [];
	}

	/**
	 * Retrieves meta entries matching the given parameters.
	 *
	 * @since 1.0.0
	 *
	 * @param array|null $params Optional filter parameters.
	 * @return Data_Object_Collection A collection of Chatbot_Meta objects.
	 */
	public function get_items( ?array $params = [] ): Data_Object_Collection {
		return Chatbot_Meta::where( $params );
	}

	/**
	 * Normalizes input meta data to an array of meta_key/meta_value pairs.
	 *
	 * @since 1.0.0
	 *
	 * @param array $metas Associative or list-based meta array.
	 * @return array Normalized array with keys `meta_key` and `meta_value`.
	 */
	public function normalize_array( array $metas ): array {
		if ( Helper::is_assoc( $metas ) ) {
			return array_map( fn( $key, $value ) => [ 'meta_key' => $key, 'meta_value' => $value ], array_keys( $metas ), $metas );
		}

		return $metas;
	}

	/**
	 * Removes all `.preview` metas for the given chatbot.
	 *
	 * @since 1.0.0
	 *
	 * @param int $chatbot_id Chatbot ID to clean preview metas for.
	 * @return void
	 */
	public function remove_preview_metas( int $chatbot_id ): void {
		// TODO make more flexible ::delete method needed to call only ::delete and that's it. Start supporting double LIKE params support
		$metas = Chatbot_Meta::where( [ 'meta_keyLIKE' => '%.preview', 'chatbot_id' => $chatbot_id ] )->get();
		foreach ( $metas as $meta ) {
			Chatbot_Meta::delete( [ 'meta_key' => $meta->get_meta_key(), 'chatbot_id' => $chatbot_id ] );
		}
	}
}