<?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\Notifications\Notification_Bootstrap;
use Limb_Chatbot\Includes\Services\Notifications\Types\Lead_Captured_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
	 *
	 * @return void
	 * @throws Exception
	 * @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();
			if ( ! $current_user instanceof Chatbot_User || $current_user->get_meta( 'captured_state' ) == 1 ) {
				return;
			}

			// 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 ) ) {
				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__ );

				return;
			}

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

			$admin_users = User_Manager::instance()->get_admins();
			$current_id  = $current_user->get_id();
			if ( $current_id !== null ) {
				$admin_users = array_values( array_filter( $admin_users, function ( $admin ) use ( $current_id ) {
					return $admin->get_id() !== $current_id;
				} ) );
			}
			Notification_Bootstrap::get_service()->queue(
				$chat->get_chatbot(),
				$admin_users,
				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();

			$user->update_meta( 'captured_at', current_time( 'mysql' ) );
			$user->update_meta( 'captured_state', 1 );

			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 ) || empty( $value ) ) {
				return;
			}

			// Validate value type before storing
			$value = $this->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__ );
		}
	}

	/**
	 * Validate and sanitize value based on data type
	 *
	 * @param  mixed  $value  The value to validate
	 * @param  string  $data_type  The expected data type
	 *
	 * @return false|float|string|null The validated value or null if invalid
	 * @since 1.0.13
	 */
	private function validate_value_type( $value, string $data_type ) {
		switch ( $data_type ) {
			case Lead_Field::DATA_TYPE_EMAIL:
				$sanitized = sanitize_email( $value );
				return ! empty( $sanitized ) ? $sanitized : null;

			case Lead_Field::DATA_TYPE_NUMBER:
				if ( ! is_numeric( $value ) ) {
					return null;
				}
				return (float) $value;

			case Lead_Field::DATA_TYPE_PHONE:
				$sanitized = sanitize_text_field( $value );
				// Basic phone validation: at least 7 digits
				$digits = preg_replace( '/\D/', '', $sanitized );
				return strlen( $digits ) >= 7 ? $sanitized : null;

			case Lead_Field::DATA_TYPE_DATE:
				// Validate date format (YYYY-MM-DD or any MySQL datetime format)
				$timestamp = strtotime( $value );
				return $timestamp ? gmdate( 'Y-m-d H:i:s', $timestamp ) : null;

			case Lead_Field::DATA_TYPE_TEXT:
			default:
				return sanitize_text_field( $value );
		}
	}
}

