<?php
/**
 * Admin display-time formatter.
 *
 * Shared helper for the §6 / §10 / §12 admin page renderers. Use
 * cases / mappers feed timestamps through the admin layer as
 * pre-formatted strings (ISO-8601 UTC `Y-m-d\TH:i:s\Z`,
 * `Y-m-d\TH:i:sP`, or MySQL `Y-m-d H:i:s` UTC), and these strings
 * are persisted in test fixtures and REST envelopes — we do NOT
 * want to mutate the wire shape. The admin pages, on the other
 * hand, are the user-facing presentation boundary, so they should
 * render the same instant in the WordPress site timezone
 * (`wp_timezone()`) so an editor in JST sees a JST clock.
 *
 * Usage:
 *
 *     echo esc_html( AdminTimeFormatter::format( $row['created_at'] ) );
 *
 * Behavior:
 *
 *   - Returns the empty string when the input is null, the empty
 *     string, or fails to parse. Callers should guard with their
 *     own "value not present" branch when they want a different
 *     placeholder.
 *
 *   - Resolves the display timezone via
 *     {@see self::resolveDisplayTimezone()}, which prefers
 *     `wp_timezone()` (WordPress site timezone) and falls back to
 *     UTC when the helper is unavailable (pure-unit harness or
 *     CLI without WP loaded).
 *
 *   - Output shape is `Y-m-d H:i:s` (no `T` / `Z`) so the admin
 *     surface looks calendar-style, not log-style. Callers that
 *     need a different shape should format the returned
 *     {@see DateTimeImmutable} themselves.
 *
 * @package Affilun\Presentation\Admin
 */

declare(strict_types=1);

namespace Affilun\Presentation\Admin;

use DateTimeImmutable;
use DateTimeZone;
use Throwable;

/**
 * Reformats UTC-shaped admin-row timestamps into the WordPress
 * site-timezone display string.
 *
 * The class is deliberately stateless: every method is `static`
 * and takes its inputs as arguments. There are no instance fields
 * and no collaborators worth injecting; the WP timezone helper
 * is consulted on every call so a runtime change to the site
 * timezone takes effect on the next render.
 */
final class AdminTimeFormatter {

	/**
	 * Format the supplied admin-row timestamp string in the
	 * WordPress site timezone.
	 *
	 * Accepted input shapes:
	 *
	 *   - ISO-8601 with `Z` suffix    (`2026-04-30T01:51:08Z`)
	 *   - ISO-8601 with offset suffix (`2026-04-29T07:19:51+00:00`)
	 *   - MySQL DATETIME              (`2026-04-30 01:51:08` — UTC assumed)
	 *
	 * @param string|null $value Raw timestamp string or null.
	 *
	 * @return string Localized `Y-m-d H:i:s` string, or empty
	 *                string when the input could not be parsed.
	 */
	public static function format( ?string $value ): string {
		if ( $value === null || $value === '' ) {
			return '';
		}

		try {
			$instant = new DateTimeImmutable( $value );
		} catch ( Throwable $e ) {
			unset( $e );
			return '';
		}

		return $instant->setTimezone( self::resolveDisplayTimezone() )
			->format( 'Y-m-d H:i:s' );
	}

	/**
	 * Resolve the timezone admin-row timestamps should be rendered
	 * in. Prefers `wp_timezone()` (WordPress site timezone), falls
	 * back to UTC otherwise.
	 */
	private static function resolveDisplayTimezone(): DateTimeZone {
		if ( function_exists( 'wp_timezone' ) ) {
			try {
				$tz = wp_timezone();
				if ( $tz instanceof DateTimeZone ) {
					return $tz;
				}
			} catch ( Throwable $e ) {
				unset( $e );
			}
		}

		return new DateTimeZone( 'UTC' );
	}
}
