<?php

namespace Limb_Chatbot\Includes\Widgets\Data;

use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Submission;
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\Data_Objects\Widget;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Factories\Parameter_Type_Factory;
use Limb_Chatbot\Includes\Interfaces\Conditional_Visibility_Interface;
use Limb_Chatbot\Includes\Interfaces\Widget_Callback_Interface;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
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;
use Limb_Chatbot\Includes\Services\User_Manager;

class Form_Widget_Config extends Abstract_Widget_Config implements Widget_Callback_Interface {
	const TYPE = 'form';

	public function get_dependencies() {
		$dependencies = [];
		$action       = Action::find( $this->get_config_value( 'action_id' ) );
		if ( ! $action instanceof Action || $action->get_type() != Action::TYPE_STATIC ) {
			return $dependencies;
		}
		$dependencies['action']    = $action->with( 'parameters' );
		$chatbot_user              = User_Manager::instance()->get_current_user();
		$submit_state              = ! Action_Submission::where( [
			'chatbot_user_id' => $chatbot_user->get_id(),
			'action_id'       => $this->get_config_value( 'action_id' )
		], 1, 1 )->is_empty();
		$dependencies['submitted'] = $submit_state;

		return $dependencies;
	}

	public function execute_callback( Widget $widget, $params ): array {
		$action            = Action::find( $this->get_config_value( 'action_id' ) );
		$current_user      = User_Manager::instance()->get_current_user();
		$widget_parameters = $this->get_widget_parameters( $widget );
		if ( $widget_parameters->is_empty() ) {
			throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
				__( 'The form does not have parameters', 'limb-chatbot' ) );
		}

		// Collect lead fields using config mapping only
		$lead_fields_data = $this->collect_lead_fields( $widget_parameters, $params );
		if ( ! empty( $lead_fields_data ) ) {
			try {
				$this->save_lead_record( $action, $lead_fields_data, $current_user, $widget );
			} catch ( \Throwable $e ) {
				// No lead record created (or lead creation failed) — still
				// register a "new form entry" event for admins.
				$this->dispatch_admin_new_form_entry_event(
					$action,
					$current_user,
					$widget,
					count( $lead_fields_data )
				);
				throw $e;
			}
		} else {
			// No lead will be created — still register a "new form entry" event.
			$this->dispatch_admin_new_form_entry_event( $action, $current_user, $widget, 0 );
		}
		// Update user metadata if name/email fields were captured
		$this->update_user_metadata_if_needed( $lead_fields_data, $widget );
		$this->create_action_submission( $params, $action, $current_user );

		return [
			'success' => true,
			'message' => $action->get_config_value( 'callback_text' ) ?? __( 'Submitted successfully', 'limb-chatbot' ),
			'data'    => [
				'widget_uuid' => $widget->get_uuid(),
				'params'      => $params,
				'captured_at' => current_time( 'mysql' ),
			],
		];
	}

	/**
	 * Extract widget fields from both legacy widget item objects and normalized arrays.
	 *
	 * @param $widget
	 *
	 * @return Collection
	 */
	private function get_widget_parameters( $widget ): Collection {
		$action_id = $this->get_config_value( 'action_id' );

		return Parameter::where( [ 'action_id' => $action_id ] );
	}

	/**
	 * 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  Collection  $parameters
	 * @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( Collection $parameters, array $params ): array {
		$lead_fields = [];

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

			// Check if this field has a lead_capture_map_field ID
			$lead_field_id = $parameter->get_config_value( Lead_Field::EXTERNAL_MAPPING_KEY );
			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  = $parameter->get_name();
			$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;
	}

	private function save_lead_record(
		Action $action,
		array $lead_fields_data,
		?Chatbot_User $current_user,
		Widget $widget
	) {
		// Create lead record in database
		$lead = new Lead();
		$lead->set_chatbot_user_uuid( $current_user->get_uuid() );
		$lead->set_chatbot_uuid( null );
		$lead->set_source_type( Lead::SOURCE_TYPE_WIDGET );
		$lead->set_source( $widget->get_id() );
		$lead->set_source_title( $action->get_title() );
		$lead->set_created_at( current_time( 'mysql', true ) );
		$lead->set_status( Lead::STATUS_NEW );

		$lead->save();


		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 ) {
			$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 = Helper::validate_value_type( $value, $data_type );
			if ( $value === null ) {
				continue;
			}
			// 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();
		}

		//todo role based notifications
		// 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,
				null,
				null,
				[
					'lead_id'   => $lead->get_id(),
					'chat_uuid' => null,
				]
			);
		}

		// 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() ]
		);
	}

	/**
	 * Dispatch a "new form entry" event to *website admins only* when the
	 * form submission did not create any lead records.
	 *
	 * This path intentionally sends no email; it is solely for event
	 * registration in the channel-event queue.
	 *
	 * @param  Action  $action
	 * @param  Chatbot_User|null  $current_user
	 * @param  Widget  $widget
	 * @param  int  $lead_fields_captured_count
	 *
	 * @return void
	 */
	private function dispatch_admin_new_form_entry_event(
		Action $action,
		?Chatbot_User $current_user,
		Widget $widget,
		int $lead_fields_captured_count
	): void {
		try {
			$admin_wp_user_ids = $this->get_admin_wp_user_ids();
			$chatbot_user_uuid = $current_user instanceof Chatbot_User ? $current_user->get_uuid() : null;

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

			// Queue unified email notification for action entry.
			// This path does not have a chat UUID; templates will show an empty preview.
			Notification_Bootstrap::get_service()->queue(
				$current_user,
				Action_Entry_Notification_Type::TYPE_KEY,
				[
					'chat_uuid' => null,
					'action_id' => $action->get_id(),
				]
			);
		} 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;
	}

	/**
	 * 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
	 * @param  Widget  $widget
	 *
	 * @return void
	 * @throws \Exception
	 * @since 1.0.14
	 */
	private function update_user_metadata_if_needed( array $lead_fields_data, Widget $widget ): void {
		$name  = null;
		$email = null;
		$user  = User_Manager::instance()->get_current_user();

		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 );
			}
		}
	}

	private function create_action_submission( $params, ?Action $action, Chatbot_User $user ) {
		$submission = new Action_Submission();
		$submission->set_action_id( $action->get_id() );
		$submission->set_chatbot_user_id( $user->get_id() );
		$submission->set_callback_results( [] );
		$submission->set_chat_uuid( null );
		$submission->set_extracted_vars( [] );
		$submission->set_status( Action_Submission::STATUS_SUCCESS );
		$submission->set_created_at( current_time( 'mysql', true ) );
		$submission->set_error_message( null );
		$submission->set_action_data( $params );
		$submission->save();
	}

	public function sanitize_and_validate_params( array $params, Widget_Config_Interface $config_handler ): array {
		$normalized = [];
		$action_id  = $config_handler->get_config_value( 'action_id' );
		$parameters = Parameter::where( [ 'action_id' => $action_id ] );
		if ( $parameters->is_empty() ) {
			return [];
		}
		$factory = new Parameter_Type_Factory();
		foreach ( $parameters as $parameter ) {
			if ( ! $parameter instanceof Parameter ) {
				continue;
			}
			$has_param   = array_key_exists( $parameter->get_name(), $params );
			$param_value = $has_param ? $params[ $parameter->get_name() ] : null;
			if ( null !== $param_value && '' !== $param_value ) {
				$parameter_type = $factory->make( $parameter->get_type() );
				$sanitized      = $parameter_type->sanitize( $param_value, $parameter );
				if ( ! $parameter_type->validate( $sanitized, $parameter ) ) {
					$error = $parameter_type->get_first_error();
					throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
						$error ?: __( 'Validation failed', 'limb-chatbot' ) );
				}
				$normalized[ $parameter->get_name() ] = $sanitized;
			}
		}

		return $normalized;
	}

	public function sanitize( $data ): array {
		$data = is_array( $data ) ? $data : [];

		return [
			'action_id' => $this->sanitize_int( $data['action_id'] ?? 0, 0 ),
			'required'  => $this->sanitize_bool( $data['required'] ?? false, false ),
		];
	}

	public function get_validation_errors( $data ): array {
		if ( empty( $data['action_id'] ) ) {
			return [
				'action_id' => __( 'Select a form action for this widget.', 'limb-chatbot' ),
			];
		}
		$action = Action::find( $data['action_id'] );
		if ( ! $action instanceof Action || $action->get_type() != Action::TYPE_STATIC ) {
			return [
				'action_id' => __( 'The selected form action is invalid or no longer available.', 'limb-chatbot' ),
			];
		}

		return [];
	}

	public function validate( $data ): bool {
		return empty( $this->get_validation_errors( $data ) );
	}
}
