<?php

namespace Limb_Chatbot\Includes\Widgets\Callbacks;

use Limb_Chatbot\Includes\Data_Objects\Chatbot;
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\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
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;
use Limb_Chatbot\Includes\Widgets\Items\Lead_Capture_Widget_Item;

/**
 * Lead Capture Widget Callback
 *
 * Handles lead capture form submissions from widgets.
 * Maps fields based on config.lead_capture_map_field (Lead_Field ID) only.
 * Only processes fields that have a valid lead_capture_map_field configuration.
 *
 * @since 1.0.13
 */
class Lead_Capture_Callback implements Widget_Callback_Interface {

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

	/**
	 * Execute the lead capture callback.
	 *
	 * @param  mixed  $widget_item  Lead capture widget item.
	 * @param  array  $params  Form field values.
	 * @param  Chatbot  $chatbot  Chatbot instance.
	 *
	 * @return array Response data.
	 * @throws Exception
	 * @since 1.0.13
	 */
	public function execute( $widget_item, array $params, Chatbot $chatbot ): array {
		// Validate widget item type
		if ( ! $widget_item instanceof Lead_Capture_Widget_Item ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'Invalid widget item type', 'limb-chatbot' ) );
		}

		$current_user = User_Manager::instance()->get_current_user();

		// Get widget fields
		$widget_fields = $widget_item->get_fields();

		// Collect lead fields using config mapping only
		$lead_fields_data = $this->collect_lead_fields( $widget_fields, $params );

		if ( empty( $lead_fields_data ) ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'No lead capture fields with valid mapping found', 'limb-chatbot' ) );
		}

		// Create lead record in database
		$lead = $this->create_lead_record( $current_user, $widget_item, $chatbot );

		if ( ! $lead instanceof Lead ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'Failed to create lead record', 'limb-chatbot' ) );
		}

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

		// Update user metadata if name/email fields were captured
		$this->update_user_metadata_if_needed( $lead_fields_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(
			$chatbot,
			$admin_users,
			Lead_Captured_Notification_Type::TYPE_KEY,
			[ 'lead_id' => $lead->get_id() ]
		);

		return [
			'success' => true,
			'message' => __( 'Lead capture successful', 'limb-chatbot' ),
			'data'    => [
				'widget_id'   => $widget_item->id,
				'params'      => $params,
				'user_id'     => $current_user->ID,
				'captured_at' => gmdate( 'c' ),
			],
		];
	}

	/**
	 * Collect lead fields from widget fields and params using config mapping
	 *
	 * Checks each widget field for lead_capture_map_field ID and matches with Lead_Field.
	 * Returns only fields that have a matching Lead_Field ID.
	 *
	 * @param  array  $widget_fields  The widget field definitions
	 * @param  array  $params  The submitted form data
	 *
	 * @return array Array of field data with lead_field_id, data_type, value, and field_key
	 * @since 1.0.14
	 */
	private function collect_lead_fields( array $widget_fields, array $params ): array {
		$lead_fields = [];

		foreach ( $widget_fields as $widget_field ) {
			if ( ! is_array( $widget_field ) ) {
				continue;
			}

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

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

			// Get field value from submitted data
			$field_name = $widget_field['name'] ?? null;
			if ( empty( $field_name ) ) {
				continue;
			}

			$field_value = $params[ $field_name ] ?? null;

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

		return $lead_fields;
	}


	/**
	 * Get lead field ID from widget field config
	 *
	 * Returns the lead_capture_map_field ID if configured, null otherwise.
	 *
	 * @param  array  $widget_field  The widget field definition
	 *
	 * @return int|null
	 * @since 1.0.14
	 */
	private function get_lead_field_id_from_config( array $widget_field ): ?int {
		$config = $widget_field['config'] ?? null;
		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 for widget submission
	 *
	 * @param  mixed  $user  The current user
	 * @param  Lead_Capture_Widget_Item  $widget_item  The widget item
	 * @param  Chatbot  $chatbot  The chatbot instance
	 *
	 * @return Lead|null The created lead or null on failure
	 * @throws \Exception
	 * @since 1.0.13
	 */
	private function create_lead_record( $user, Lead_Capture_Widget_Item $widget_item, Chatbot $chatbot ): ?Lead {
		try {
			$lead = new Lead();
			$lead->set_chatbot_user_uuid( $user->get_uuid() );
			$lead->set_chatbot_uuid( $chatbot->uuid );
			$lead->set_source_type( Lead::SOURCE_TYPE_WIDGET );
			$lead->set_source( $widget_item->id );
			$lead->set_source_title( $widget_item->get_heading() );
			$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 widget 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.14
	 */
	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.14
	 */
	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 );
		}
	}

	/**
	 * Update user metadata if name/email fields were captured
	 *
	 * Checks for fields with field_key 'name' or 'email' and updates user metadata accordingly.
	 *
	 * @param  array  $lead_fields_data  The collected lead fields data
	 *
	 * @return void
	 * @throws \Exception
	 * @since 1.0.14
	 */
	private function update_user_metadata_if_needed( array $lead_fields_data ): void {
		$name  = null;
		$email = null;
		$user  = User_Manager::instance()->get_current_user();
		$user->update_meta( 'captured_at', current_time( 'mysql' ) );
		$user->update_meta( 'captured_state', 1 );

		foreach ( $lead_fields_data as $field_data ) {
			$field_key = $field_data['field_key'] ?? '';
			$value     = $field_data['value'] ?? null;

			if ( empty( $value ) ) {
				continue;
			}

			// Check if field_key contains 'name' (case-insensitive)
			if ( stripos( $field_key, 'name' ) !== false ) {
				$name = sanitize_text_field( $value );
			}

			// Check if field_key contains 'email' (case-insensitive)
			if ( stripos( $field_key, 'email' ) !== false ) {
				$email = sanitize_email( $value );
			}
		}

		// Only update metadata if we found name or email
		if ( $name || $email ) {
			if ( $name ) {
				$user->update_meta( 'name', $name );
			}
			if ( $email ) {
				$user->update_meta( 'email', $email );
			}
		}
	}
}
