<?php
/**
 * Base object for form submissions
 *
 * @since 1.0
 */
namespace WPEventGenius\Common\Registration\Submission;

use WPEventGenius\Common\Database;
use WPEventGenius\Common\Registration\Form\Form;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class BaseSubmission implements Submission {

	/**
	 * @var Validator
	 *
	 * @since 1.0
	 */
	protected $validator;

	/**
	 * @var array
	 */
	protected $raw_data;

	/**
	 * @var array
	 *
	 * @since 1.0
	 */
	protected $data;

	/**
	 * @var Form
	 *
	 * @since 1.0
	 */
	protected $form;

	/**
	 * @var array
	 *
	 * @since 1.0
	 */
	protected $errors = array();

	public function __construct( Validator $validator, Form $form ) {
		$this->validator = $validator;
		$this->form = $form;
		$this->form->set_fields();
	}

	/**
	 * Get the list of expected field keys that should be included in the raw data
	 *
	 * @return array
	 *
	 * @since 1.0
	 */
	public function get_expected_fields() {
		$expected_fields = [
			'evge_user_comments', // honeypot field
			'event_id',
			'user_id',
		];

		// Add all form field slugs
		foreach ($this->form->get_fields() as $field) {
			$expected_fields[] = 'evge_' . $field->get_slug();
		}

		return $expected_fields;
	}

	public function get_fields() {
		return $this->form->get_fields();
	}

	/**
	 * @param array $raw_data
	 *
	 * @return mixed|void
	 *
	 * @since 1.0
	 */
	public function set_raw_data( $raw_data ) {
		$this->raw_data = $raw_data;
	}

	/**
	 * @return array
	 *
	 * @since 1.0
	 */
	public function get_raw_data() {
		return $this->raw_data;
	}

	/**
	 * @param string $key
	 * @param mixed $value
	 *
	 * @since 1.0
	 */
	public function set_single_raw_datum( $key, $value ) {
		$this->raw_data[ $key ] = $value;
	}

	/**
	 * @return array|mixed
	 *
	 * @since 1.0
	 */
	public function get_data() {
		return $this->data;
	}

	/**
	 * @param string $key
	 * @param mixed $value
	 *
	 * @since 1.0
	 */
	public function set_single_datum( $key, $value ) {
		$this->data[ $key ] = $value;
	}

	/**
	 * @return mixed|void
	 *
	 * @since 1.0
	 */
	public function normalize() {
		foreach ( $this->raw_data as $key => $value ) {
			// submission data is prefixed to avoid conflicts
			// we should ignore any data that doesn't have the evge prefix for non-admin submissions
			if ( strpos( $key, 'evge_' ) === 0 ) {
				$field_name = str_replace( 'evge_', '', $key ); //

				$this->raw_data[ $field_name ] = $value;
			}
		}
	}

	/**
	 * Check if a field is required for this submission type
	 * Override in subclasses to use different requirement logic
	 *
	 * @param \WPEventGenius\Common\Registration\Field\Field $field The field to check
	 * @return bool True if the field is required
	 *
	 * @since 1.0
	 */
	protected function is_field_required( $field ) {
		return $field->main_is_required();
	}

	/**
	 * @return mixed|void
	 *
	 * @since 1.0
	 */
	public function validate() {
		$submission_data = $this->raw_data;

		// check spam honeypot, error if not empty
		if ( ! empty( $submission_data['evge_user_comments'] ) ) {
			$this->add_error( 'evge_user_comments', 'honeypot' );
		}
		if ( empty( $submission_data['event_id'] ) ) {
			$this->add_error( 'event_id', 'Missing Event ID' );
		}

		$event_id               = (int) $submission_data['event_id'];
		$this->data['event_id'] = $event_id;

		if ( ! empty( $this->raw_data['user_id'] ) ) {
			$this->data['user_id'] = absint( $this->raw_data['user_id'] );
		}

		// Normalize registration types: array of [ type_id, quantity ] per submission (main or guest).
		// Supports: registration_types (array) or legacy event_registration_type_id + quantity.
		$registration_types = $this->normalize_registration_types_from_raw( $event_id );
		$this->data['registration_types'] = $registration_types;
		$total_quantity = $this->sum_registration_types_quantity( $registration_types );
		$this->data['quantity'] = $total_quantity > 0 ? $total_quantity : 1;

		// Enforce minimum quantity when multiple registration types (type selector).
		$min_quantity = 1;
		if ( $this->form && method_exists( $this->form, 'get_min_guest_count' ) ) {
			$min_quantity = max( 1, (int) $this->form->get_min_guest_count() );
		}
		if ( count( $registration_types ) >= 2 && $total_quantity < $min_quantity ) {
			$this->add_error( 'registration_types', __( 'Please make a selection', 'event-genius' ) );
		}

		if ( count( $registration_types ) === 1 ) {
			$this->data['event_registration_type_id'] = (int) $registration_types[0]['type_id'];
		} elseif ( count( $registration_types ) > 1 ) {
			$this->data['event_registration_type_id'] = null;
		}

		// Validate each type/quantity (active for event, capacity, availability).
		if ( $event_id > 0 && ! empty( $registration_types ) ) {
			foreach ( $registration_types as $index => $row ) {
				$type_id   = isset( $row['type_id'] ) ? (int) $row['type_id'] : 0;
				$quantity  = isset( $row['quantity'] ) ? max( 0, (int) $row['quantity'] ) : 0;
				if ( $type_id <= 0 || $quantity <= 0 ) {
					continue;
				}
				$type_validation = $this->validate_registration_type( $event_id, $type_id, $quantity );
				if ( ! $type_validation['valid'] ) {
					$this->add_error( 'registration_types_' . $index, $type_validation['message'] );
					break;
				}
			}
		}

		foreach ( $this->get_fields() as $field ) {
			$value = $field->parse_value( $submission_data );
			if ( Validator::is_absent_submission_value( $value ) && ! $this->is_field_required( $field ) ) {
				$this->data[ $field->get_slug() ] = $value;
			} elseif ( ! $field->is_valid( $this->validator, $value ) ) {
				$this->add_error( $field->get_slug(), $field->get_label() );
			} else {
				$this->data[ $field->get_slug() ] = $value;
			}
		}
	}

	/**
	 * @return mixed|void
	 *
	 * @since 1.0
	 */
	public function sanitize() {
		// Get all valid fields from the form
		$valid_fields = $this->get_fields();
		$sanitized_data = array();

		// Always preserve these system fields with basic sanitization
		$system_fields = array( 'event_id', 'user_id', 'quantity', 'event_registration_type_id' );
		foreach ( $system_fields as $field ) {
			if ( isset( $this->data[ $field ] ) ) {
				$sanitized_data[ $field ] = absint( $this->data[ $field ] );
			}
		}
		if ( isset( $this->data['registration_types'] ) && is_array( $this->data['registration_types'] ) ) {
			$sanitized_data['registration_types'] = array();
			foreach ( $this->data['registration_types'] as $row ) {
				if ( ! is_array( $row ) ) {
					continue;
				}
				$type_id  = isset( $row['type_id'] ) ? absint( $row['type_id'] ) : 0;
				$quantity = isset( $row['quantity'] ) ? max( 0, absint( $row['quantity'] ) ) : 0;
				if ( $type_id > 0 && $quantity > 0 ) {
					$sanitized_data['registration_types'][] = array( 'type_id' => $type_id, 'quantity' => $quantity );
				}
			}
		}

		// Only process fields that are part of the form
		foreach ($valid_fields as $field) {
			$field_slug = $field->get_slug();
			
			// Skip if the field isn't in submitted data
			if (!isset($this->data[$field_slug])) {
				continue;
			}

			$input_value = $this->data[$field_slug];
			$type = $field->get_type();
			
			switch ($type) {
				case 'textarea':
					// Allow newlines and basic HTML
					$new_val = wp_kses(wp_unslash($input_value), [
						'br' => [],
						'p' => [],
						'strong' => [],
						'em' => [],
					]);
					break;

				case 'checkbox':
					// Handle array of checkbox values
					if (is_array($input_value)) {
						$new_val = array_map(function($value) {
							return sanitize_text_field(wp_unslash($value));
						}, $input_value);
					} else {
						$new_val = sanitize_text_field(wp_unslash($input_value));
					}
					break;

				case 'single-checkbox':
					// Ensure boolean value
					$new_val = !empty($input_value);
					break;

				case 'email':
					$new_val = sanitize_email(wp_unslash($input_value));
					break;

				case 'phone':
					// Keep only digits, plus, parentheses, and dashes
					$new_val = preg_replace('/[^\d\+\-\(\)]/', '', wp_unslash($input_value));
					break;

				case 'select':
				case 'radio':
					// Ensure value matches one of the allowed options (string/int "0" must match option value)
					$new_val = sanitize_text_field(wp_unslash($input_value));
					$valid_options = array_column($field->get_options(), 'value');
					$matched = false;
					foreach ( $valid_options as $opt ) {
						if ( (string) $opt === (string) $new_val ) {
							$new_val = $opt;
							$matched = true;
							break;
						}
					}
					if ( ! $matched ) {
						$new_val = '';
					}
					break;

				case 'text':
				default:
					$new_val = sanitize_text_field(wp_unslash($input_value));
					break;
			}

			$sanitized_data[$field_slug] = $new_val;
		}

		// Replace the data with only sanitized values
		$this->data = $sanitized_data;
	}

	/**
	 * Normalize raw_data into a list of registration type rows (type_id, quantity) for this submission.
	 *
	 * Expected POST shape (each submission gets its slice when expected_data is built):
	 *   registration_types[main]      = [ [ type_id => x, quantity => y ], ... ]   (main submission)
	 *   registration_types[guest][n]  = [ [ type_id => x, quantity => y ], ... ]   (guest n, Pro only)
	 *
	 * @param int $event_id Event post ID (for context).
	 * @return array<int, array{ type_id: int, quantity: int }> List of type/quantity pairs.
	 */
	protected function normalize_registration_types_from_raw( $event_id ) {
		$raw = $this->raw_data;
		$out = array();

		if ( empty( $raw['registration_types'] ) || ! is_array( $raw['registration_types'] ) ) {
			return $out;
		}

		foreach ( $raw['registration_types'] as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$type_id  = isset( $row['type_id'] ) ? (int) $row['type_id'] : 0;
			$quantity = isset( $row['quantity'] ) ? max( 0, (int) $row['quantity'] ) : 0;
			if ( $type_id > 0 ) {
				$out[] = array( 'type_id' => $type_id, 'quantity' => $quantity );
			}
		}

		return $out;
	}

	/**
	 * Sum quantity across registration_types rows.
	 *
	 * @param array<int, array{ type_id: int, quantity: int }> $registration_types Normalized list.
	 * @return int Total quantity.
	 */
	protected function sum_registration_types_quantity( array $registration_types ) {
		$sum = 0;
		foreach ( $registration_types as $row ) {
			$sum += isset( $row['quantity'] ) ? max( 0, (int) $row['quantity'] ) : 0;
		}
		return $sum;
	}

	/**
	 * Validate that a registration type is active for the event and has capacity/availability.
	 *
	 * @param int $event_id  Event post ID.
	 * @param int $type_id  Event registration type ID (evge_event_registration_types.id).
	 * @param int $quantity Requested quantity.
	 * @return array{ valid: bool, message: string }
	 */
	protected function validate_registration_type( $event_id, $type_id, $quantity ) {
		$database = new Database();
		$type_id  = (int) $type_id;

		// Free/Pro: virtual single type (id 0); no type selector. Treat as valid.
		if ( $type_id === 0 ) {
			return array( 'valid' => true, 'message' => '' );
		}

		$type = $database->get_event_registration_type( $type_id );

		if ( ! $type || ! is_array( $type ) ) {
			return array(
				'valid'   => false,
				'message' => __( 'This registration type is not available.', 'event-genius' ),
			);
		}

		if ( (int) $type['event_id'] !== (int) $event_id ) {
			return array(
				'valid'   => false,
				'message' => __( 'This registration type is not available for this event.', 'event-genius' ),
			);
		}

		if ( empty( $type['status'] ) || $type['status'] !== 'active' ) {
			return array(
				'valid'   => false,
				'message' => __( 'This registration type is not currently available.', 'event-genius' ),
			);
		}

		// Standard tier: use capacity/sales-window validation when available.
		if ( class_exists( 'WPEventGenius\Standard\Services\RegistrationTypeCapacityService' ) ) {
			$capacity_service = new \WPEventGenius\Standard\Services\RegistrationTypeCapacityService( $database );
			$result           = $capacity_service->validate_type_quantity( $type_id, $quantity );
			if ( ! $result['valid'] ) {
				return array(
					'valid'   => false,
					'message' => $result['message'],
				);
			}
		}

		return array( 'valid' => true, 'message' => '' );
	}

	/**
	 * @return bool
	 *
	 * @since 1.0
	 */
	public function is_valid() {
		return ! empty( $this->data ) && empty( $this->errors );
	}

	/**
	 * @param string $field
	 * @param bool $type
	 *
	 * @return mixed|void
	 *
	 * @since 1.0
	 */
	public function add_error( $field, $type = false ) {
		$this->errors[ $field ] = $type;
	}

	/**
	 * @return array|mixed
	 *
	 * @since 1.0
	 */
	public function get_errors() {
		return $this->errors;
	}

	/**
	 * @return bool
	 *
	 * @since 1.0
	 */
	public function is_edit() {
		return false;
	}

	/**
	 * @since 1.0
	 */
	public function add_user_data() {
	}

}
