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

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


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

/**
 * 管理用ユーティリティ REST API
 *
 * ダッシュボード用のシステム状態チェック等を提供する。
 *
 * @since 1.0.0
 */
class AdminApi {

	use Singleton;
	use HasHooks;

	private const NAMESPACE = 'conf-manager/v1';

	protected function init(): void {
		$this->register_hooks();
	}

	protected function register_hooks(): void {
		$this->add_action( 'rest_api_init', [ $this, 'register_routes' ] );
	}

	public function register_routes(): void {
		register_rest_route( self::NAMESPACE, '/admin/system-status', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_system_status' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_manage_settings' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/admin/dashboard-stats', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_dashboard_stats' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_view_dashboard' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/admin/notification-settings', [
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'save_notification_settings' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_manage_settings' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/admin/smtp-test', [
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'test_smtp' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_manage_settings' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/admin/payment-settings', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_payment_settings' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_manage_settings' ),
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'save_payment_settings' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_manage_settings' ),
			],
		] );

	}

	/**
	 * ダッシュボード統計（閲覧者ロール向け、機密情報なし）
	 */
	public function get_dashboard_stats(): \WP_REST_Response {
		global $wpdb;

		$member_count       = \ConfManager\Members\MemberManager::instance()->count( 'all' );
		$event_count_result = \ConfManager\Events\EventManager::instance()->search( [ 'per_page' => 1 ] );

		// 承認待ち件数
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$pending_approval = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->prefix}conf_members WHERE approval_status = 'pending' AND status = 'inactive'" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
		);

		// イベント別参加者数（アクティブイベント、上位5件）
		$reg_table   = $wpdb->prefix . 'conf_registrations';
		$event_table = $wpdb->prefix . 'conf_events';
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names from plugin-defined prefix, not user input; no user values in this query.
		$event_stats = $wpdb->get_results(
			"SELECT e.id, e.title, e.date_start,
				COUNT(r.id) AS total,
				SUM(CASE WHEN r.status = 'paid' OR r.status = 'attended' THEN 1 ELSE 0 END) AS paid_count
			FROM {$event_table} e
			LEFT JOIN {$reg_table} r ON r.event_id = e.id AND r.status != 'cancelled'
			WHERE e.status IN ('published', 'closed')
			GROUP BY e.id
			ORDER BY e.date_start DESC
			LIMIT 5",
			ARRAY_A
		) ?: [];
		// phpcs:enable

		// 決済サマリー（全イベント合計）
		$pay_table     = $wpdb->prefix . 'conf_payments';
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from plugin-defined prefix, not user input; no user values in this query.
		$payment_stats = $wpdb->get_row(
			"SELECT
				COALESCE(SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END), 0) AS total_paid,
				COALESCE(SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END), 0) AS total_unpaid,
				COUNT(CASE WHEN status = 'completed' THEN 1 END) AS paid_count,
				COUNT(CASE WHEN status = 'pending' THEN 1 END) AS unpaid_count
			FROM {$pay_table}",
			ARRAY_A
		);
		// phpcs:enable

		return new \WP_REST_Response( [
			'counts' => [
				'members'          => $member_count,
				'events'           => $event_count_result['total'],
				'pending_approval' => $pending_approval,
			],
			'event_stats'   => $event_stats,
			'payment_stats' => $payment_stats,
			'pages' => $this->get_pages_status(),
		], 200 );
	}

	/**
	 * 固定ページ状態チェック
	 */
	private function get_pages_status(): array {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$registration_page = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_status = %s AND post_type = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				'%[conf_manager]%',
				'publish',
				'page'
			)
		);
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$mypage_page       = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_status = %s AND post_type = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				'%[conf_manager view="mypage"]%',
				'publish',
				'page'
			)
		);
		return [
			'registration' => ! empty( $registration_page ),
			'mypage'       => ! empty( $mypage_page ),
		];
	}

	/**
	 * システム状態を返す（管理者のみ）
	 */
	public function get_system_status(): \WP_REST_Response {
		$font_dir = \ConfManager\Fonts\FontManager::get_font_dir();

		$fonts = [
			'ipaexg'     => [
				'name'   => 'IPAexゴシック',
				'file'   => 'ipaexg.ttf',
				'exists' => file_exists( $font_dir . '/ipaexg.ttf' ),
			],
			'ipaexm'     => [
				'name'   => 'IPAex明朝',
				'file'   => 'ipaexm.ttf',
				'exists' => file_exists( $font_dir . '/ipaexm.ttf' ),
			],
			'notosansjp' => [
				'name'   => 'Noto Sans JP',
				'file'   => 'NotoSansJP.ttf',
				'exists' => file_exists( $font_dir . '/NotoSansJP.ttf' ),
			],
		];

		$pdf_available   = class_exists( '\\Mpdf\\Mpdf' );
		$any_font_exists = $fonts['ipaexg']['exists'] || $fonts['ipaexm']['exists'] || $fonts['notosansjp']['exists'];

		$encryption_key_hash   = '';
		$encryption_key_set    = false;
		$encryption_key_source = 'none';
		try {
			$enc = \ConfManager\Security\Encryption::instance();
			$encryption_key_set    = true;
			$encryption_key_hash   = substr( $enc->get_current_key_hash(), 0, 8 );
			$encryption_key_source = $enc->get_key_source();
		} catch ( \Throwable $e ) {
			// キー取得に失敗（通常は発生しない）
		}

		$member_count       = \ConfManager\Members\MemberManager::instance()->count( 'all' );
		$event_count_result = \ConfManager\Events\EventManager::instance()->search( [ 'per_page' => 1 ] );

		// タイムゾーン情報
		$timezone_string = get_option( 'timezone_string', '' );
		$gmt_offset      = get_option( 'gmt_offset', 0 );
		$timezone_set    = ! empty( $timezone_string ) || 0 !== (int) $gmt_offset;

		// 固定ページ（ショートコード）チェック
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$registration_page = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_status = %s AND post_type = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				'%[conf_manager]%',
				'publish',
				'page'
			)
		);
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$mypage_page       = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_status = %s AND post_type = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				'%[conf_manager view="mypage"]%',
				'publish',
				'page'
			)
		);

		return new \WP_REST_Response( [
			'fonts'              => $fonts,
			'pdf_available'      => $pdf_available,
			'any_font_exists'    => $any_font_exists,
			'encryption'         => [
				'key_set'    => $encryption_key_set,
				'key_hash'   => $encryption_key_hash,
				'key_source' => $encryption_key_source,
			],
			'counts'             => [
				'members' => $member_count,
				'events'  => $event_count_result['total'],
			],
			'timezone'           => [
				'string'   => $timezone_string,
				'offset'   => $gmt_offset,
				'is_set'   => $timezone_set,
				'display'  => $timezone_string ?: 'UTC' . ( $gmt_offset >= 0 ? '+' : '' ) . $gmt_offset,
			],
			'pages'              => [
				'registration' => ! empty( $registration_page ),
				'mypage'       => ! empty( $mypage_page ),
			],
			'mail'               => $this->get_mail_status(),
			'payment_settings'   => $this->get_payment_settings_data(),
			'admin_email'           => get_option( 'admin_email', '' ),
			'notification_settings' => [
				'admin_notify_registration' => (bool) get_option( 'conf_manager_admin_notify_registration', true ),
				'notification_email'        => get_option( 'conf_manager_notification_email', '' ),
				'require_approval'          => (bool) get_option( 'conf_manager_require_approval', false ),
				// 会員種別の表示に前置する学会の略称。
				// 🔴 既定は空。「空欄ならサイト名」にすると、更新した瞬間に
				//    全サイトの表示が変わってしまう。
				'society_short_name'        => \ConfManager\Members\MemberTypeLabel::society_short_name(),
			],
			'smtp' => [
				'configured' => 'none' !== \ConfManager\Notification\MailHeaders::get_smtp_source(),
				'source'     => \ConfManager\Notification\MailHeaders::get_smtp_source(),
				'host'       => \ConfManager\Notification\MailHeaders::get_smtp_settings()['host'],
				'port'       => \ConfManager\Notification\MailHeaders::get_smtp_settings()['port'],
				'user'       => \ConfManager\Notification\MailHeaders::get_smtp_settings()['user'],
				'secure'     => \ConfManager\Notification\MailHeaders::get_smtp_settings()['secure'],
			],
			'mail_bounces_count' => count( get_option( 'conf_manager_mail_bounces', [] ) ),
		], 200 );
	}

	/**
	 * メール配信の状態を返す
	 */
	private function get_mail_status(): array {
		// WP Mail SMTP プラグインの検出
		$smtp_plugin      = false;
		$smtp_plugin_name = '';
		if ( class_exists( 'WPMailSMTP\\Core' ) ) {
			$smtp_plugin      = true;
			$smtp_plugin_name = 'WP Mail SMTP';
		} elseif ( class_exists( 'PostSMTP\\Postman' ) || defined( 'POST_SMTP_VER' ) ) {
			$smtp_plugin      = true;
			$smtp_plugin_name = 'Post SMTP';
		} elseif ( function_exists( 'swpsmtp_init' ) ) {
			$smtp_plugin      = true;
			$smtp_plugin_name = 'Easy WP SMTP';
		}

		// FROM設定・ドメイン情報
		$admin_email = get_option( 'admin_email', '' );
		$site_name   = get_option( 'blogname', '' );
		$site_url    = get_option( 'siteurl', '' );
		$domain      = wp_parse_url( $site_url, PHP_URL_HOST ) ?: '';

		return [
			'smtp_plugin'      => $smtp_plugin,
			'smtp_plugin_name' => $smtp_plugin_name,
			'admin_email'      => $admin_email,
			'site_name'        => $site_name,
			'domain'           => $domain,
		];
	}

	/**
	 * 決済手段設定データを取得
	 */
	private function get_payment_settings_data(): array {
		$methods = get_option( 'conf_manager_payment_methods', [ 'bank' ] );
		if ( ! is_array( $methods ) ) {
			$methods = [ 'bank' ];
		}

		return [
			'methods'             => $methods,
			'tax_registration_no' => get_option( 'conf_manager_tax_registration_no', '' ),
		];
	}

	/**
	 * 決済手段設定を取得（REST API）
	 */
	public function get_payment_settings(): \WP_REST_Response {
		return new \WP_REST_Response( $this->get_payment_settings_data(), 200 );
	}

	/**
	 * 決済手段設定を保存
	 */
	public function save_payment_settings( \WP_REST_Request $request ): \WP_REST_Response {
		$methods = $request->get_param( 'methods' );
		if ( is_array( $methods ) ) {
			// ⚠️ v1.4.0 でカード決済（Stripe）は Pro 版の機能になった。
			// 保存できる手段からは外す（過去に保存された 'stripe' は
			// PublicController が公開画面へ出す前に落とす）。
			$valid   = [ 'bank' ];
			$methods = array_values( array_intersect( $methods, $valid ) );
			if ( empty( $methods ) ) {
				$methods = [ 'bank' ];
			}
			update_option( 'conf_manager_payment_methods', $methods, false );
		}

		$tax_registration_no = $request->get_param( 'tax_registration_no' );
		if ( null !== $tax_registration_no ) {
			update_option( 'conf_manager_tax_registration_no', sanitize_text_field( $tax_registration_no ), false );
		}

		return new \WP_REST_Response( [
			'success' => true,
			'data'    => $this->get_payment_settings_data(),
		], 200 );
	}

	/**
	 * 通知設定を保存
	 */
	public function save_notification_settings( \WP_REST_Request $request ): \WP_REST_Response {
		$admin_notify = $request->get_param( 'admin_notify_registration' );
		if ( null !== $admin_notify ) {
			update_option( 'conf_manager_admin_notify_registration', (bool) $admin_notify, false );
		}

		// 通知先メールアドレス（カンマ区切りで複数可・空欄なら admin_email にフォールバック）
		$notification_email = $request->get_param( 'notification_email' );
		if ( null !== $notification_email ) {
			$valid = [];
			foreach ( explode( ',', (string) $notification_email ) as $candidate ) {
				$candidate = sanitize_email( trim( $candidate ) );
				if ( '' !== $candidate && is_email( $candidate ) ) {
					$valid[] = $candidate;
				}
			}
			update_option( 'conf_manager_notification_email', implode( ', ', array_unique( $valid ) ), false );
		}

		$require_approval = $request->get_param( 'require_approval' );
		if ( null !== $require_approval ) {
			update_option( 'conf_manager_require_approval', (bool) $require_approval, false );
		}

		// 会員種別に前置する学会の略称。
		// ⚠️ **空文字の保存を必ず通すこと。** 空にするのが唯一の「前置をやめる」手段なので、
		//    `if ( '' !== $value )` のように弾くと一度入れたら二度と外せなくなる。
		$society_short_name = $request->get_param( 'society_short_name' );
		if ( null !== $society_short_name ) {
			update_option(
				\ConfManager\Members\MemberTypeLabel::OPTION,
				sanitize_text_field( (string) $society_short_name ),
				false
			);
		}

		// SMTP設定の保存（wp-config.php未設定の場合のみ）
		$smtp_host = $request->get_param( 'smtp_host' );
		if ( null !== $smtp_host && ! defined( 'CONF_MANAGER_SMTP_HOST' ) ) {
			update_option( 'conf_manager_smtp_host', sanitize_text_field( $smtp_host ), false );
		}

		$smtp_port = $request->get_param( 'smtp_port' );
		if ( null !== $smtp_port && ! defined( 'CONF_MANAGER_SMTP_PORT' ) ) {
			update_option( 'conf_manager_smtp_port', absint( $smtp_port ), false );
		}

		$smtp_user = $request->get_param( 'smtp_user' );
		if ( null !== $smtp_user && ! defined( 'CONF_MANAGER_SMTP_USER' ) ) {
			update_option( 'conf_manager_smtp_user', sanitize_text_field( $smtp_user ), false );
		}

		$smtp_secure = $request->get_param( 'smtp_secure' );
		if ( null !== $smtp_secure && ! defined( 'CONF_MANAGER_SMTP_SECURE' ) ) {
			$smtp_secure = in_array( $smtp_secure, [ 'tls', 'ssl' ], true ) ? $smtp_secure : 'tls';
			update_option( 'conf_manager_smtp_secure', $smtp_secure, false );
		}

		// パスワードは暗号化して保存
		$smtp_password = $request->get_param( 'smtp_password' );
		if ( null !== $smtp_password && '' !== $smtp_password && ! defined( 'CONF_MANAGER_SMTP_PASSWORD' ) ) {
			$encrypted = \ConfManager\Security\Encryption::instance()->encrypt( $smtp_password );
			update_option( 'conf_manager_smtp_password_encrypted', $encrypted, false );
		}

		return new \WP_REST_Response( [
			'success' => true,
			'data'    => [
				'admin_notify_registration' => (bool) get_option( 'conf_manager_admin_notify_registration', true ),
				'notification_email'        => get_option( 'conf_manager_notification_email', '' ),
				'smtp_configured'           => 'none' !== \ConfManager\Notification\MailHeaders::get_smtp_source(),
			],
		], 200 );
	}

	/**
	 * SMTPテスト送信
	 */
	public function test_smtp( \WP_REST_Request $request ): \WP_REST_Response {
		$to = get_option( 'admin_email', '' );
		if ( empty( $to ) ) {
			return new \WP_REST_Response( [ 'success' => false, 'message' => __( 'Admin email address is not configured.', 'conferencemanager' ) ], 200 );
		}

		$subject = __( '[Conference Manager] SMTP Test', 'conferencemanager' );
		$body    = sprintf(
			/* translators: %s: date and time of the test */
			__( "This is a test email from Conference Manager.\n\nSent at: %s\nIf you received this email, your SMTP configuration is working correctly.", 'conferencemanager' ),
			wp_date( 'Y-m-d H:i:s' )
		);

		$result = wp_mail( $to, $subject, $body );

		$settings = \ConfManager\Notification\MailHeaders::get_smtp_settings();
		$via      = ! empty( $settings['host'] )
			/* translators: 1: SMTP host, 2: SMTP port number */
			? sprintf( __( 'SMTP (%1$s:%2$d)', 'conferencemanager' ), $settings['host'], $settings['port'] )
			: __( 'PHP mail()', 'conferencemanager' );

		if ( $result ) {
			return new \WP_REST_Response( [
				'success' => true,
				/* translators: 1: recipient email address, 2: delivery method */
				'message' => sprintf( __( 'A test email has been sent to %1$s (via %2$s).', 'conferencemanager' ), $to, $via ),
			], 200 );
		}

		return new \WP_REST_Response( [
			'success' => false,
			'message' => __( 'Failed to send email. Please check your SMTP settings.', 'conferencemanager' ),
		], 200 );
	}

}
