<?php
declare(strict_types=1);
namespace ConfManager\Admin;

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


use ConfManager\Traits\Singleton;
use ConfManager\Traits\HasHooks;

/**
 * 「What's New」ページ
 *
 * この無料版に含まれる機能と、別配布の Pro 版で追加できることを 1 画面で紹介する。
 *
 * WordPress.org ガイドラインとの関係:
 * - GL11 は「upgrade prompts は plugin's setting page に限る」としており、本ページは
 *   Conference メニュー配下の専用ページなので範囲内。他画面への admin notice は出さない。
 * - GL5（Trialware）対策として、Pro セクションは「別プラグインの紹介」であることを
 *   本文で明示する。この無料版にロックされたコードは 1 行も含まれない。
 * - GL8 対策として画像を一切使わない（外部 CDN 参照ゼロ・同梱アセット増加ゼロ）。
 *
 * @since 1.2.0
 */
class WhatsNewPage {

	use Singleton;
	use HasHooks;

	/**
	 * Pro 版の案内ページ。リンク先はここ 1 箇所だけに持つ。
	 */
	private const PRO_URL = 'https://cs24.biz/conference/pro/';

	/**
	 * 管理画面のメニュー slug
	 */
	private const PAGE_SLUG = 'conferencemanager-whats-new';

	/**
	 * NEW / UPDATED バッジを出す下限の版数
	 *
	 * これより古い版で追加・更新されたものはバッジを出さない。リリースのたびに
	 * ここを上げれば、古いカードのバッジは各エントリを編集しなくても自然に落ちる
	 * （版数を持たない基本機能・Pro カードは status の指定だけで判断する）。
	 */
	private const HIGHLIGHT_SINCE = '1.1.8';

	/**
	 * add_submenu_page() が返す hook suffix
	 *
	 * 親メニュータイトルが翻訳されると hook suffix も変わるため、リテラルで
	 * 比較してはならない（日本語 UI で CSS が読み込まれなくなる）。
	 *
	 * @var string|false
	 */
	private $hook_suffix = false;

	/**
	 * 初期化
	 */
	protected function init(): void {
		$this->register_hooks();
	}

	/**
	 * フック登録
	 */
	protected function register_hooks(): void {
		// AdminMenu が親メニューを登録した後に走らせる。
		$this->add_action( 'admin_menu', [ $this, 'register_menu' ], 11 );
		$this->add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
	}

	/**
	 * サブメニューを登録
	 */
	public function register_menu(): void {
		// 親と同じ slug で先に登録し、WordPress が自動生成する重複ラベル
		// （「Conference > Conference」）を「Dashboard」に置き換える。
		add_submenu_page(
			'conferencemanager',
			__( 'Conference Manager', 'conferencemanager' ),
			__( 'Dashboard', 'conferencemanager' ),
			'conf_manager_access',
			'conferencemanager'
		);

		$this->hook_suffix = add_submenu_page(
			'conferencemanager',
			__( 'What’s New', 'conferencemanager' ),
			__( 'What’s New', 'conferencemanager' ),
			'conf_manager_access',
			self::PAGE_SLUG,
			[ $this, 'render_page' ]
		);
	}

	/**
	 * スタイルをエンキュー
	 *
	 * React バンドルは読み込まない（このページは PHP でレンダリングする）。
	 *
	 * @param string $hook_suffix 現在の管理画面フックサフィックス
	 */
	public function enqueue_styles( string $hook_suffix ): void {
		if ( false === $this->hook_suffix || $hook_suffix !== $this->hook_suffix ) {
			return;
		}

		wp_enqueue_style(
			'conf-manager-admin',
			CONF_MANAGER_PLUGIN_URL . 'admin/css/admin.css',
			[ 'wp-components' ],
			CONF_MANAGER_VERSION
		);
	}

	/**
	 * ページを描画
	 */
	public function render_page(): void {
		?>
		<div class="wrap conf-manager-whatsnew">
			<h1><?php esc_html_e( 'Conference Manager — features & updates', 'conferencemanager' ); ?></h1>
			<?php // WordPress は .wrap 直下の h1 の後ろに他プラグインの管理通知を差し込む。 ?>
			<?php // 自分が描画したものだけを囲んでおくと、体裁も検証の境界も安定する。 ?>
			<div class="conf-manager-whatsnew__body">
			<p class="conf-manager-whatsnew__intro">
				<?php esc_html_e( 'What this plugin can do, newest additions first. The free plugin is listed at the top; the separately distributed Pro edition is listed below it.', 'conferencemanager' ); ?>
			</p>

			<h2 class="conf-manager-whatsnew__heading">
				<span class="conf-manager-badge conf-manager-badge--tier is-free"><?php esc_html_e( 'Free', 'conferencemanager' ); ?></span>
				<?php esc_html_e( 'Included in this plugin', 'conferencemanager' ); ?>
			</h2>
			<p class="conf-manager-whatsnew__note">
				<?php esc_html_e( 'Everything below is part of this plugin and works in full. Nothing here is locked, time-limited or unlocked by payment.', 'conferencemanager' ); ?>
			</p>
			<?php $this->render_cards( self::get_entries( 'free' ) ); ?>

			<h2 class="conf-manager-whatsnew__heading">
				<span class="conf-manager-badge conf-manager-badge--tier is-pro"><?php esc_html_e( 'Pro', 'conferencemanager' ); ?></span>
				<?php esc_html_e( 'Available in Conference Manager Pro', 'conferencemanager' ); ?>
			</h2>
			<p class="conf-manager-whatsnew__note">
				<?php esc_html_e( 'Conference Manager Pro is a separate plugin distributed by the plugin author. It is not required to use this plugin, and none of its code is included here.', 'conferencemanager' ); ?>
			</p>
			<?php $this->render_cards( self::get_entries( 'pro' ) ); ?>

			<p class="conf-manager-whatsnew__cta">
				<a href="<?php echo esc_url( self::PRO_URL ); ?>" target="_blank" rel="noopener noreferrer">
					<?php esc_html_e( 'Learn more about Conference Manager Pro', 'conferencemanager' ); ?>
				</a>
			</p>
			</div>
		</div>
		<?php
	}

	/**
	 * カードグリッドを描画
	 *
	 * @param array<int, array<string, string>> $entries 機能エントリ
	 */
	private function render_cards( array $entries ): void {
		if ( empty( $entries ) ) {
			return;
		}
		?>
		<div class="conf-manager-whatsnew__cards">
			<?php foreach ( $entries as $entry ) : ?>
				<div class="conf-manager-whatsnew__card<?php echo 'pro' === $entry['edition'] ? ' is-pro' : ''; ?>">
					<div class="conf-manager-whatsnew__card-head">
						<span class="dashicons <?php echo esc_attr( 'dashicons-' . $entry['icon'] ); ?>" aria-hidden="true"></span>
						<h3><?php echo esc_html( $entry['title'] ); ?></h3>
						<?php if ( self::shows_flag( $entry ) ) : ?>
							<span class="conf-manager-badge conf-manager-whatsnew__flag is-<?php echo esc_attr( $entry['status'] ); ?>">
								<?php
								if ( 'updated' === $entry['status'] ) {
									esc_html_e( 'Updated', 'conferencemanager' );
								} else {
									esc_html_e( 'New', 'conferencemanager' );
								}
								?>
							</span>
						<?php endif; ?>
					</div>
					<p><?php echo esc_html( $entry['description'] ); ?></p>
					<?php if ( '' !== $entry['version'] ) : ?>
						<p class="conf-manager-whatsnew__meta">
							<?php
							$timestamp = '' !== $entry['date'] ? strtotime( $entry['date'] ) : false;
							if ( false !== $timestamp ) {
								printf(
									/* translators: 1: plugin version number, 2: release date */
									esc_html__( 'v%1$s · %2$s', 'conferencemanager' ),
									esc_html( $entry['version'] ),
									esc_html( date_i18n( (string) get_option( 'date_format' ), $timestamp ) )
								);
							} else {
								printf(
									/* translators: %s: plugin version number */
									esc_html__( 'v%s', 'conferencemanager' ),
									esc_html( $entry['version'] )
								);
							}
							?>
						</p>
					<?php endif; ?>
				</div>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * NEW / UPDATED バッジを出すか
	 *
	 * 版数を持つエントリは HIGHLIGHT_SINCE で足切りする。現行版で追加・更新された
	 * ものは、HIGHLIGHT_SINCE の上げ忘れがあっても必ず出す。
	 *
	 * @param array<string, string> $entry 機能エントリ.
	 * @return bool
	 */
	private static function shows_flag( array $entry ): bool {
		if ( '' === $entry['status'] ) {
			return false;
		}
		if ( '' === $entry['version'] ) {
			return true;
		}
		return CONF_MANAGER_VERSION === $entry['version']
			|| version_compare( $entry['version'], self::HIGHLIGHT_SINCE, '>=' );
	}

	/**
	 * 機能エントリを取得する
	 *
	 * version / date が空のものは「初回リリースから入っている基本機能」で、
	 * 版数バッジを出さずに新しい追加分の後ろに並べる。
	 *
	 * @param string $edition 'free' | 'pro' | '' （空なら全件）
	 * @return array<int, array<string, string>>
	 */
	public static function get_entries( string $edition = '' ): array {
		$with_edition = static function ( array $entries, string $tier ): array {
			return array_map(
				static function ( array $entry ) use ( $tier ): array {
					$entry['edition'] = $tier;
					return $entry;
				},
				$entries
			);
		};

		$entries = array_merge(
			$with_edition( self::get_free_entries(), 'free' ),
			$with_edition( self::get_pro_entries(), 'pro' )
		);

		if ( '' !== $edition ) {
			$entries = array_values(
				array_filter(
					$entries,
					static fn( array $entry ): bool => $entry['edition'] === $edition
				)
			);
		}

		// 日付の新しい順。日付を持たない基本機能は宣言順のまま末尾へ。
		usort(
			$entries,
			static function ( array $a, array $b ): int {
				if ( '' === $a['date'] && '' === $b['date'] ) {
					return 0;
				}
				if ( '' === $a['date'] ) {
					return 1;
				}
				if ( '' === $b['date'] ) {
					return -1;
				}
				if ( $a['date'] === $b['date'] ) {
					return version_compare( $b['version'], $a['version'] );
				}
				return strcmp( $b['date'], $a['date'] );
			}
		);

		return $entries;
	}

	/**
	 * この無料版に含まれる機能
	 *
	 * 版数と日付は readme.txt の changelog に対応する。
	 *
	 * @return array<int, array<string, string>>
	 */
	private static function get_free_entries(): array {
		return [
			[
				'id'          => 'settlement',
				'title'       => __( 'Payment settlement', 'conferencemanager' ),
				'description' => __( 'Track the billed amount and the amount actually received separately, then settle the difference by refund or surcharge from one dialog.', 'conferencemanager' ),
				'version'     => '1.1.9',
				'date'        => '2026-08-05',
				'status'      => 'new',
				'icon'        => 'money-alt',
			],
			[
				'id'          => 'trustworthy-mail',
				'title'       => __( 'Recognisable verification email', 'conferencemanager' ),
				'description' => __( 'One-time codes and My Page login emails carry your site name, the purpose and a contact footer, so strict mail providers are less likely to reject them.', 'conferencemanager' ),
				'version'     => '1.1.8',
				'date'        => '2026-08-04',
				'status'      => 'new',
				'icon'        => 'email-alt',
			],
			[
				'id'          => 'receipt-addressee',
				'title'       => __( 'Self-service receipt addressee', 'conferencemanager' ),
				'description' => __( 'Participants can correct the addressee and company on their receipt from My Page. The PDF is reissued under the same receipt number.', 'conferencemanager' ),
				'version'     => '1.1.6',
				'date'        => '2026-07-06',
				'status'      => '',
				'icon'        => 'media-text',
			],
			[
				'id'          => 'transfer-note',
				'title'       => __( 'Bank transfer name note', 'conferencemanager' ),
				'description' => __( 'A participant paying under a company or family name can tell you the name the transfer will arrive under. It is never printed on the receipt.', 'conferencemanager' ),
				'version'     => '1.1.6',
				'date'        => '2026-07-06',
				'status'      => '',
				'icon'        => 'nametag',
			],
			[
				'id'          => 'fee-category-fix',
				'title'       => __( 'Correct a fee category after the fact', 'conferencemanager' ),
				'description' => __( 'Change a registration’s fee category and participation type from the Participants screen. It now also works after payment has been received, surfacing the difference as a refund or a surcharge.', 'conferencemanager' ),
				'version'     => '1.1.9',
				'date'        => '2026-08-05',
				'status'      => 'updated',
				'icon'        => 'edit',
			],
			[
				'id'          => 'admin-notify',
				'title'       => __( 'Configurable admin notification', 'conferencemanager' ),
				'description' => __( 'Choose who receives the new-registration notice — one or more addresses, or the WordPress admin email by default.', 'conferencemanager' ),
				'version'     => '1.1.5',
				'date'        => '2026-06-19',
				'status'      => '',
				'icon'        => 'admin-users',
			],
			[
				'id'          => 'free-events',
				'title'       => __( 'Free events confirm themselves', 'conferencemanager' ),
				'description' => __( 'Registrations totalling zero are confirmed automatically, with no payment record and no bank details in the confirmation email.', 'conferencemanager' ),
				'version'     => '1.1.4',
				'date'        => '2026-06-18',
				'status'      => '',
				'icon'        => 'tickets-alt',
			],
			[
				'id'          => 'email-preferences',
				'title'       => __( 'Email opt-out for participants', 'conferencemanager' ),
				'description' => __( 'An Email Preferences tab in My Page plus an RFC 8058 List-Unsubscribe header, for compliance with anti-spam law.', 'conferencemanager' ),
				'version'     => '1.1.0',
				'date'        => '2026-04-21',
				'status'      => '',
				'icon'        => 'email',
			],
			[
				'id'          => 'registration',
				'title'       => __( 'Registration & group registration', 'conferencemanager' ),
				'description' => __( 'Build the registration form, take individual and group applications, and manage everyone from the participant list.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'list-view',
			],
			[
				'id'          => 'events',
				'title'       => __( 'Events & sessions', 'conferencemanager' ),
				'description' => __( 'Set up events with their sessions, fee categories and tax handling.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'calendar-alt',
			],
			[
				'id'          => 'members',
				'title'       => __( 'Member management', 'conferencemanager' ),
				'description' => __( 'Member records with categories, plus CSV and Excel import and export.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'groups',
			],
			[
				'id'          => 'documents',
				'title'       => __( 'Invoice & receipt PDFs', 'conferencemanager' ),
				'description' => __( 'Generate invoices and receipts as PDFs with proper Japanese typesetting. The IPAex fonts install with one click from the Dashboard.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'media-document',
			],
			[
				'id'          => 'mypage',
				'title'       => __( 'My Page for participants', 'conferencemanager' ),
				'description' => __( 'Participants review their registrations and documents, signing in with a one-time code sent to their email address.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'admin-network',
			],
			[
				'id'          => 'security',
				'title'       => __( 'Encryption & audit logging', 'conferencemanager' ),
				'description' => __( 'Email addresses, phone numbers and addresses are stored AES-256-GCM encrypted, with audit logs, rate limiting and bot protection.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'lock',
			],
		];
	}

	/**
	 * 別配布の Pro 版で追加できること
	 *
	 * 実際に配布している Pro 版に存在する機能だけを載せる。
	 *
	 * @return array<int, array<string, string>>
	 */
	private static function get_pro_entries(): array {
		return [
			[
				'id'          => 'pro-submissions',
				'title'       => __( 'Abstract submission & peer review', 'conferencemanager' ),
				'description' => __( 'Authors submit abstracts with co-authors; reviewers are assigned, score and comment, and the decision goes back to the author.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => 'updated',
				'icon'        => 'media-document',
			],
			[
				'id'          => 'pro-sponsors',
				'title'       => __( 'Sponsor management', 'conferencemanager' ),
				'description' => __( 'A sponsor portal with ranks, PR PDF delivery, sponsor staff passes and QR reception.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'building',
			],
			[
				'id'          => 'pro-notifications',
				'title'       => __( 'Bulk & scheduled email', 'conferencemanager' ),
				'description' => __( 'Send to a filtered audience, split large sends into batches, schedule them in advance, and honour one-click unsubscribe.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => 'updated',
				'icon'        => 'email',
			],
			[
				'id'          => 'pro-certificate',
				'title'       => __( 'Attendance certificates', 'conferencemanager' ),
				'description' => __( 'Issue certificates of attendance as PDFs, individually or for everyone at once, with a QR code for verification.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'awards',
			],
			[
				'id'          => 'pro-checkin',
				'title'       => __( 'QR reception desk', 'conferencemanager' ),
				'description' => __( 'Check participants in on the day by scanning their QR code with a phone or tablet.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'camera',
			],
			[
				'id'          => 'pro-membership-fees',
				'title'       => __( 'Annual membership fees', 'conferencemanager' ),
				'description' => __( 'Track annual dues, and let unpaid membership fall back to the non-member price semi-automatically.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'money-alt',
			],
			[
				'id'          => 'pro-coupons',
				'title'       => __( 'Referral coupons & discounts', 'conferencemanager' ),
				'description' => __( 'Issue referral coupons and discount codes with one-time redemption and usage limits.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'tickets-alt',
			],
			[
				'id'          => 'pro-stripe',
				'title'       => __( 'Stripe card payments', 'conferencemanager' ),
				'description' => __( 'Accept credit cards with your own Stripe keys. Money goes straight to your account — this plugin takes no percentage.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'cart',
			],
			[
				'id'          => 'pro-fee-summary',
				'title'       => __( 'Fee totals & payment records', 'conferencemanager' ),
				'description' => __( 'Aggregate participation fees across categories and produce the payment records your accounting needs.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'chart-bar',
			],
			[
				'id'          => 'pro-invitations',
				'title'       => __( 'Speaker & chair invitations', 'conferencemanager' ),
				'description' => __( 'Send and track invitations to invited speakers and session chairs, with a printable single-page letter.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'megaphone',
			],
			[
				'id'          => 'pro-program-pdf',
				'title'       => __( 'Programme PDFs', 'conferencemanager' ),
				'description' => __( 'Build the conference programme as a PDF from your sessions and accepted abstracts.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'book',
			],
			[
				'id'          => 'pro-promotion',
				'title'       => __( 'Social promotion', 'conferencemanager' ),
				'description' => __( 'Generate post templates for social networks and hand off to LINE, with customisable OGP.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'share',
			],
			[
				'id'          => 'pro-mfa',
				'title'       => __( 'Multi-factor administrator sign-in', 'conferencemanager' ),
				'description' => __( 'Add a second factor to administrator sign-in for sites handling large volumes of personal data.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'shield',
			],
			[
				'id'          => 'pro-online',
				'title'       => __( 'Online participation', 'conferencemanager' ),
				'description' => __( 'Manage online attendees and their meeting details alongside on-site participants.', 'conferencemanager' ),
				'version'     => '',
				'date'        => '',
				'status'      => '',
				'icon'        => 'video-alt3',
			],
		];
	}
}
