<?php

namespace MyListing\Src\Forms\Fields;

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

use MyListing\Src\Forms\Fields\Traits\Editor_Markup_Helpers;
use MyListing\Src\Forms\Fields\Traits\Validation_Helpers;

class Term_Select_Field extends Base_Field {
	use Editor_Markup_Helpers;
	use Validation_Helpers;

	public function get_posted_value() {
		$field = $this->props;
		$key = $this->key;
		if ( ! empty( $field['terms-template'] ) && in_array( $field['terms-template'], [ 'single-select', 'hierarchy', 'multiselect', 'checklist'] ) ) {
			$template = $field['terms-template'];
		} else {
			$template = 'multiselect';
		}

		$value = ! empty( $_POST[ $key ] ) ? (array) $_POST[ $key ] : [];

		if ( $template === 'single-select' || $template === 'hierarchy' ) {
			return ! empty( $value[0] ) && $value[0] > 0 ? absint( $value[0] ) : '';
		}

		if ( $template === 'multiselect' && $this->props['create_tag'] ) {
			$new_terms = [];
			foreach ( $value as $term ) {
				if ( is_numeric( $term ) ) {
					$maybe_term = get_term( absint( $term ), $this->props['taxonomy'] );
					if ( $maybe_term && ! is_wp_error( $maybe_term ) ) {
						$new_terms[] = $maybe_term->term_id;
						continue;
					}
				}

				$term = sanitize_text_field( $term );
				$term_exists = term_exists( $term, $this->props['taxonomy'] );
				if ( $term_exists !== 0 && $term_exists !== null ) {
					$term_obj = get_term_by( 'name', $term, $this->props['taxonomy'] );
					$new_terms[] = $term_obj->term_id;
					continue;
				}

				$new_item = wp_insert_term( $term, $this->props['taxonomy'] );
				if ( is_wp_error( $new_item ) ) {
					unset( $value[ $term ] );
					continue;
				}

				// Attach current listing type to newly created term
				if ( $this->listing_type && apply_filters( 'mylisting/term-select/attach_listing_type_on_create', false, $this, $new_item ) ) {
					$current_type_id = absint( $this->listing_type->get_id() );
					if ( $current_type_id > 0 ) {
						$existing_types = get_term_meta( $new_item['term_id'], 'listing_type', true );
						$existing_types = is_array( $existing_types ) ? array_map( 'absint', $existing_types ) : [];
						if ( ! in_array( $current_type_id, $existing_types, true ) ) {
							$updated = array_values( array_unique( array_merge( $existing_types, [ $current_type_id ] ) ) );
							update_term_meta( $new_item['term_id'], 'listing_type', $updated );
						}
					}
				}

				$term_obj = get_term_by( 'id', $new_item['term_id'], $this->props['taxonomy'] );
				$new_terms[] = $term_obj->term_id;
			}

			return array_map( 'absint', $new_terms );
		} elseif  ( $template === 'multiselect' ) {
			return array_map( 'absint', $value );
		}

		if ( $template === 'checklist' ) {
			return array_map( 'absint', $value );
		}
	}

	public function validate() {
		$value = $this->get_posted_value();

		// make sure the field has a taxonomy set
		if ( empty( $this->props['taxonomy'] ) || ! ( taxonomy_exists( $this->props['taxonomy'] ) ) || ! $this->listing_type ) {
			// translators: %s is the field label.
			throw new \Exception( sprintf( _x( '%s is an invalid field.', 'Add listing form', 'my-listing' ), $this->props['label'] ) );
		}

		foreach ( (array) $value as $term_id ) {
			// make sure each posted term exists
			if ( ! term_exists( $term_id, $this->props['taxonomy'] ) ) {
				// translators: %s is the field label.
				throw new \Exception( sprintf( _x( 'Invalid value supplied for %s.', 'Add listing form', 'my-listing' ), $this->props['label'] ) );
			}

			// make sure the selected terms can be used with this listing type
			$term_meta = get_term_meta( $term_id, 'listing_type', true );
			if ( is_array( $term_meta ) && ! empty( $term_meta ) && ! in_array( $this->listing_type->get_id(), $term_meta ) ) {
				// translators: %s is the field label.
				throw new \Exception( sprintf( _x( 'Invalid term supplied for %s.', 'Add listing form', 'my-listing' ), $this->props['label'] ) );
			}
		}

		// validate package limit
		$limit = $this->get_current_selection_limit();
		$submitted_values = is_array($value) ? $value : ( empty($value) ? [] : [$value] );

		$this->validate_selection_count(
			$submitted_values,
			$limit,
			$this->props['label']
		);
	}

	public function update() {
		$value = $this->get_posted_value();
		wp_set_object_terms( $this->listing->get_id(), $value, $this->props['taxonomy'], false );

		// save drag&drop term order
		if ( ! empty( $value ) ) {
			c27()->set_terms_order( $this->listing->get_id(), $value );
		}

		do_action( 'mylisting/fields/term-select:updated', $value, $this->props['taxonomy'], $this );
	}

	public function get_value( $all = false ) {
		$terms_query_args = apply_filters( 'mylisting/fields/term-select/get_value/query_args', [
			'orderby' => 'term_order',
			'order' => 'ASC',
		], $this );

		$terms = wp_get_object_terms( $this->listing->get_id(), $this->props['taxonomy'], $terms_query_args );

		$terms_to_display = ! is_wp_error( $terms ) ? $terms : [];

		// When we switch or duplicate a listing we can show all terms
		// Field validation will prevent users from selecting more terms than the limit.
		if ( $all ) {
			return $terms_to_display;
		}
		if ( isset( $_REQUEST['action'] ) && ( $_REQUEST['action'] === 'switch' || $_REQUEST['action'] === 'duplicate' ) ) {
			return $terms_to_display;
		}
		// Apply the selection limit for display.
		// When we upgarde/downgrade subscription packages, we need to apply the selection limit for display.
		$limit = $this->get_current_selection_limit();

		if ( ! is_null( $limit ) && count( $terms_to_display ) > $limit ) {
			$terms_to_display = array_slice( $terms_to_display, 0, $limit );
		}

		return $terms_to_display;
	}

	public function string_value( $modifier = null ) {
		$terms = (array) $this->get_value();
		if ( empty( $terms ) || is_wp_error( $terms ) ) {
			return '';
		}

		$names = array_map( function( $term ) {
			return $term->name;
		}, $terms );

		return join( ', ', $names );
	}

	public function field_props() {
		$this->props['type'] = 'term-select';
		$this->props['taxonomy'] = '';
		$this->props['terms-template'] = 'multiselect';
		$this->props['create_tag'] = false;
		$this->props['selection_limit'] = '';
		$this->props['enable_package_limits'] = false;
		$this->props['package_limits'] = [];
	}

	protected function getSelectionLimitHTML() {
		ob_start();
		$this->get_selection_limit_editor_options();
		return ob_get_clean();
	}

	public function get_editor_options() {
		$this->getLabelField();
		$this->getKeyField();
		$this->getPlaceholderField();
		$this->getDescriptionField();
		$this->getTermsTemplateField();
		$this->getCreateTagsField();
		if (method_exists($this, 'getSelectionLimitHTML')) {
			echo sprintf(
				'<div v-if="field[\'terms-template\'] === \'multiselect\' || field[\'terms-template\'] === \'checklist\'">%s</div>',
				$this->getSelectionLimitHTML()
			);
		}
		$this->getRequiredField();
		$this->getShowInSubmitFormField();
		$this->getShowInAdminField();
		$this->getShowInCompareField();
	}

	public function getCreateTagsField() { ?>

		<div class="form-group" v-show="field['terms-template'] == 'multiselect'">
			<label>Allow users to add terms?</label>
			<label class="form-switch mb0">
				<input type="checkbox" v-model="field.create_tag">
				<span class="switch-slider"></span>
			</label>
		</div>
		<?php
	}

	public function getTermsTemplateField() { ?>
		<div class="form-group">
			<label>Template</label>
			<div class="select-wrapper">
				<select v-model="field['terms-template']">
					<option value="single-select">Term Select</option>
					<option value="multiselect">Term Multiselect</option>
					<option value="hierarchy">Term Hierarchy</option>
					<option value="checklist">Term Checklist</option>
				</select>
			</div>
		</div>
	<?php }

	/**
	 * Calculate selection limit for the current field,
	 * considering default and package-specific settings.
	 *
	 * @return int|null The selection limit, or null if no limit is set.
	 */
	public function get_current_selection_limit() {
		$package_id = $this->_get_package_id_from_context( $this->listing );
		return $this->_calculate_current_selection_limit( $this->props, $package_id );
	}
}
