<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Callback;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Factories\Action_Callback_Executor_Factory;

/**
 * Service class for managing Action_Callback operations.
 *
 * Handles business logic for creating, updating, and validating callbacks.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Action_Callback_Service {

	/**
	 * Create a new callback.
	 *
	 * @param int   $action_id The action ID
	 * @param array $params    Callback configuration
	 *
	 * @return Action_Callback The created callback
	 * @throws Exception If validation fails
	 * @since 1.0.0
	 */
	public function create( int $action_id, array $params ): Action_Callback {
		$action = Action::find( $action_id );
		if ( $action->has_callbacks() ) {
			throw new Exception( Error_Codes::NOT_SUPPORTED, __( 'The multiple callbacks are not supported in the free version', 'limb-chatbot' ) );
		}
		$callback = new Action_Callback();
		$callback->set_action_id( $action_id );

		return $this->validate_and_save( $callback, $params );
	}

	/**
	 * Update an existing callback.
	 *
	 * @param int   $id     Callback ID
	 * @param array $params Updated configuration
	 *
	 * @return Action_Callback The updated callback
	 * @throws Exception If validation fails or callback not found
	 * @since 1.0.0
	 */
	public function update( int $id, array $params ): Action_Callback {
		$callback = Action_Callback::find( $id );
		if ( empty( $callback ) || ! $callback instanceof Action_Callback ) {
			throw new Exception( Error_Codes::NOT_FOUND, __( 'Callback not found.', 'limb-chatbot' ) );
		}
		foreach ( $callback::FILLABLE as $key => $value ) {
			if ( ! array_key_exists( $value, $params ) ) {
				$params[ $value ] = $callback->{$value};
			}
		}
		return $this->validate_and_save( $callback, $params );
	}

	/**
	 * Validate and save callback.
	 *
	 * @param Action_Callback $callback The callback instance
	 * @param array           $params   Parameters to set
	 *
	 * @return Action_Callback The validated and saved callback
	 * @throws Exception If validation fails
	 * @since 1.0.0
	 */
	private function validate_and_save( Action_Callback $callback, array $params ): Action_Callback {
		$this->validate_name( $callback, $params['name'] ?? '' );
		$this->validate_type( $params['type'] ?? '' );
		$this->validate_config( $callback, $params['type'] ?? '', $params['config'] ?? [] );

		$callback->set_name( sanitize_text_field( $params['name'] ?? '' ) );
		$callback->set_title( sanitize_text_field( $params['title'] ?? $params['name'] ?? '' ) );
		$callback->set_description( sanitize_textarea_field( $params['description'] ?? '' ) );
		$callback->set_type( sanitize_text_field( $params['type'] ?? '' ) );
		$callback->set_config( is_array( $params['config'] ?? null ) ? $params['config'] : [] );
		$callback->set_order( absint( $params['order'] ?? 0 ) );
		$callback->set_is_required( in_array( $params['is_required'] ?? 1, [ 1, '1', true ], true ) ? 1 : 0 );
		$callback->set_status( in_array( $params['status'] ?? 1, [ 1, '1', true ], true ) ? 1 : 0 );

		try {
			$callback->save();
		} catch ( \Exception $e ) {
			throw new Exception( Error_Codes::TECHNICAL_ERROR, __( 'Failed to save callback.', 'limb-chatbot' ) );
		}

		return $callback;
	}

	/**
	 * Validate callback name.
	 *
	 * @param string $name The name to validate
	 *
	 * @throws Exception If validation fails
	 * @since 1.0.0
	 */
	private function validate_name( Action_Callback $callback, string $name ): void {
		$name = sanitize_text_field( trim( $name ) );
		if ( empty( $name ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Callback name is required.', 'limb-chatbot' ) );
		}
		if ( ! preg_match( '/^[a-z0-9_]+$/', $name ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Callback name can only contain lowercase letters, numbers, and underscores.', 'limb-chatbot' ) );
		}
		$callbacks = Action_Callback::where( [ 'action_id' => $callback->get_action_id(), 'name' => $name ] );
		if ( ! $callbacks->is_empty() ) {
			$callbacks->each( function ( Action_Callback $item ) use ( $callback ) {
				if ( ! $callback->get_id() || $item->get_id() != $callback->get_id() ) {
					throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Callback names of the action should be unique.', 'limb-chatbot' ) );
				}
			} );
		}
	}

	/**
	 * Validate callback type.
	 *
	 * @param string $type The type to validate
	 *
	 * @throws Exception If validation fails
	 * @since 1.0.0
	 */
	private function validate_type( string $type ): void {
		$type = sanitize_text_field( trim( strtolower( $type ) ) );
		if ( empty( $type ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Callback type is required.', 'limb-chatbot' ) );
		}

		$valid_types = [
			Action_Callback::TYPE_HTTP_REQUEST,
			Action_Callback::TYPE_EMAIL,
			Action_Callback::TYPE_SLACK,
			Action_Callback::TYPE_TRELLO,
			Action_Callback::TYPE_CREATE_POST,
			Action_Callback::TYPE_UPDATE_POST,
			Action_Callback::TYPE_WEBHOOK,
			Action_Callback::TYPE_CUSTOM,
		];

		if ( ! in_array( $type, $valid_types, true ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, sprintf(
				__( 'Invalid callback type "%s".', 'limb-chatbot' ),
				$type
			) );
		}
	}

	/**
	 * Validate callback configuration.
	 *
	 * @param string $type   Callback type
	 * @param null|array  $config Configuration to validate
	 *
	 * @throws Exception If validation fails
	 * @since 1.0.0
	 */
	private function validate_config( Action_Callback $callback, string $type, ?array $config ): void {
		if ( empty( $config ) ) {
			throw new Exception( Error_Codes::VALIDATION_REQUIRED, __( 'Callback configuration is required.', 'limb-chatbot' ) );
		}

		try {
			$executor = ( new Action_Callback_Executor_Factory() )->make( $type );
			if ( ! $executor->validate_config( $config ) ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Invalid callback configuration.', 'limb-chatbot' ) );
			}
		} catch ( \Exception $e ) {
			if ( $e instanceof Exception ) {
				throw $e;
			}
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, $e->getMessage() );
		}
	}
}

