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

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


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

/**
 * メールヘッダー管理
 *
 * wp_mail フィルタを使い、全送信メールに適切なヘッダーを統一適用する。
 * - FROM / FROM Name の統一
 * - Reply-To ヘッダー
 * - List-Unsubscribe ヘッダー（一斉通知のみ）
 * - Content-Type の統一
 *
 * @since 1.4.1
 */
class MailHeaders {

	use Singleton;
	use HasHooks;

	/** @var bool 一斉通知の送信中フラグ */
	private bool $is_bulk_sending = false;

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

	protected function register_hooks(): void {
		$this->add_filter( 'wp_mail_from', [ $this, 'filter_from_email' ], 10, 1 );
		$this->add_filter( 'wp_mail_from_name', [ $this, 'filter_from_name' ], 10, 1 );
		$this->add_filter( 'wp_mail', [ $this, 'filter_wp_mail' ], 10, 1 );
		$this->add_action( 'phpmailer_init', [ $this, 'configure_smtp' ], 10, 1 );
		$this->add_action( 'wp_mail_failed', [ $this, 'handle_mail_failure' ], 10, 1 );
	}

	/**
	 * FROM メールアドレスを管理者メールに統一
	 */
	public function filter_from_email( string $from_email ): string {
		$admin_email = get_option( 'admin_email', '' );
		if ( ! empty( $admin_email ) && is_email( $admin_email ) ) {
			return $admin_email;
		}
		return $from_email;
	}

	/**
	 * FROM 名をサイト名に統一
	 */
	public function filter_from_name( string $from_name ): string {
		$site_name = get_option( 'blogname', '' );
		if ( ! empty( $site_name ) ) {
			return $site_name;
		}
		return $from_name;
	}

	/**
	 * wp_mail フィルタで追加ヘッダーを付与
	 *
	 * @param array $args { to, subject, message, headers, attachments }
	 * @return array
	 */
	public function filter_wp_mail( array $args ): array {
		$headers = $args['headers'];
		if ( is_string( $headers ) ) {
			$headers = empty( $headers ) ? [] : explode( "\n", $headers );
		}
		if ( ! is_array( $headers ) ) {
			$headers = [];
		}

		$admin_email = get_option( 'admin_email', '' );

		// Reply-To（未設定の場合のみ追加）
		$has_reply_to = false;
		foreach ( $headers as $header ) {
			if ( stripos( $header, 'Reply-To:' ) !== false ) {
				$has_reply_to = true;
				break;
			}
		}
		if ( ! $has_reply_to && ! empty( $admin_email ) ) {
			$site_name = get_option( 'blogname', '' );
			$headers[] = sprintf( 'Reply-To: %s <%s>', $site_name, $admin_email );
		}

		// List-Unsubscribe（一斉通知の場合のみ — RFC 8058）
		if ( $this->is_bulk_sending ) {
			$mypage_url = $this->get_mypage_url();
			if ( ! empty( $mypage_url ) ) {
				$unsubscribe_url = add_query_arg( 'tab', 'email-settings', $mypage_url );
				$headers[] = sprintf(
					'List-Unsubscribe: <%s>, <mailto:%s?subject=unsubscribe>',
					$unsubscribe_url,
					$admin_email
				);
			} elseif ( ! empty( $admin_email ) ) {
				$headers[] = sprintf( 'List-Unsubscribe: <mailto:%s?subject=unsubscribe>', $admin_email );
			}
			$headers[] = 'List-Unsubscribe-Post: List-Unsubscribe=One-Click';
		}

		$args['headers'] = $headers;
		return $args;
	}

	/**
	 * 一斉通知送信モードを開始
	 */
	public function start_bulk_sending(): void {
		$this->is_bulk_sending = true;
	}

	/**
	 * 一斉通知送信モードを終了
	 */
	public function stop_bulk_sending(): void {
		$this->is_bulk_sending = false;
	}

	/**
	 * マイページのURLを取得する
	 */
	private function get_mypage_url(): string {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$page_id = $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",
				'%[conf_manager view="mypage"]%',
				'publish',
				'page'
			)
		);
		return $page_id ? (string) get_permalink( (int) $page_id ) : '';
	}

	/**
	 * SMTP 設定を PHPMailer に適用する
	 *
	 * 独自ドメインのメールアカウント（Xserver等）を利用してSMTP認証送信を行う。
	 * wp-config.php 定数を優先し、未設定ならDB設定をフォールバック。
	 * TLS/SSL暗号化は必須（平文SMTPは使用不可）。
	 *
	 * @param \PHPMailer\PHPMailer\PHPMailer $phpmailer
	 */
	public function configure_smtp( $phpmailer ): void {
		$settings = self::get_smtp_settings();

		if ( empty( $settings['host'] ) || empty( $settings['user'] ) || empty( $settings['password'] ) ) {
			return;
		}

		$secure = $settings['secure'];
		if ( ! in_array( $secure, [ 'tls', 'ssl' ], true ) ) {
			$secure = 'tls';
		}

		$phpmailer->isSMTP();
		$phpmailer->Host       = $settings['host'];
		$phpmailer->Port       = (int) $settings['port'];
		$phpmailer->SMTPAuth   = true;
		$phpmailer->Username   = $settings['user'];
		$phpmailer->Password   = $settings['password'];
		$phpmailer->SMTPSecure = $secure;

		// 🔴 PHPMailer の既定は 300 秒。OTP は wp_mail() で同期送信なので、
		//    外部 SMTP が詰まると**利用者の画面が最大 5 分固まる**。固まれば
		//    利用者は「再送信」を押す＝レート制限の枠を消費する。
		//    📌 遅延そのものは直らない。画面が固まるのを止めるだけ。
		$timeout            = (int) apply_filters( 'conf_manager_smtp_timeout', 15 );
		$phpmailer->Timeout = $timeout > 0 ? $timeout : 15;
	}

	/**
	 * SMTP設定を取得する（定数優先、DBフォールバック）
	 *
	 * @return array{host: string, port: int, user: string, password: string, secure: string}
	 */
	public static function get_smtp_settings(): array {
		$host     = defined( 'CONF_MANAGER_SMTP_HOST' ) ? CONF_MANAGER_SMTP_HOST : get_option( 'conf_manager_smtp_host', '' );
		$port     = defined( 'CONF_MANAGER_SMTP_PORT' ) ? (int) CONF_MANAGER_SMTP_PORT : (int) get_option( 'conf_manager_smtp_port', 587 );
		$user     = defined( 'CONF_MANAGER_SMTP_USER' ) ? CONF_MANAGER_SMTP_USER : get_option( 'conf_manager_smtp_user', '' );
		$secure   = defined( 'CONF_MANAGER_SMTP_SECURE' ) ? CONF_MANAGER_SMTP_SECURE : get_option( 'conf_manager_smtp_secure', 'tls' );

		// パスワード: 定数優先、なければDB（暗号化保存）から復号
		$password = '';
		if ( defined( 'CONF_MANAGER_SMTP_PASSWORD' ) ) {
			$password = CONF_MANAGER_SMTP_PASSWORD;
		} else {
			$encrypted = get_option( 'conf_manager_smtp_password_encrypted', '' );
			if ( '' !== $encrypted ) {
				try {
					$password = \ConfManager\Security\Encryption::instance()->decrypt( $encrypted );
				} catch ( \RuntimeException $e ) {
					$password = '';
				}
			}
		}

		return [
			'host'     => $host,
			'port'     => $port,
			'user'     => $user,
			'password' => $password,
			'secure'   => $secure,
		];
	}

	/**
	 * SMTP設定の設定元を返す
	 *
	 * @return string 'config' | 'database' | 'none'
	 */
	public static function get_smtp_source(): string {
		if ( defined( 'CONF_MANAGER_SMTP_HOST' ) && '' !== CONF_MANAGER_SMTP_HOST ) {
			return 'config';
		}
		if ( '' !== get_option( 'conf_manager_smtp_host', '' ) ) {
			return 'database';
		}
		return 'none';
	}

	/**
	 * メール送信失敗時のハンドリング（バウンス記録）
	 *
	 * @param \WP_Error $wp_error
	 */
	public function handle_mail_failure( $wp_error ): void {
		if ( ! ( $wp_error instanceof \WP_Error ) ) {
			return;
		}

		$data = $wp_error->get_error_data();
		$to   = $data['to'] ?? [];
		if ( is_string( $to ) ) {
			$to = [ $to ];
		}

		foreach ( $to as $email ) {
			$email = is_array( $email ) ? ( $email[0] ?? '' ) : $email;
			if ( empty( $email ) || ! is_email( $email ) ) {
				continue;
			}
			$this->record_bounce( $email, $wp_error->get_error_message() );
		}
	}

	/**
	 * バウンスを記録する
	 */
	private function record_bounce( string $email, string $reason ): void {
		$bounces = get_option( 'conf_manager_mail_bounces', [] );
		if ( ! is_array( $bounces ) ) {
			$bounces = [];
		}

		$bounces[ $email ] = [
			'count'   => ( $bounces[ $email ]['count'] ?? 0 ) + 1,
			'reason'  => $reason,
			'last_at' => current_time( 'mysql' ),
		];

		// 最大500件に制限
		if ( count( $bounces ) > 500 ) {
			$bounces = array_slice( $bounces, -500, 500, true );
		}

		update_option( 'conf_manager_mail_bounces', $bounces, false );
	}

	/**
	 * 指定メールアドレスがバウンス状態かどうかを確認
	 *
	 * @param string $email
	 * @param int    $threshold バウンス回数の閾値（デフォルト: 3）
	 * @return bool
	 */
	public static function is_bounced( string $email, int $threshold = 3 ): bool {
		$bounces = get_option( 'conf_manager_mail_bounces', [] );
		return isset( $bounces[ $email ] ) && $bounces[ $email ]['count'] >= $threshold;
	}
}
