import $ from 'jquery';
import { dialog, ajax, hooks } from 'peepso';
import { register_avatar as avatarData } from 'peepsodata';

/**
 * Avatar picker/cropper for the registration form (#5471).
 *
 * Mirrors the logged-in profile avatar dialog (assets/js/page-profile/header/avatar-dialog.js)
 * but the visitor has no user ID yet, so:
 *   - all endpoints are the guest `registeravatarajax.*` handlers, keyed by a random token
 *     rather than a user_id;
 *   - there is no Gravatar option;
 *   - "Done" does NOT hit a confirm endpoint. The uploaded/cropped temp files ARE the
 *     persisted state; Done simply records the token in the form's hidden field and updates
 *     the on-page preview. The real attach happens server-side after the account is created.
 */

if (avatarData) {
	const TOKEN_FIELD = 'registration_avatar_token';
	const TOKEN = avatarData.token;
	const NONCE = avatarData.nonce;
	const PEEPSO_NONCE = peepsodata.peepso_nonce;
	const UPLOAD_MAXSIZE = +peepsodata.upload_size;
	const IMG_AVATAR_DEFAULT = avatarData.img_avatar_default;
	const UPLOAD_URL = `${peepsodata.ajaxurl_legacy}registeravatarajax.upload?avatar`;
	const TEXT_ERROR_FILETYPE = avatarData.text_error_filetype;
	const TEXT_ERROR_FILESIZE = avatarData.text_error_filesize;

	// The registration form is server-rendered; find the field the render loop emitted.
	const $field = $('.ps-js-registration-avatar');
	if ($field.length) {
		wireField($field.first());
	}

	/**
	 * Wire a single registration avatar field: preview, dialog trigger, submit guard.
	 *
	 * @param {jQuery} $field
	 */
	function wireField($field) {
		const $onPagePreview = $field.find('.ps-js-avatar-image');
		const $button = $field.find('.ps-js-avatar-button');
		const $token = $field.find('.ps-js-avatar-token');
		const required = '1' === String($field.data('required'));

		const $form = $field.closest('form.ps-js-form-register');
		const $submit = $form.find('[type=submit]');
		// register.js gates this same button on the terms/privacy checkboxes.
		const $consents = $form.find('input[name=terms], input[name=privacy]');

		// Required mode: keep the Register button disabled until an avatar exists.
		// The button has two independent gates — this avatar one and register.js's
		// consent one — so recompute from BOTH and re-assert. peepso-register-avatar
		// depends on peepso-register, so our consent listener is bound after (and
		// fires after) register.js's, letting us have the final say.
		function syncSubmit() {
			if (!required || !$submit.length) {
				return;
			}
			if (!$token.val()) {
				$submit.attr('disabled', 'disabled');
				return;
			}
			// Avatar satisfied — defer to the consent gate if it's present.
			let consentsOk = true;
			$consents.each(function () {
				if (!this.checked) {
					consentsOk = false;
				}
			});
			$submit.prop('disabled', !consentsOk);
		}

		let popup;
		let $container;
		let $upload, $uploadFile, $remove, $done;
		let $cropStart, $cropCancel, $cropConfirm, $rotateLeft, $rotateRight;
		let $error, $loading, $hasAvatar, $noAvatar, $imgOriginal, $imgPreview, $imgLoading;
		let cropCoords;
		let hasAvatar = false;

		/**
		 * Lazily build the dialog on first open.
		 */
		function open() {
			if (!popup) {
				popup = dialog(avatarData.template, { wide: true, destroyOnClose: false }).show();
				init();
			}
			return popup;
		}

		function init() {
			$container = popup.$el;
			$upload = $container.find('.ps-js-upload').on('click', upload);
			$uploadFile = uploadInit();
			$remove = $container.find('.ps-js-remove').on('click', remove);
			$done = $container.find('.ps-js-submit').on('click', finalize);
			$cropStart = $container.find('.ps-js-btn-crop').on('click', cropStart);
			$cropCancel = $container.find('.ps-js-btn-crop-cancel').on('click', cropCancel);
			$cropConfirm = $container.find('.ps-js-btn-crop-save').on('click', cropConfirm);
			$rotateLeft = $container.find('.ps-js-btn-rotate-l').on('click', { dir: 'ccw' }, rotate);
			$rotateRight = $container.find('.ps-js-btn-rotate-r').on('click', { dir: 'cw' }, rotate);
			$error = $container.find('.ps-js-error').hide();
			$loading = $container.find('.ps-js-loading').hide();
			$hasAvatar = $container.find('.ps-js-has-avatar');
			$noAvatar = $container.find('.ps-js-no-avatar');
			$imgOriginal = $container.find('.ps-js-original');
			$imgPreview = $container.find('.ps-js-preview');
			$imgLoading = $container.find('.ps-js-avatar-loading');

			update();

			// Disable the "Done" button until an avatar exists.
			$done.attr('disabled', 'disabled');
		}

		/**
		 * Update the dialog's images and edit controls.
		 *
		 * @param {?string} avatar
		 * @param {?string} original
		 */
		function update(avatar, original) {
			if (!avatar) {
				$remove.hide();
				$hasAvatar.hide();
				$noAvatar.show();

				hasAvatar = false;
				$imgPreview.attr('src', IMG_AVATAR_DEFAULT);
				$imgOriginal.removeAttr('src');
				window.ps_crop.destroy($imgOriginal);
				return;
			}

			if ('loading' === avatar) {
				$remove.hide();
				$noAvatar.hide();
				$hasAvatar.show();
				$imgOriginal.hide();
				$imgLoading.show();
			} else {
				$remove.show();
				$noAvatar.hide();
				$hasAvatar.show();

				let cacheBust = '?nocache-' + new Date().getTime();
				hasAvatar = true;

				$imgPreview.attr('src', `${avatar}${cacheBust}`);
				$imgLoading.hide();
				$imgOriginal.show();
				if (original) {
					$imgOriginal.attr('src', `${original}${cacheBust}`);
					window.ps_crop.destroy($imgOriginal);
				}
			}

			// Reset crop/rotate buttons.
			$cropStart.show();
			$cropCancel.hide();
			$cropConfirm.hide();
			$rotateLeft.show();
			$rotateRight.show();
		}

		function upload(e) {
			e.preventDefault();
			$uploadFile[0].value = null;
			$uploadFile.trigger('click');
		}

		function uploadInit() {
			let css = { position: 'absolute', opacity: 0, height: 1, width: 1 },
				html = '<input type="file" name="filedata" accept="image/*" data-name="register-avatar-upload" />',
				$file = $(html).css(css);

			$file.appendTo(document.body);

			if ($file.psFileupload) {
				$file.psFileupload({
					formData: { [TOKEN_FIELD]: TOKEN, _wpnonce: NONCE },
					dataType: 'json',
					url: UPLOAD_URL,
					beforeSend: function (xhr) {
						xhr.setRequestHeader('X-PeepSo-Nonce', PEEPSO_NONCE);
					},
					replaceFileInput: false,
					dropZone: null,
					add(e, data) {
						let file = data.files[0];
						if (!file.type.match(/image\/(jpe?g|png|webp)$/i)) {
							$error.html(TEXT_ERROR_FILETYPE).show();
						} else if (file.size > UPLOAD_MAXSIZE) {
							$error.html(TEXT_ERROR_FILESIZE).show();
						} else {
							$error.hide();
							update('loading');
							data.submit();
						}
					},
					done(e, data) {
						let json = data.result;
						if (json.success) {
							let avatar = json.data && json.data.image_url,
								original = json.data && json.data.orig_image_url;
							update(avatar, original);
							$done.removeAttr('disabled');
						} else if (json.errors) {
							update();
							$error.html(json.errors[0]).show();
						}
					}
				});
			}

			return $file;
		}

		function remove(e) {
			e.preventDefault();

			let params = { [TOKEN_FIELD]: TOKEN, _wpnonce: NONCE };
			ajax.post('registeravatarajax.remove', params).then(json => {
				if (json.success) {
					update(null);
					window.ps_crop.destroy($imgOriginal);

					// Clear the form state and on-page preview. The dialog stays open,
					// back in its empty upload state, so the visitor can pick another
					// image right away (#5471 review).
					$token.val('');
					$onPagePreview.attr('src', IMG_AVATAR_DEFAULT);

					$done.attr('disabled', 'disabled');
					syncSubmit();
				}
			});
		}

		/**
		 * "Done": no server round-trip. The temp files already hold the avatar; record the
		 * token in the hidden form field and reflect the choice in the on-page preview.
		 *
		 * @param {Event} e
		 */
		function finalize(e) {
			e.preventDefault();

			if (hasAvatar) {
				$token.val(TOKEN);
				$onPagePreview.attr('src', $imgPreview.attr('src'));

				// Let the mode-2 submit guard know the requirement is satisfied. The
				// error div is a sibling of the field markup, inside .ps-form__field.
				$field
					.closest('.ps-form__field')
					.removeClass('ps-form__field--invalid')
					.find('.ps-form__error')
					.hide();

				syncSubmit();
			}

			window.ps_crop.destroy($imgOriginal);
			popup.hide();
		}

		function cropStart(e) {
			e.preventDefault();

			window.ps_crop.init({
				elem: $imgOriginal,
				change: coords => {
					cropCoords = cropGetCoords($imgOriginal[0], coords);
				}
			});

			$cropStart.hide();
			$cropCancel.show();
			$cropConfirm.show();
			$rotateLeft.hide();
			$rotateRight.hide();
		}

		function cropCancel(e) {
			e.preventDefault();

			window.ps_crop.detach($imgOriginal);
			$cropStart.show();
			$cropCancel.hide();
			$cropConfirm.hide();
			$rotateLeft.show();
			$rotateRight.show();
		}

		function cropConfirm(e) {
			e.preventDefault();

			window.ps_crop.detach($imgOriginal);
			$cropStart.show();
			$cropCancel.hide();
			$cropConfirm.hide();
			$rotateLeft.show();
			$rotateRight.show();

			let cropData = {
				x: cropCoords.x1,
				y: cropCoords.y1,
				x2: cropCoords.x2,
				y2: cropCoords.y2,
				width: cropCoords.width,
				height: cropCoords.height,
				tmp: 1
			};

			let params = $.extend(cropData, { [TOKEN_FIELD]: TOKEN, _wpnonce: NONCE });
			ajax.post('registeravatarajax.crop', params).then(json => {
				if (json.success) {
					update(json.data.image_url);
					$done.removeAttr('disabled');
				}
			});
		}

		function cropGetCoords(img, coords) {
			let $img = $(img),
				ratio = 1,
				maxWH = 800,
				resize = false,
				width,
				height,
				params;

			if (img.naturalWidth) {
				width = img.naturalWidth || $img.width();
				height = img.naturalHeight || $img.height();

				if (width > maxWH || height > maxWH) {
					ratio = maxWH / Math.max(width, height);
					width = width * ratio;
					height = height * ratio;
					resize = true;
				}

				ratio = width / $img.width();
			}

			params = {
				x1: Math.floor(ratio * coords.x),
				y1: Math.floor(ratio * coords.y),
				x2: Math.floor(ratio * (coords.x + coords.width)),
				y2: Math.floor(ratio * (coords.y + coords.height))
			};

			if (resize) {
				params.width = width;
				params.height = height;
			}

			return params;
		}

		function rotate(e) {
			e.preventDefault();

			let params = $.extend(
				{ direction: 'ccw' === e.data.dir ? 'ccw' : 'cw' },
				{ [TOKEN_FIELD]: TOKEN, _wpnonce: NONCE, tmp: 1 }
			);

			update('loading');
			ajax.post('registeravatarajax.rotate', params).then(json => {
				if (json.success) {
					let avatar = json.data && json.data.image_url,
						original = json.data && json.data.orig_image_url;
					update(avatar, original);
					$done.removeAttr('disabled');
				}
			});
		}

		// Open the dialog from the on-page camera button.
		$button.on('click', e => {
			e.preventDefault();
			e.stopPropagation();
			open().show();
		});

		// Mode 2 (required): keep the Register button disabled until an avatar has
		// been chosen, and re-assert it whenever register.js's consent handler might
		// have re-enabled it (our listener is bound after its, so we run last). The
		// server also enforces the requirement, so a JS-disabled visitor cannot skip it.
		if (required) {
			$consents.on('change.ps-register-avatar click.ps-register-avatar', syncSubmit);
			syncSubmit();

			// Defence in depth: even with the button disabled, block the form's
			// `submit` in the CAPTURE phase (it runs before register.js's bubble-phase
			// handler, so register.js's ajax flow never starts and the form is never
			// left locked). Covers any path that submits without the button — an
			// Enter-key implicit submit, a programmatic submit, etc.
			const form = $form[0];
			if (form) {
				form.addEventListener(
					'submit',
					function (e) {
						if ($token.val()) {
							return;
						}
						e.preventDefault();
						e.stopImmediatePropagation();
						syncSubmit();

						// The error div render_fields() emits is a SIBLING of the field
						// markup, inside the .ps-form__field wrapper.
						$field
							.closest('.ps-form__field')
							.addClass('ps-form__field--invalid')
							.find('.ps-form__error')
							.html(
								'<div class="ps-form__error-item">' +
									(avatarData.text_required || 'Please upload a profile photo.') +
									'</div>'
							)
							.show();
						$field[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
					},
					true
				);
			}
		}
	}
}
