<?php

namespace Limb_Chatbot\Includes\Services\Actions\Executors;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Action_Callback;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Services\Actions\Action_Callback_Execution_Context;

/**
 * Class Email_Callback_Executor
 *
 * Executes email callbacks using WordPress wp_mail().
 * Supports HTML emails, templates, and variable substitution.
 *
 * @package Limb_Chatbot\Includes\Services\Actions\Executors
 * @since 1.0.0
 */
class Email_Callback_Executor extends Abstract_Callback_Executor {

	/**
	 * Get callback type
	 *
	 * @return string
	 */
	public function get_type(): string {
		return Action_Callback::TYPE_EMAIL;
	}

	/**
	 * Get required configuration fields
	 *
	 * @return array
	 */
	public function get_required_fields(): array {
		return [ 'to', 'subject' ];
	}

	/**
	 * Execute email callback
	 *
	 * @param  array  $config  Configuration
	 * @param  Action_Callback_Execution_Context  $context  Execution context
	 *
	 * @return Callback_Executor_Response Response data
	 * @throws Exception On execution failure
	 */
	public function execute_callback(
		array $config,
		Action_Callback_Execution_Context $context
	): Callback_Executor_Response {
		$to      = $config['to'];
		$subject = $this->build_subject_with_identifier( (string) $config['subject'], $context );
		$body    = $this->get_email_body( $config );
		$headers = $this->build_headers( $config );

		// Send email
		$result = wp_mail( $to, $subject, $body, $headers );

		if ( ! $result ) {
			throw new Exception( 'Failed to send email' );
		}

		// Return successful response with email metadata
		$email_data = [
			'sent'    => true,
			'to'      => $to,
			'subject' => $subject,
			'body'    => $body,
			'sent_at' => current_time( 'mysql', true ),
		];

		return Callback_Executor_Response::success(
			$email_data,
			[
				'to'      => $to,
				'from'    => $config['from'] ?? get_option( 'admin_email' ),
				'subject' => $subject,
			]
		);
	}

	/**
	 * Get email body
	 *
	 * @param  array  $config  Configuration
	 *
	 * @return string
	 */
	private function get_email_body( array $config ): string {
		// Use body_html if available, otherwise body_text
		if ( ! empty( $config['body_html'] ) ) {
			return $config['body_html'];
		}

		if ( ! empty( $config['body_text'] ) ) {
			return $config['body_text'];
		}

		// Fallback to body field
		return $config['body'] ?? '';
	}

	/**
	 * Build email headers
	 *
	 * @param  array  $config  Configuration
	 *
	 * @return array
	 */
	private function build_headers( array $config ): array {
		$headers = [];

		// Set From header
		if ( ! empty( $config['from'] ) ) {
			$from_name = $config['from_name'] ?? '';
			if ( $from_name ) {
				$headers[] = sprintf( 'From: %s <%s>', $from_name, $config['from'] );
			} else {
				$headers[] = sprintf( 'From: %s', $config['from'] );
			}
		}

		// Set Reply-To header
		if ( ! empty( $config['reply_to'] ) ) {
			$headers[] = sprintf( 'Reply-To: %s', $config['reply_to'] );
		}

		// Set CC
		if ( ! empty( $config['cc'] ) ) {
			if ( is_array( $config['cc'] ) ) {
				foreach ( $config['cc'] as $cc_email ) {
					$headers[] = sprintf( 'Cc: %s', $cc_email );
				}
			} else {
				$headers[] = sprintf( 'Cc: %s', $config['cc'] );
			}
		}

		// Set BCC
		if ( ! empty( $config['bcc'] ) ) {
			if ( is_array( $config['bcc'] ) ) {
				foreach ( $config['bcc'] as $bcc_email ) {
					$headers[] = sprintf( 'Bcc: %s', $bcc_email );
				}
			} else {
				$headers[] = sprintf( 'Bcc: %s', $config['bcc'] );
			}
		}

		// Set content type (HTML or plain text)
		if ( ! empty( $config['body_html'] ) ) {
			$headers[] = 'Content-Type: text/html; charset=UTF-8';
		} else {
			$headers[] = 'Content-Type: text/plain; charset=UTF-8';
		}

		// Add custom headers
		if ( ! empty( $config['headers'] ) && is_array( $config['headers'] ) ) {
			foreach ( $config['headers'] as $header_name => $header_value ) {
				$headers[] = sprintf( '%s: %s', $header_name, $header_value );
			}
		}

		return $headers;
	}

	/**
	 * Append a compact identifier to callback email subjects.
	 *
	 * This prevents mailbox clients from collapsing separate callback emails
	 * that otherwise share an identical subject.
	 *
	 * @param string $subject
	 * @param Action_Callback_Execution_Context $context
	 * @return string
	 */
	private function build_subject_with_identifier( string $subject, Action_Callback_Execution_Context $context ): string {
		$source = array_merge(
			is_array( $context->get_variables() ) ? $context->get_variables() : [],
			is_array( $context->get_parameters() ) ? $context->get_parameters() : []
		);
		$submission = $context->get_submission();

		$candidate = (string) ( $submission->get_id() ?? '' );
		if ( '' === trim( $candidate ) ) {
			$candidate = (string) ( $submission->get_action_id() ?? '' );
		}
		foreach ( [ 'entry_id', 'lead_id', 'chat_uuid', 'chat_id', 'submission_id', 'id' ] as $key ) {
			if ( '' !== trim( $candidate ) ) {
				break;
			}

			if ( isset( $source[ $key ] ) && ( is_scalar( $source[ $key ] ) || null === $source[ $key ] ) ) {
				$candidate = (string) $source[ $key ];
			}
		}

		$candidate = trim( $candidate );
		if ( '' === $candidate ) {
			return $subject;
		}

		return sprintf( '%s [#%s]', $subject, $candidate );
	}

	/**
	 * Validate email configuration
	 *
	 * @param  array  $config  Configuration
	 *
	 * @return bool
	 */
	public function validate_config( array $config ): bool {
		// Check required fields
		if ( ! parent::validate_config( $config ) ) {
			return false;
		}

		// Validate email address format
		if ( ! is_email( $config['to'] ) && ! str_starts_with( $config['to'], '{' ) ) {
			throw new \Limb_Chatbot\Includes\Exceptions\Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'To field is invalid.', 'limb-chatbot' )  );
		}

		// Check that at least one body field is present
		if ( empty( $config['body'] ) && empty( $config['body_html'] ) && empty( $config['body_text'] ) ) {
			throw new \Limb_Chatbot\Includes\Exceptions\Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Email body is empty.', 'limb-chatbot' )  );
		}

		return true;
	}
}

