<?php
/**
 * Toasts trait
 *
 * @package     AffiliateWP
 * @subpackage  Utils\Traits
 * @copyright   Copyright (c) 2025, Sandhills Development, LLC
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       2.33.0
 */

namespace AffiliateWP\Utils\Traits;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Toasts trait
 *
 * Provides toast notification rendering.
 * Renders a fixed-position notification that auto-dismisses after 3.5 seconds.
 * Triggered via JavaScript custom events.
 *
 * Usage (JavaScript):
 *   window.dispatchEvent(new CustomEvent('affwp-toast', {
 *       detail: { message: 'Schedule created.', type: 'success' }
 *   }));
 *
 * @since 2.33.0
 */
trait Toasts {

	/**
	 * Whether the toast has been rendered on this page.
	 *
	 * @since 2.33.0
	 * @var bool
	 */
	protected static $toast_rendered = false;

	/**
	 * Render the toast notification component.
	 *
	 * Should be called once per page, typically in the admin footer.
	 * Subsequent calls are no-ops.
	 *
	 * Two ways to fire a toast:
	 *
	 *   1. Immediate (same page): dispatch the `affwp-toast` event.
	 *      window.dispatchEvent( new CustomEvent( 'affwp-toast', {
	 *          detail: { message: 'Saved.', type: 'success' }
	 *      } ) );
	 *
	 *   2. Persisted (survives a page redirect): use the helper.
	 *      window.affwpToast( { message: 'Saved.', type: 'success', persist: true } );
	 *      Stored in sessionStorage and replayed on the next page load by
	 *      this trait's x-init. Useful for actions that redirect (delete,
	 *      retry from a drawer, bulk actions) where the success state
	 *      needs to be communicated on the *destination* page.
	 *
	 * @since 2.33.0
	 *
	 * @return string The rendered toast HTML, or empty string if already rendered.
	 */
	public function render_toast() {

		if ( self::$toast_rendered ) {
			return '';
		}

		self::$toast_rendered = true;

		ob_start();
		?>
		<!-- AffiliateWP Toast Notifications -->
		<script>
			/*
			 * Global helper: window.affwpToast( { message, type, persist } ).
			 * `persist: true` saves the toast to sessionStorage so it
			 * survives a same-origin navigation and replays on the next
			 * page when this trait's x-init drains the queue. Without
			 * persist, behaves like dispatching the affwp-toast event.
			 * Guarded with `__affwpToastInit` so duplicate trait renders
			 * (or hot-reloads) don't redefine the helper.
			 */
			( function () {
				if ( window.__affwpToastInit ) {
					return;
				}
				window.__affwpToastInit = true;
				var STORAGE_KEY = 'affwp-queued-toasts';
				window.affwpToast = function ( opts ) {
					opts = opts || {};
					if ( opts.persist ) {
						try {
							var queue = JSON.parse( sessionStorage.getItem( STORAGE_KEY ) || '[]' );
							queue.push( { message: opts.message, type: opts.type || 'success' } );
							sessionStorage.setItem( STORAGE_KEY, JSON.stringify( queue ) );
						} catch ( e ) {
							/*
							 * sessionStorage full or disabled — fall through to
							 * immediate dispatch so the toast isn't silently lost.
							 */
						}
						return;
					}
					window.dispatchEvent( new CustomEvent( 'affwp-toast', {
						detail: { message: opts.message, type: opts.type || 'success' }
					} ) );
				};
			} )();
		</script>
		<div class="affwp-ui">
		<div
			class="pointer-events-none fixed inset-0 flex items-end justify-center px-4 pb-8"
			style="z-index: 9999999;"
			x-data="{ show: false, message: '', type: 'success', timeout: null, sidebarWidth: 0, queue: [] }"
			x-init="
				sidebarWidth = parseInt(getComputedStyle(document.getElementById('wpcontent')).marginLeft) || 0;
				$el.style.left = sidebarWidth + 'px';
				window.addEventListener('affwp-toast', e => {
					message = e.detail.message;
					type = e.detail.type || 'success';
					show = true;
					clearTimeout(timeout);
					timeout = setTimeout(() => show = false, 3500);
				});
				/* Drain any toasts persisted from the previous page (sessionStorage
				   queue populated by window.affwpToast({ persist: true })). Re-fire
				   them as normal affwp-toast events so they take the same code path
				   as in-page toasts. setTimeout staggers them so multiple don't
				   overwrite each other instantly. */
				try {
					queue = JSON.parse(sessionStorage.getItem('affwp-queued-toasts') || '[]');
					sessionStorage.removeItem('affwp-queued-toasts');
					queue.forEach((t, idx) => {
						setTimeout(() => window.dispatchEvent(new CustomEvent('affwp-toast', { detail: t })), idx * 4000);
					});
				} catch (e) { /* sessionStorage disabled — nothing to drain */ }
			"
			aria-live="assertive"
		>
			<div class="flex w-full flex-col items-center space-y-3">
				<div
					x-show="show"
					x-transition:enter="transform ease-out duration-300 transition"
					x-transition:enter-start="translate-y-2 opacity-0"
					x-transition:enter-end="translate-y-0 opacity-100"
					x-transition:leave="transition ease-in duration-100"
					x-transition:leave-start="opacity-100"
					x-transition:leave-end="opacity-0"
					class="pointer-events-auto w-full max-w-md overflow-hidden rounded-lg bg-gray-900 shadow-2xl ring-1 ring-white/10"
					style="display: none;"
				>
					<div class="p-4">
						<div class="flex items-center">
							<div class="shrink-0">
								<!-- Success icon -->
								<template x-if="type === 'success'">
									<svg class="size-5 text-green-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
										<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
									</svg>
								</template>
								<!-- Error icon -->
								<template x-if="type === 'error'">
									<svg class="size-5 text-red-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
										<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
									</svg>
								</template>
								<!-- Warning icon -->
								<template x-if="type === 'warning'">
									<svg class="size-5 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
										<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
									</svg>
								</template>
								<!-- Info icon -->
								<template x-if="type === 'info'">
									<svg class="size-5 text-blue-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
										<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
									</svg>
								</template>
							</div>
							<div class="ml-3 w-0 flex-1">
								<p class="text-sm font-medium text-white m-0" x-text="message"></p>
							</div>
							<div class="ml-4 flex shrink-0">
								<button
									type="button"
									x-on:click="show = false; clearTimeout(timeout)"
									class="inline-flex items-center justify-center rounded-md bg-gray-900 text-gray-400 hover:text-gray-300 focus:ring-2 focus:ring-affwp-brand-500 focus:ring-offset-2 focus:ring-offset-gray-900 focus:outline-none cursor-pointer"
								>
									<span class="sr-only"><?php esc_html_e( 'Close', 'affiliate-wp' ); ?></span>
									<svg class="size-5" viewBox="0 0 20 20" fill="currentColor">
										<path d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z" />
									</svg>
								</button>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		</div>
		<?php

		return ob_get_clean();
	}
}
