<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan;
use Limb_Chatbot\Includes\Data_Objects\Action_Submission;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_User;
use Limb_Chatbot\Includes\Data_Objects\Lead;
use Limb_Chatbot\Includes\Data_Objects\Lead_Field;
use Limb_Chatbot\Includes\Data_Objects\Lead_Value;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\User_Manager;
use Limb_Chatbot\Includes\Services\User_Sync_Tracker;
use Limb_Chatbot\Includes\Services\Channel_Events\Channel_Event_Bootstrap;
use Limb_Chatbot\Includes\Services\Channel_Events\Channel_Event_Types;
use Limb_Chatbot\Includes\Services\Notifications\Notification_Bootstrap;
use Limb_Chatbot\Includes\Services\Notifications\Types\Lead_Captured_Notification_Type;
use Limb_Chatbot\Includes\Services\Notifications\Types\Action_Entry_Notification_Type;

/**
 * Class Lead_Capture_Handler
 *
 * Handles lead capture field processing during action submissions.
 *
 * Checks for lead_capture_map_field IDs in parameter config and matches them
 * with Lead_Field IDs. Creates Lead records only when field mappings match.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.13
 */
class Lead_Capture_Handler {

	/**
	 * Config key for lead capture field mapping
	 */
	const CONFIG_LEAD_CAPTURE_MAP_FIELD = 'lead_capture_map_field';

	/**
	 * Process lead capture for action submission
	 *
	 * Checks lead_capture_map_field IDs in parameter config and matches with Lead_Field IDs.
	 * Creates Lead records only when field mappings match.
	 *
	 * @param  Action_Submission  $submission  The action submission with collected data
	 * @param  Action_Plan  $action_plan  The action plan containing parameter definitions
	 * @param  Chat  $chat
	 *
	 * @return void
	 * @since 1.0.13
	 */
	public function process_lead_capture( Action_Submission $submission, Action_Plan $action_plan, Chat $chat ): void {
		try {
			// Get current user
			$current_user = User_Manager::instance()->get_current_user();

			// Get action parameters
			$parameters = $action_plan->get_parameters();
			if ( empty( $parameters ) ) {
				return;
			}

			// Get collected action data
			$action_data = $submission->get_action_data();
			if ( empty( $action_data ) || ! is_array( $action_data ) ) {
				return;
			}

			// Check for lead fields and collect them
			$lead_fields_data = $this->collect_lead_fields( $parameters, $action_data );
			if ( empty( $lead_fields_data ) ) {
				// No lead will be created — still register a "new form entry" event.
				$this->dispatch_admin_new_form_entry_event(
					$current_user,
					$action_plan,
					$submission,
					$chat,
					0
				);

				return;
			}

			// Create lead record in database
			$lead = $this->create_lead_record(
				$current_user,
				$action_plan,
				$submission,
				$chat
			);

			if ( ! $lead instanceof Lead ) {
				Helper::log( 'Failed to create lead record', __METHOD__ );

				// Lead creation failed — register "new form entry".
				$this->dispatch_admin_new_form_entry_event(
					$current_user,
					$action_plan,
					$submission,
					$chat,
					count( $lead_fields_data )
				);

				return;
			}

			// Store field values
			foreach ( $lead_fields_data as $field_data ) {
				$this->store_field_value( $lead, $field_data );
			}

			$chat_uuid = $chat->get_uuid();

			// Always create a channel event row for *website admins only*.
			$admin_chatbot_users = User_Manager::instance()->get_admins();
			$admin_wp_user_ids   = [];
			foreach ( $admin_chatbot_users as $admin ) {
				$wp_user_id = $admin->get_wp_user_id();
				if ( $wp_user_id !== null ) {
					$admin_wp_user_ids[] = (int) $wp_user_id;
				}
			}
			$admin_wp_user_ids = array_values( array_unique( array_filter( $admin_wp_user_ids ) ) );

			if ( ! empty( $admin_wp_user_ids ) ) {
				Channel_Event_Bootstrap::get_dispatcher()->dispatch(
					Channel_Event_Types::ADMIN_NEW_LEAD,
					$admin_wp_user_ids,
					$chat_uuid,
					null,
					[
						'lead_id'   => $lead->get_id(),
						'chat_uuid' => $chat_uuid,
						'action_id' => $action_plan->action()->get_id()
					]
				);
			}

			// Queue unified email notification; recipient resolution (native WP roles,
			// stale rule for manage_options, and custom emails) is handled by
			// Notification_Service via `lbaic.plugin.notifications`.
			Notification_Bootstrap::get_service()->queue(
				$current_user,
				Lead_Captured_Notification_Type::TYPE_KEY,
				[
					'lead_id'   => $lead->get_id(),
					'chat_uuid' => $chat_uuid,
				]
			);

		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );
		}
	}

	/**
	 * Collect lead fields from parameters and action data
	 *
	 * Checks each parameter for lead_capture_map_field ID and matches with Lead_Field.
	 * Returns only fields that have a matching Lead_Field ID.
	 *
	 * @param  array  $parameters  The parameters from action plan
	 * @param  array  $action_data  The submitted action data
	 *
	 * @return array Array of field data with lead_field_id
	 * @since 1.0.13
	 */
	private function collect_lead_fields( array $parameters, array $action_data ): array {
		$lead_fields = [];

		foreach ( $parameters as $parameter_name => $parameter ) {
			if ( ! $parameter instanceof Parameter ) {
				continue;
			}

			// Check if this parameter has a lead_capture_map_field ID
			$lead_field_id = $this->get_lead_field_id_from_parameter( $parameter );
			if ( empty( $lead_field_id ) ) {
				continue;
			}

			// Verify the Lead_Field exists
			$lead_field = Lead_Field::find( $lead_field_id );
			if ( ! $lead_field instanceof Lead_Field ) {
				Helper::log( sprintf( 'Lead field not found: %d', $lead_field_id ), __METHOD__ );
				continue;
			}

			// Get parameter value from submitted data
			$parameter_value = $action_data[ $parameter_name ] ?? null;

			// Store field data with lead_field_id
			$lead_fields[] = [
				'lead_field_id' => $lead_field->get_id(),
				'data_type'     => $lead_field->get_data_type(),
				'value'         => $parameter_value,
			];
		}

		return $lead_fields;
	}

	/**
	 * Get lead field ID from parameter config
	 *
	 * Returns the lead_capture_map_field ID if configured, null otherwise.
	 *
	 * @param  Parameter  $parameter  The parameter to check
	 *
	 * @return int|null
	 * @since 1.0.13
	 */
	private function get_lead_field_id_from_parameter( Parameter $parameter ): ?int {
		$config = $parameter->get_config();
		if ( ! is_array( $config ) ) {
			return null;
		}

		$lead_field_id = $config[ self::CONFIG_LEAD_CAPTURE_MAP_FIELD ] ?? null;

		if ( empty( $lead_field_id ) || ! is_numeric( $lead_field_id ) ) {
			return null;
		}

		return (int) $lead_field_id;
	}

	/**
	 * Create a lead record in the database
	 *
	 * @param  Chatbot_User  $user  The current user
	 * @param  Action_Plan  $action_plan  The action plan
	 * @param  Action_Submission  $submission  The action submission
	 *
	 * @return Lead|null The created lead or null on failure
	 * @since 1.0.13
	 */
	private function create_lead_record( Chatbot_User $user, Action_Plan $action_plan, Action_Submission $submission, Chat $chat ): ?Lead {
		try {
			$lead = new Lead();
			$lead->set_chatbot_user_uuid( $user->get_uuid() );
			$lead->set_source_type( Lead::SOURCE_TYPE_ACTION );
			$lead->set_source( (string) $action_plan->action()->get_id() );
			$lead->set_source_title( $action_plan->action()->get_title() );
			$lead->set_chat_uuid( $chat->uuid ?? null );
			$lead->set_created_at( current_time( 'mysql', true ) );
			$lead->set_status( Lead::STATUS_NEW );

			$lead->save();

			return $lead;
		} catch ( Exception $e ) {
			Helper::log( sprintf( 'Error creating lead record: %s', $e->getMessage() ), __METHOD__ );

			return null;
		}
	}

	/**
	 * Store a field value in the database
	 *
	 * @param  Lead  $lead  The lead record
	 * @param  array  $field_data  Field data containing lead_field_id, data_type, and value
	 *
	 * @return void
	 * @since 1.0.13
	 */
	private function store_field_value( Lead $lead, array $field_data ): void {
		try {
			$lead_field_id = $field_data['lead_field_id'] ?? null;
			$data_type     = $field_data['data_type'] ?? Lead_Field::DATA_TYPE_TEXT;
			$value         = $field_data['value'] ?? null;

			if ( empty( $lead_field_id ) || null === $value || '' === $value ) {
				return;
			}

			// Validate value type before storing
			$value = Helper::validate_value_type( $value, $data_type );
			if ( $value === null ) {
				Helper::log( sprintf( 'Invalid value for data type: %s', $data_type ), __METHOD__ );

				return;
			}

			// Create the field value
			$field_value = new Lead_Value();
			$field_value->set_lead_id( $lead->get_id() );
			$field_value->set_field_id( $lead_field_id );
			$field_value->set_value( $data_type, $value );
			$field_value->save();

		} catch ( Exception $e ) {
			Helper::log( sprintf( 'Error storing field value: %s', $e->getMessage() ), __METHOD__ );
		}
	}

	/**
	 * Dispatch a "new form entry" event to *website admins only* when no
	 * lead record was created.
	 *
	 * This path intentionally sends no email; it is solely for event
	 * registration in the channel-event queue.
	 *
	 * @param  Chatbot_User  $current_user
	 * @param  Action_Plan   $action_plan
	 * @param  Chat          $chat
	 * @param  int           $lead_fields_captured_count
	 *
	 * @return void
	 */
	private function dispatch_admin_new_form_entry_event(
		Chatbot_User $current_user,
		Action_Plan $action_plan,
		Action_Submission $submission,
		Chat $chat,
		int $lead_fields_captured_count
	): void {
		try {
			$admin_wp_user_ids = $this->get_admin_wp_user_ids();
			$chat_uuid = $chat->get_uuid();
			$chatbot_user_uuid = $current_user->get_uuid();

			if ( ! empty( $admin_wp_user_ids ) ) {
				Channel_Event_Bootstrap::get_dispatcher()->dispatch(
					Channel_Event_Types::ADMIN_NEW_FORM_ENTRY,
					$admin_wp_user_ids,
					$chat_uuid,
					$chatbot_user_uuid,
					[
						'action_id'                   => $action_plan->action()->get_id(),
						'lead_created'               => false,
						'lead_fields_captured_count' => $lead_fields_captured_count,
					]
				);
			}

			// Queue unified email notification for action entry.
			Notification_Bootstrap::get_service()->queue(
				$current_user,
				Action_Entry_Notification_Type::TYPE_KEY,
				[
					'chat_uuid'            => $chat_uuid,
					'action_submission_id' => $submission->get_id(),
					'action_id'            => $action_plan->action()->get_id(),
				]
			);
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );
		} catch ( \Throwable $e ) {
			Helper::log( $e, __METHOD__ );
		}
	}

	/**
	 * WordPress user IDs for website administrators (administrator + super_admin).
	 *
	 * @return int[]
	 */
	private function get_admin_wp_user_ids(): array {
		$admin_ids = get_users(
			[
				'role__in' => [ 'administrator', 'super_admin' ],
				'fields'   => 'ID',
			]
		);

		$admin_ids = array_map( 'absint', (array) $admin_ids );
		$admin_ids = array_values( array_unique( array_filter( $admin_ids ) ) );

		return $admin_ids;
	}
}

