<?php
/**
 * Changeloger Meta Registration
 *
 * @package Changeloger
 */

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

/**
 * Class Changeloger_Release_Meta
 * 
 * Handles meta registration for releases
 */
class Changeloger_Release_Meta {

	/**
	 * Meta keys
	 */
	const META_VERSION        = '_cha_version';
	const META_RELEASE_DATE   = '_cha_release_date';
	const META_CHANGE_ITEMS   = '_cha_change_items';
	const META_FEATURED_IMAGE = '_cha_featured_image';

	/**
	 * Initialize the class
	 *
	 * @return void
	 */
	public static function init() {
		add_action( 'init', [ __CLASS__, 'register_meta' ] );
		add_action( 'add_meta_boxes', [ __CLASS__, 'add_meta_boxes' ] );
		add_action( 'save_post_' . Changeloger_Post_Types::POST_TYPE, [ __CLASS__, 'save_meta_box' ], 10, 2 );
	}

	/**
	 * Register post meta
	 *
	 * @return void
	 */
	public static function register_meta() {
		$post_type = Changeloger_Post_Types::POST_TYPE;

		register_post_meta( $post_type, self::META_VERSION, [
			'type'              => 'string',
			'single'            => true,
			'show_in_rest'      => true,
			'sanitize_callback' => 'sanitize_text_field',
		] );

		register_post_meta( $post_type, self::META_RELEASE_DATE, [
			'type'              => 'string',
			'single'            => true,
			'show_in_rest'      => true,
			'sanitize_callback' => 'sanitize_text_field',
		] );

		register_post_meta( $post_type, self::META_CHANGE_ITEMS, [
			'type'         => 'array',
			'single'       => true,
			'show_in_rest' => [
				'schema' => [
					'type'  => 'array',
					'items' => [
						'type'       => 'object',
						'properties' => [
							'group' => [ 'type' => 'string' ],
							'items' => [
								'type'  => 'array',
								'items' => [ 'type' => 'string' ],
							],
						],
					],
				],
			],
		] );

		register_post_meta( $post_type, self::META_FEATURED_IMAGE, [
			'type'              => 'integer',
			'single'            => true,
			'show_in_rest'      => true,
			'sanitize_callback' => 'absint',
		] );
	}

	/**
	 * Add meta boxes
	 *
	 * @return void
	 */
	public static function add_meta_boxes() {
		add_meta_box(
			'cha_release_details',
			__( 'Release Details', 'changeloger' ),
			[ __CLASS__, 'render_meta_box' ],
			Changeloger_Post_Types::POST_TYPE,
			'normal',
			'high'
		);

		// Remove default taxonomy meta box
		remove_meta_box( 'tagsdiv-' . Changeloger_Post_Types::TAXONOMY_PRODUCT, Changeloger_Post_Types::POST_TYPE, 'side' );
	}

	/**
	 * Render meta box
	 *
	 * @param WP_Post $post Post object.
	 * @return void
	 */
	public static function render_meta_box( $post ) {
		wp_nonce_field( 'cha_release_meta', 'cha_release_meta_nonce' );

		$version        = get_post_meta( $post->ID, self::META_VERSION, true );
		$release_date   = get_post_meta( $post->ID, self::META_RELEASE_DATE, true );
		$change_items   = get_post_meta( $post->ID, self::META_CHANGE_ITEMS, true );
		$featured_image = get_post_meta( $post->ID, self::META_FEATURED_IMAGE, true );

		// Get products for dropdown.
		$taxonomy = Changeloger_Post_Types::TAXONOMY_PRODUCT;
		$products = get_terms( [
			'taxonomy'   => $taxonomy,
			'hide_empty' => false,
			'orderby'    => 'term_id',
		] );
		$current_terms = wp_get_post_terms( $post->ID, $taxonomy, [ 'fields' => 'ids' ] );
		$current_product = ! empty( $current_terms ) && ! is_wp_error( $current_terms ) ? $current_terms[0] : '';

		// Get badges for dropdown.
		$badges = Changeloger_Post_Types::get_release_badges();
		$current_badge_terms = wp_get_post_terms( $post->ID, Changeloger_Post_Types::TAXONOMY_RELEASE_BADGE, [ 'fields' => 'ids' ] );
		$current_badge = ! empty( $current_badge_terms ) && ! is_wp_error( $current_badge_terms ) ? $current_badge_terms[0] : '';

		$change_groups = Changeloger_Post_Types::get_change_groups();
		?>
		<style>
			.cha-meta-box {
				margin: -6px -12px -12px -12px;
				padding: 0;
			}
			.cha-meta-columns {
				display: grid;
				grid-template-columns: repeat(2, 1fr);
				gap: 0;
				border-bottom: 1px solid #e0e0e0;
			}
			.cha-meta-field {
				padding: 16px 20px;
				border-right: 1px solid #e0e0e0;
				background: #fff;
			}
			.cha-meta-field:nth-child(2n) {
				border-right: none;
			}
			.cha-meta-field:nth-child(n+3) {
				border-top: 1px solid #e0e0e0;
			}
			.cha-meta-field label {
				display: block;
				font-weight: 600;
				font-size: 13px;
				color: #1e1e1e;
				margin-bottom: 8px;
			}
			.cha-meta-field label .required {
				color: #d63638;
				margin-left: 2px;
			}
			.cha-meta-field input[type="text"],
			.cha-meta-field input[type="date"],
			.cha-meta-field select {
				width: 100%;
				padding: 8px 12px;
				font-size: 14px;
				border: 1px solid #8c8f94;
				border-radius: 4px;
				background: #fff;
				color: #1e1e1e;
				transition: border-color 0.15s, box-shadow 0.15s;
			}
			.cha-meta-field input:focus,
			.cha-meta-field select:focus {
				border-color: #2271b1;
				box-shadow: 0 0 0 1px #2271b1;
				outline: none;
			}
			.cha-meta-field .field-desc {
				font-size: 12px;
				color: #646970;
				margin-top: 6px;
			}
			.cha-change-section {
				padding: 16px 20px;
				background: #f6f7f7;
			}
			.cha-change-section > label {
				display: block;
				font-weight: 600;
				font-size: 13px;
				color: #1e1e1e;
				margin-bottom: 12px;
			}
			.cha-change-groups {
				display: grid;
				grid-template-columns: repeat(3, 1fr);
				gap: 12px;
			}
			.cha-change-group {
				background: #fff;
				border: 1px solid #e0e0e0;
				border-radius: 6px;
				padding: 14px;
			}
			.cha-change-group h4 {
				margin: 0 0 10px 0;
				font-size: 13px;
				font-weight: 600;
				text-transform: capitalize;
				color: #1e1e1e;
			}
			.cha-change-item-input {
				display: flex;
				gap: 6px;
				margin-bottom: 6px;
			}
			.cha-change-item-input input {
				flex: 1;
				padding: 6px 10px;
				font-size: 13px;
				border: 1px solid #c3c4c7;
				border-radius: 4px;
			}
			.cha-change-item-input input:focus {
				border-color: #2271b1;
				box-shadow: 0 0 0 1px #2271b1;
				outline: none;
			}
			.cha-add-item {
				background: #f0f0f1;
				border: 1px dashed #c3c4c7;
				cursor: pointer;
				padding: 6px 10px;
				border-radius: 4px;
				font-size: 12px;
				color: #2271b1;
				width: 100%;
				margin-top: 4px;
				transition: background 0.15s;
			}
			.cha-add-item:hover {
				background: #e0e0e1;
			}
			.cha-remove-item {
				background: #d63638;
				color: white;
				border: none;
				cursor: pointer;
				padding: 6px 8px;
				border-radius: 4px;
				font-size: 14px;
				line-height: 1;
			}
			.cha-remove-item:hover {
				background: #b32d2e;
			}
			@media (max-width: 782px) {
				.cha-meta-columns {
					grid-template-columns: 1fr;
				}
				.cha-meta-field {
					border-right: none;
				}
				.cha-meta-field:nth-child(n+2) {
					border-top: 1px solid #e0e0e0;
				}
				.cha-change-groups {
					grid-template-columns: 1fr;
				}
			}
		</style>

		<div class="cha-meta-box">
			<div class="cha-meta-columns">
				<div class="cha-meta-field">
					<label for="cha_version"><?php esc_html_e( 'Version Number', 'changeloger' ); ?> <span class="required">*</span></label>
					<input type="text" id="cha_version" name="cha_version" value="<?php echo esc_attr( $version ); ?>" placeholder="e.g., 2.0.0">
					<p class="field-desc"><?php esc_html_e( 'Semantic versioning recommended (e.g., 1.0.0)', 'changeloger' ); ?></p>
				</div>

				<div class="cha-meta-field">
					<label for="cha_release_badge"><?php esc_html_e( 'Badge', 'changeloger' ); ?></label>
					<select id="cha_release_badge" name="cha_release_badge">
						<option value=""><?php esc_html_e( 'Select a badge...', 'changeloger' ); ?></option>
						<?php foreach ( $badges as $badge ) : ?>
							<option value="<?php echo esc_attr( $badge['id'] ); ?>" <?php selected( $current_badge, $badge['id'] ); ?>><?php echo esc_html( $badge['name'] ); ?></option>
						<?php endforeach; ?>
					</select>
				</div>

				<div class="cha-meta-field">
					<label for="cha_release_date"><?php esc_html_e( 'Release Date', 'changeloger' ); ?></label>
					<input type="date" id="cha_release_date" name="cha_release_date" value="<?php echo esc_attr( $release_date ); ?>">
				</div>

				<div class="cha-meta-field">
					<label for="cha_product"><?php esc_html_e( 'Parent Product', 'changeloger' ); ?></label>
					<select id="cha_product" name="cha_product">
						<option value=""><?php esc_html_e( 'Select a product...', 'changeloger' ); ?></option>
						<?php if ( ! is_wp_error( $products ) && ! empty( $products ) ) : ?>
							<?php foreach ( $products as $product ) : ?>
								<option value="<?php echo esc_attr( $product->term_id ); ?>" <?php selected( $current_product, $product->term_id ); ?>><?php echo esc_html( $product->name ); ?></option>
							<?php endforeach; ?>
						<?php endif; ?>
					</select>
				</div>
			</div>

		<!-- Featured Image -->
		<div class="cha-change-section">
			<label><?php esc_html_e( 'Featured Image', 'changeloger' ); ?></label>
			<div class="cha-featured-image-field">
				<input type="hidden" id="cha_featured_image" name="cha_featured_image" value="<?php echo esc_attr( $featured_image ); ?>">
				<div id="cha-featured-image-preview" class="cha-featured-image-preview" style="<?php echo $featured_image ? '' : 'display:none;'; ?>">
					<?php if ( $featured_image ) : ?>
						<?php echo wp_get_attachment_image( $featured_image, 'medium' ); ?>
					<?php endif; ?>
					<button type="button" class="cha-featured-image-remove" id="cha-featured-image-remove" title="<?php esc_attr_e( 'Remove image', 'changeloger' ); ?>">×</button>
				</div>
				<button type="button" id="cha-featured-image-upload" class="cha-add-item" style="<?php echo $featured_image ? 'display:none;' : ''; ?>">
					<?php esc_html_e( '+ Upload Image', 'changeloger' ); ?>
				</button>
			</div>
		</div>

		<div class="cha-change-section">
			<label><?php esc_html_e( 'Change Items', 'changeloger' ); ?></label>
			<div class="cha-change-groups">
				<?php foreach ( $change_groups as $group_key => $group_label ) : ?>
					<?php
					$group_items = [];
					if ( is_array( $change_items ) ) {
						foreach ( $change_items as $group ) {
							if ( isset( $group['group'] ) && $group['group'] === $group_key && isset( $group['items'] ) ) {
								$group_items = $group['items'];
								break;
							}
						}
					}
					?>
					<div class="cha-change-group">
						<h4><?php echo esc_html( $group_label ); ?></h4>
						<div class="cha-items-container" data-group="<?php echo esc_attr( $group_key ); ?>">
							<?php if ( ! empty( $group_items ) ) : ?>
								<?php foreach ( $group_items as $item ) : ?>
									<div class="cha-change-item-input">
										<input type="text" name="cha_change_items[<?php echo esc_attr( $group_key ); ?>][]" value="<?php echo esc_attr( $item ); ?>">
										<button type="button" class="cha-remove-item" onclick="this.parentElement.remove()">×</button>
									</div>
								<?php endforeach; ?>
							<?php endif; ?>
						</div>
						<button type="button" class="cha-add-item" onclick="chaAddChangeItem('<?php echo esc_js( $group_key ); ?>')">+ <?php esc_html_e( 'Add Item', 'changeloger' ); ?></button>
					</div>
				<?php endforeach; ?>
			</div>
		</div>
		</div><!-- /.cha-meta-box -->

		<script>
		function chaAddChangeItem(group) {
			const container = document.querySelector(`.cha-items-container[data-group="${group}"]`);
			const div = document.createElement('div');
			div.className = 'cha-change-item-input';
			div.innerHTML = `
				<input type="text" name="cha_change_items[${group}][]" value="">
				<button type="button" class="cha-remove-item" onclick="this.parentElement.remove()">×</button>
			`;
			container.appendChild(div);
		}

		// Featured Image uploader for metabox.
		(function() {
			var uploadBtn = document.getElementById('cha-featured-image-upload');
			var removeBtn = document.getElementById('cha-featured-image-remove');
			var hiddenInput = document.getElementById('cha_featured_image');
			var preview = document.getElementById('cha-featured-image-preview');
			var frame;

			if (uploadBtn) {
				uploadBtn.addEventListener('click', function(e) {
					e.preventDefault();
					if (frame) {
						frame.open();
						return;
					}
					frame = wp.media({
						title: 'Select Featured Image',
						button: { text: 'Use this image' },
						multiple: false,
						library: { type: 'image' }
					});
					frame.on('select', function() {
						var attachment = frame.state().get('selection').first().toJSON();
						var imgUrl = attachment.sizes && attachment.sizes.medium ? attachment.sizes.medium.url : attachment.url;
						hiddenInput.value = attachment.id;
						preview.innerHTML = '<img src="' + imgUrl + '" alt=""><button type="button" class="cha-featured-image-remove" id="cha-featured-image-remove" title="Remove image">×</button>';
						preview.style.display = '';
						uploadBtn.style.display = 'none';
						bindRemove();
					});
					frame.open();
				});
			}

			function bindRemove() {
				var btn = document.getElementById('cha-featured-image-remove');
				if (btn) {
					btn.addEventListener('click', function(e) {
						e.preventDefault();
						hiddenInput.value = '';
						preview.innerHTML = '';
						preview.style.display = 'none';
						uploadBtn.style.display = '';
					});
				}
			}
			bindRemove();
		})();
		</script>
		<?php
	}

	/**
	 * Save meta box data
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 * @return void
	 */
	public static function save_meta_box( $post_id, $post ) {
		// Verify nonce
		if ( ! isset( $_POST['cha_release_meta_nonce'] ) || ! wp_verify_nonce( $_POST['cha_release_meta_nonce'], 'cha_release_meta' ) ) {
			return;
		}

		// Check autosave
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		// Check permissions
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		// Save version
		if ( isset( $_POST['cha_version'] ) ) {
			update_post_meta( $post_id, self::META_VERSION, sanitize_text_field( $_POST['cha_version'] ) );
		}

		// Save release date.
		if ( isset( $_POST['cha_release_date'] ) ) {
			update_post_meta( $post_id, self::META_RELEASE_DATE, sanitize_text_field( $_POST['cha_release_date'] ) );
		}

		// Save release badge (taxonomy).
		if ( isset( $_POST['cha_release_badge'] ) ) {
			$badge_id = absint( $_POST['cha_release_badge'] );
			if ( $badge_id ) {
				wp_set_object_terms( $post_id, $badge_id, Changeloger_Post_Types::TAXONOMY_RELEASE_BADGE );
			} else {
				wp_set_object_terms( $post_id, [], Changeloger_Post_Types::TAXONOMY_RELEASE_BADGE );
			}
		}

		// Save change items.
		if ( isset( $_POST['cha_change_items'] ) && is_array( $_POST['cha_change_items'] ) ) {
			$change_items = [];
			foreach ( $_POST['cha_change_items'] as $group => $items ) {
				$filtered_items = array_filter( array_map( 'sanitize_text_field', $items ) );
				if ( ! empty( $filtered_items ) ) {
					$change_items[] = [
						'group' => sanitize_text_field( $group ),
						'items' => array_values( $filtered_items ),
					];
				}
			}
			update_post_meta( $post_id, self::META_CHANGE_ITEMS, $change_items );
		} else {
			update_post_meta( $post_id, self::META_CHANGE_ITEMS, [] );
		}

		// Save featured image.
		if ( isset( $_POST['cha_featured_image'] ) ) {
			$image_id = absint( $_POST['cha_featured_image'] );
			if ( $image_id ) {
				update_post_meta( $post_id, self::META_FEATURED_IMAGE, $image_id );
			} else {
				delete_post_meta( $post_id, self::META_FEATURED_IMAGE );
			}
		}

		// Save product (taxonomy) - uses main nonce since it's in the same meta box
		if ( isset( $_POST['cha_product'] ) ) {
			$product_id = absint( $_POST['cha_product'] );
			if ( $product_id ) {
				wp_set_object_terms( $post_id, $product_id, Changeloger_Post_Types::TAXONOMY_PRODUCT );
			} else {
				wp_set_object_terms( $post_id, [], Changeloger_Post_Types::TAXONOMY_PRODUCT );
			}
		}
	}
}

/**
 * Class Changeloger_Feedback_Meta
 *
 * Handles meta fields for feedback requests
 */
class Changeloger_Feedback_Meta {

	/**
	 * Meta keys
	 */
	const META_STATUS     = '_cha_feedback_status';
	const META_UPVOTES    = '_cha_feedback_upvotes';
	const META_UPVOTERS   = '_cha_feedback_upvoters';
	const META_DOWNVOTES  = '_cha_feedback_downvotes';
	const META_DOWNVOTERS = '_cha_feedback_downvoters';
	const META_VISIBILITY = '_cha_feedback_visibility';

	/**
	 * Initialize
	 *
	 * @return void
	 */
	public static function init() {
		add_action( 'add_meta_boxes', [ __CLASS__, 'add_meta_boxes' ] );
		add_action( 'save_post_' . Changeloger_Post_Types::POST_TYPE_FEEDBACK, [ __CLASS__, 'save_meta_box' ] );
	}

	/**
	 * Add meta boxes
	 *
	 * @return void
	 */
	public static function add_meta_boxes() {
		add_meta_box(
			'cha_feedback_details',
			__( 'Feedback Details', 'changeloger' ),
			[ __CLASS__, 'render_meta_box' ],
			Changeloger_Post_Types::POST_TYPE_FEEDBACK,
			'side',
			'high'
		);
	}

	/**
	 * Render meta box
	 *
	 * @param WP_Post $post Post object.
	 * @return void
	 */
	public static function render_meta_box( $post ) {
		wp_nonce_field( 'cha_feedback_meta_nonce', 'cha_feedback_meta_nonce' );

		$status     = get_post_meta( $post->ID, self::META_STATUS, true ) ?: 'in_review';
		$upvotes    = (int) get_post_meta( $post->ID, self::META_UPVOTES, true );
		$visibility = get_post_meta( $post->ID, self::META_VISIBILITY, true ) ?: 'public';
		$statuses   = Changeloger_Post_Types::get_feedback_statuses();
		?>
		<style>
			.cha-feedback-meta { margin-bottom: 16px; }
			.cha-feedback-meta label { display: block; margin-bottom: 6px; font-weight: 600; }
			.cha-feedback-meta .cha-select,
			.cha-feedback-meta .cha-input { width: 100%; }
			.cha-feedback-stat { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid #ddd; }
			.cha-feedback-stat svg { width: 16px; height: 16px; opacity: 0.6; }
		</style>

		<div class="cha-feedback-meta">
			<label for="cha_feedback_status"><?php esc_html_e( 'Status', 'changeloger' ); ?></label>
			<select name="cha_feedback_status" id="cha_feedback_status" class="cha-select">
				<?php foreach ( $statuses as $key => $label ) : ?>
					<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $status, $key ); ?>>
						<?php echo esc_html( $label ); ?>
					</option>
				<?php endforeach; ?>
			</select>
		</div>

		<div class="cha-feedback-meta">
			<label for="cha_feedback_visibility"><?php esc_html_e( 'Visibility', 'changeloger' ); ?></label>
			<select name="cha_feedback_visibility" id="cha_feedback_visibility" class="cha-select">
				<option value="public" <?php selected( $visibility, 'public' ); ?>>
					<?php esc_html_e( 'Public', 'changeloger' ); ?>
				</option>
				<option value="private" <?php selected( $visibility, 'private' ); ?>>
					<?php esc_html_e( 'Private', 'changeloger' ); ?>
				</option>
			</select>
		</div>

		<div class="cha-feedback-stat">
			<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
				<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"/>
			</svg>
			<span><?php printf( esc_html__( '%d upvotes', 'changeloger' ), $upvotes ); ?></span>
		</div>
		<?php
	}

	/**
	 * Save meta box
	 *
	 * @param int $post_id Post ID.
	 * @return void
	 */
	public static function save_meta_box( $post_id ) {
		if ( ! isset( $_POST['cha_feedback_meta_nonce'] ) ) {
			return;
		}

		if ( ! wp_verify_nonce( $_POST['cha_feedback_meta_nonce'], 'cha_feedback_meta_nonce' ) ) {
			return;
		}

		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		// Get old status for hook.
		$old_status = get_post_meta( $post_id, self::META_STATUS, true );

		// Save status.
		if ( isset( $_POST['cha_feedback_status'] ) ) {
			$new_status = sanitize_text_field( $_POST['cha_feedback_status'] );
			update_post_meta( $post_id, self::META_STATUS, $new_status );

			// Fire status change hook.
			if ( $old_status !== $new_status ) {
				do_action( 'cha_feedback_status_changed', $post_id, $new_status, $old_status );
			}
		}

		// Save visibility.
		if ( isset( $_POST['cha_feedback_visibility'] ) ) {
			$visibility = sanitize_text_field( $_POST['cha_feedback_visibility'] );
			update_post_meta( $post_id, self::META_VISIBILITY, $visibility );
		}
	}

	/**
	 * Get feedback status
	 *
	 * @param int $post_id Post ID.
	 * @return string
	 */
	public static function get_status( $post_id ) {
		return get_post_meta( $post_id, self::META_STATUS, true ) ?: 'in_review';
	}

	/**
	 * Get upvote count
	 *
	 * @param int $post_id Post ID.
	 * @return int
	 */
	public static function get_upvotes( $post_id ) {
		return (int) get_post_meta( $post_id, self::META_UPVOTES, true );
	}

	/**
	 * Get list of user IDs who upvoted
	 *
	 * @param int $post_id Post ID.
	 * @return array
	 */
	public static function get_upvoters( $post_id ) {
		$upvoters = get_post_meta( $post_id, self::META_UPVOTERS, true );
		return is_array( $upvoters ) ? $upvoters : [];
	}

	/**
	 * Check if user has upvoted
	 *
	 * @param int $post_id Post ID.
	 * @param int $user_id User ID.
	 * @return bool
	 */
	public static function has_user_upvoted( $post_id, $user_id ) {
		$upvoters = self::get_upvoters( $post_id );
		return in_array( $user_id, $upvoters, true );
	}

	/**
	 * Toggle upvote
	 *
	 * @param int $post_id Post ID.
	 * @param int $user_id User ID.
	 * @return array Result with action and count.
	 */
	public static function toggle_upvote( $post_id, $user_id ) {
		$upvoters   = self::get_upvoters( $post_id );
		$upvotes    = self::get_upvotes( $post_id );
		$downvoters = self::get_downvoters( $post_id );
		$downvotes  = self::get_downvotes( $post_id );

		// Remove downvote if exists when upvoting.
		if ( in_array( $user_id, $downvoters, true ) ) {
			$downvoters = array_diff( $downvoters, [ $user_id ] );
			$downvotes  = max( 0, $downvotes - 1 );
			update_post_meta( $post_id, self::META_DOWNVOTERS, array_values( $downvoters ) );
			update_post_meta( $post_id, self::META_DOWNVOTES, $downvotes );
		}

		if ( in_array( $user_id, $upvoters, true ) ) {
			// Remove vote.
			$upvoters = array_diff( $upvoters, [ $user_id ] );
			$upvotes  = max( 0, $upvotes - 1 );
			$action   = 'removed';
		} else {
			// Add vote.
			$upvoters[] = $user_id;
			$upvotes++;
			$action = 'added';
		}

		update_post_meta( $post_id, self::META_UPVOTERS, array_values( $upvoters ) );
		update_post_meta( $post_id, self::META_UPVOTES, $upvotes );

		// Fire upvote hook.
		do_action( 'cha_feedback_upvoted', $post_id, $user_id, $upvotes );

		return [
			'action'    => $action,
			'count'     => $upvotes,
			'upvotes'   => $upvotes,
			'downvotes' => $downvotes,
			'net_votes' => $upvotes - $downvotes,
		];
	}

	/**
	 * Get visibility
	 *
	 * @param int $post_id Post ID.
	 * @return string
	 */
	public static function get_visibility( $post_id ) {
		return get_post_meta( $post_id, self::META_VISIBILITY, true ) ?: 'public';
	}

	/**
	 * Get downvote count
	 *
	 * @param int $post_id Post ID.
	 * @return int
	 */
	public static function get_downvotes( $post_id ) {
		return (int) get_post_meta( $post_id, self::META_DOWNVOTES, true );
	}

	/**
	 * Get list of user IDs who downvoted
	 *
	 * @param int $post_id Post ID.
	 * @return array
	 */
	public static function get_downvoters( $post_id ) {
		$downvoters = get_post_meta( $post_id, self::META_DOWNVOTERS, true );
		return is_array( $downvoters ) ? $downvoters : [];
	}

	/**
	 * Check if user has downvoted
	 *
	 * @param int $post_id Post ID.
	 * @param int $user_id User ID.
	 * @return bool
	 */
	public static function has_user_downvoted( $post_id, $user_id ) {
		$downvoters = self::get_downvoters( $post_id );
		return in_array( $user_id, $downvoters, true );
	}

	/**
	 * Toggle downvote
	 * When downvoting, removes any existing upvote from the same user.
	 *
	 * @param int $post_id Post ID.
	 * @param int $user_id User ID.
	 * @return array Result with action and counts.
	 */
	public static function toggle_downvote( $post_id, $user_id ) {
		$downvoters = self::get_downvoters( $post_id );
		$downvotes  = self::get_downvotes( $post_id );
		$upvoters   = self::get_upvoters( $post_id );
		$upvotes    = self::get_upvotes( $post_id );

		// If user had upvoted, just remove their upvote (don't add downvote).
		if ( in_array( $user_id, $upvoters, true ) ) {
			$upvoters = array_diff( $upvoters, [ $user_id ] );
			$upvotes  = max( 0, $upvotes - 1 );
			update_post_meta( $post_id, self::META_UPVOTERS, array_values( $upvoters ) );
			update_post_meta( $post_id, self::META_UPVOTES, $upvotes );

			// Fire downvote hook (which indicates vote change).
			do_action( 'cha_feedback_downvoted', $post_id, $user_id, $downvotes );

			return [
				'action'     => 'removed_upvote',
				'downvotes'  => $downvotes,
				'upvotes'    => $upvotes,
				'net_votes'  => $upvotes - $downvotes,
			];
		}

		if ( in_array( $user_id, $downvoters, true ) ) {
			// Remove downvote.
			$downvoters = array_diff( $downvoters, [ $user_id ] );
			$downvotes  = max( 0, $downvotes - 1 );
			$action     = 'removed';
		} else {
			// Add downvote.
			$downvoters[] = $user_id;
			$downvotes++;
			$action = 'added';
		}

		update_post_meta( $post_id, self::META_DOWNVOTERS, array_values( $downvoters ) );
		update_post_meta( $post_id, self::META_DOWNVOTES, $downvotes );

		// Fire downvote hook.
		do_action( 'cha_feedback_downvoted', $post_id, $user_id, $downvotes );

		return [
			'action'     => $action,
			'downvotes'  => $downvotes,
			'upvotes'    => $upvotes,
			'net_votes'  => $upvotes - $downvotes,
		];
	}

	/**
	 * Get net votes (upvotes - downvotes)
	 *
	 * @param int $post_id Post ID.
	 * @return int
	 */
	public static function get_net_votes( $post_id ) {
		return self::get_upvotes( $post_id ) - self::get_downvotes( $post_id );
	}
}

// Initialize Feedback Meta.
Changeloger_Feedback_Meta::init();
