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

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


use ConfManager\Traits\Singleton;
use ConfManager\Traits\HasHooks;
use ConfManager\Members\MemberManager;
use ConfManager\Events\EventManager;
use ConfManager\Registration\RegistrationManager;
use ConfManager\Registration\GroupRegistration;
use ConfManager\Payment\PaymentManager;
use ConfManager\Payment\InvoiceGenerator;
use ConfManager\Payment\ReceiptGenerator;
use ConfManager\Security\Encryption;
use ConfManager\Security\AuditLog;

/**
 * トランザクションメール（個別トリガー型）
 *
 * 参加申込時・入金確認時に自動でメールを送信する。
 *
 * @since 1.2.0
 */
class TransactionalMailer {

	use Singleton;
	use HasHooks;

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

	protected function register_hooks(): void {
		$this->add_action( 'conf_manager_registration_created', [ $this, 'send_registration_confirmation' ], 10, 2 );
		$this->add_action( 'conf_manager_registration_created', [ $this, 'notify_admin_new_registration' ], 15, 2 );
		$this->add_action( 'conf_manager_registration_created', [ $this, 'auto_generate_invoice' ], 20, 2 );
		$this->add_action( 'conf_manager_payment_confirmed', [ $this, 'send_payment_confirmation' ], 10, 2 );
		$this->add_action( 'conf_manager_payment_confirmed', [ $this, 'auto_generate_receipt' ], 20, 2 );
		$this->add_action( 'conf_manager_group_registration_created', [ $this, 'send_group_registration_confirmation' ], 10, 3 );
		$this->add_action( 'conf_manager_group_registration_created', [ $this, 'notify_admin_new_registration' ], 15, 3 );
		$this->add_action( 'conf_manager_group_registration_created', [ $this, 'auto_generate_invoice_for_group' ], 20, 3 );
	}

	/**
	 * マイページURLを取得
	 *
	 * ⭐ **公開口。** 認証コードのメールなど、このクラスの外からも同じ URL を使う。
	 * 別に組み立てると、申込確認メールや案内文と**案内先が食い違う**。
	 */
	public function get_mypage_url(): string {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query.
		$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", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from safe internal method.
				'%[conf_manager view="mypage"]%',
				'publish',
				'page'
			)
		);
		if ( $page_id ) {
			return get_permalink( (int) $page_id );
		}
		return site_url( '/c-mypage/' );
	}

	/**
	 * メールフッターを生成
	 */
	private function build_footer( array $event ): string {
		$footer = "\n" . str_repeat( '━', 26 ) . "\n";
		if ( ! empty( $event['organizer_info'] ) ) {
			$footer .= $event['organizer_info'] . "\n\n";
		}
		$footer .= sprintf(
			/* translators: %s: site name */
			__( 'This email was automatically sent by the registration system of %s.', 'conferencemanager' ),
			get_option( 'blogname', '' )
		) . "\n";
		$footer .= __( 'If you have any questions, please contact the organizer.', 'conferencemanager' ) . "\n";
		return $footer;
	}

	/**
	 * 会場案内セクションを生成
	 */
	private function build_venue_section( array $event ): string {
		if ( empty( $event['venue'] ) && empty( $event['venue_address'] ) ) {
			return '';
		}
		$section = "\n" . __( '[Venue Information]', 'conferencemanager' ) . "\n";
		if ( ! empty( $event['venue'] ) ) {
			$section .= $event['venue'] . "\n";
		}
		if ( ! empty( $event['venue_address'] ) ) {
			$section .= $event['venue_address'] . "\n";
		}
		return $section;
	}

	/**
	 * マイページ案内セクションを生成
	 */
	private function build_mypage_section(): string {
		$section  = "\n" . __( '[My Page]', 'conferencemanager' ) . "\n";
		$section .= $this->get_mypage_url() . "\n";
		$section .= __( 'Use My Page to check your registration status and download invoices and receipts.', 'conferencemanager' ) . "\n";
		return $section;
	}

	/**
	 * 開催日の表示文字列を生成（終了日がある場合は「〜」でつなぐ）
	 *
	 * DB値は日時だがメールでは日付のみ表示する。
	 * 日付部分のみ切り出してからフォーマットすることで
	 * タイムゾーン補正による日付ズレを防ぐ。
	 */
	private function format_event_dates( array $event ): string {
		$start_date = substr( $event['date_start'], 0, 10 );
		$start = wp_date( 'Y年n月j日', strtotime( $start_date ) );

		if ( ! empty( $event['date_end'] ) ) {
			$end_date = substr( $event['date_end'], 0, 10 );
			if ( $end_date !== $start_date ) {
				$end = wp_date( 'Y年n月j日', strtotime( $end_date ) );
				return $start . ' 〜 ' . $end;
			}
		}
		return $start;
	}

	/**
	 * 参加申込確認メールを送信
	 *
	 * @param int $registration_id 申込ID
	 * @param int $payment_id      決済ID（0の場合あり）
	 */
	public function send_registration_confirmation( int $registration_id, int $payment_id ): void {
		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$member = MemberManager::instance()->get( (int) $registration['member_id'] );
		if ( null === $member ) {
			return;
		}

		$event = EventManager::instance()->get( (int) $registration['event_id'] );
		if ( null === $event ) {
			return;
		}

		$email = $member['email'];
		if ( empty( $email ) || ! is_email( $email ) ) {
			return;
		}

		$name   = $member['name_sei'] . ' ' . $member['name_mei'];
		$amount = (int) $registration['amount'];
		$tax    = (int) $registration['tax'];
		$total  = $amount + $tax;

		$subject = sprintf(
			/* translators: %s: event title */
			__( '[%s] Registration Received', 'conferencemanager' ),
			$event['title']
		);

		$participation_label = 'online' === $registration['participation_type']
			? __( 'Online', 'conferencemanager' )
			: __( 'On-site', 'conferencemanager' );

		$body = sprintf(
			/* translators: %s: member full name */
			__( 'Dear %s,', 'conferencemanager' ),
			$name
		) . "\n\n";

		$body .= __( 'Your registration for the following event has been received.', 'conferencemanager' ) . "\n\n";

		/* translators: %s: event title */
		$body .= sprintf( __( 'Event: %s', 'conferencemanager' ), $event['title'] ) . "\n";
		/* translators: %s: event date(s) */
		$body .= sprintf( __( 'Date: %s', 'conferencemanager' ), $this->format_event_dates( $event ) ) . "\n";
		/* translators: %s: registration number */
		$body .= sprintf( __( 'Registration No.: %s', 'conferencemanager' ), $registration['registration_no'] ) . "\n";
		/* translators: %s: participation type label */
		$body .= sprintf( __( 'Participation type: %s', 'conferencemanager' ), $participation_label ) . "\n";

		if ( $tax > 0 ) {
			/* translators: %s: amount excluding tax */
			$body .= sprintf( __( 'Amount (excl. tax): ¥%s', 'conferencemanager' ), number_format( $amount ) ) . "\n";
			/* translators: %s: tax amount */
			$body .= sprintf( __( 'Tax: ¥%s', 'conferencemanager' ), number_format( $tax ) ) . "\n";
			/* translators: %s: total amount */
			$body .= sprintf( __( 'Total: ¥%s', 'conferencemanager' ), number_format( $total ) ) . "\n";
		} else {
			/* translators: %s: payment amount */
			$body .= sprintf( __( 'Amount: ¥%s', 'conferencemanager' ), number_format( $amount ) ) . "\n";
		}

		// 会場案内（会場参加の場合のみ）
		if ( 'onsite' === ( $registration['participation_type'] ?? 'onsite' ) ) {
			$body .= $this->build_venue_section( $event );
		}

		// 今後のお手続き
		$payment_method = $registration['payment_method'] ?? 'bank';
		if ( $total > 0 ) {
			// ⚠️ v1.4.0 でカード決済（Stripe）は Pro 版の機能になったので分岐を落とした。
			// あわせて「項目が 1 つも無いのに見出しだけ出る」を避けるため、
			// 中身を組み立ててから見出しを付ける。
			$steps = '';
			if ( 'bank' === $payment_method ) {
				$steps .= __( '1. Please transfer the payment to the account below.', 'conferencemanager' ) . "\n";
				$steps .= __( '2. A confirmation email will be sent after payment is verified.', 'conferencemanager' ) . "\n";
				$steps .= __( '3. You can download invoices and receipts from My Page.', 'conferencemanager' ) . "\n";
			}
			if ( '' !== $steps ) {
				$body .= "\n" . __( '[Next Steps]', 'conferencemanager' ) . "\n" . $steps;
			}
		} else {
			$body .= "\n" . __( '[Next Steps]', 'conferencemanager' ) . "\n";
			$body .= __( 'This is a free event. Your registration is complete.', 'conferencemanager' ) . "\n";
			$body .= __( 'We look forward to seeing you at the event.', 'conferencemanager' ) . "\n";
		}

		// 前売料金適用期限
		if ( ! empty( $event['advance_fee_deadline'] ) ) {
			$deadline_ts = strtotime( $event['advance_fee_deadline'] );
			if ( $deadline_ts && $deadline_ts > time() ) {
				$deadline_date = substr( $event['advance_fee_deadline'], 0, 10 );
				$body .= "\n" . sprintf(
					/* translators: %s: deadline date */
					__( '* Advance ticket deadline: %s', 'conferencemanager' ),
					wp_date( 'Y年n月j日', strtotime( $deadline_date ) )
				) . "\n";
			}
		}

		// 振込先情報（無料＝合計0円のときは表示しない）
		if ( $total > 0 && 'bank' === $payment_method && ( ! empty( $event['payment_account_name'] ) || ! empty( $event['payment_account_info'] ) ) ) {
			$body .= "\n" . __( '[Bank Transfer Details]', 'conferencemanager' ) . "\n";
			if ( ! empty( $event['payment_account_name'] ) ) {
				$body .= $event['payment_account_name'] . "\n";
			}
			if ( ! empty( $event['payment_account_info'] ) ) {
				$body .= $event['payment_account_info'] . "\n";
			}
		}

		// マイページ案内
		$body .= $this->build_mypage_section();

		// フッター
		$body .= $this->build_footer( $event );

		wp_mail( $email, $subject, $body );

		AuditLog::instance()->log( 'send_registration_email', 'registration', $registration_id, [
			'email_hash' => $member['email_hash'],
		] );
	}

	/**
	 * 入金確認メールを送信
	 *
	 * @param int $payment_id      決済ID
	 * @param int $registration_id 申込ID
	 */
	public function send_payment_confirmation( int $payment_id, int $registration_id ): void {
		$payment = PaymentManager::instance()->get( $payment_id );
		if ( null === $payment ) {
			return;
		}

		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$member = MemberManager::instance()->get( (int) $registration['member_id'] );
		if ( null === $member ) {
			return;
		}

		$event = EventManager::instance()->get( (int) $registration['event_id'] );
		if ( null === $event ) {
			return;
		}

		$email = $member['email'];
		if ( empty( $email ) || ! is_email( $email ) ) {
			return;
		}

		$name  = $member['name_sei'] . ' ' . $member['name_mei'];
		$total = (int) $payment['amount'] + (int) $payment['tax'];

		$subject = sprintf(
			/* translators: %s: event title */
			__( '[%s] Payment Confirmed', 'conferencemanager' ),
			$event['title']
		);

		$body = sprintf(
			/* translators: %s: member full name */
			__( 'Dear %s,', 'conferencemanager' ),
			$name
		) . "\n\n";

		$body .= __( 'Your payment has been confirmed. Thank you.', 'conferencemanager' ) . "\n\n";

		/* translators: %s: event title */
		$body .= sprintf( __( 'Event: %s', 'conferencemanager' ), $event['title'] ) . "\n";
		/* translators: %s: event date(s) */
		$body .= sprintf( __( 'Date: %s', 'conferencemanager' ), $this->format_event_dates( $event ) ) . "\n";
		/* translators: %s: registration number */
		$body .= sprintf( __( 'Registration No.: %s', 'conferencemanager' ), $registration['registration_no'] ) . "\n";
		// 実際に記録された入金額を優先する（未記録の旧データは請求額どおり入金されたものとして扱う）
		$paid = ( null === ( $payment['paid_amount'] ?? null ) ) ? $total : (int) $payment['paid_amount'];

		/* translators: %s: billed amount */
		$body .= sprintf( __( 'Amount billed: ¥%s', 'conferencemanager' ), number_format( $total ) ) . "\n";
		/* translators: %s: amount actually received */
		$body .= sprintf( __( 'Amount paid: ¥%s', 'conferencemanager' ), number_format( $paid ) ) . "\n";

		if ( ! empty( $payment['paid_at'] ) ) {
			/* translators: %s: payment date */
			$body .= sprintf( __( 'Payment date: %s', 'conferencemanager' ), wp_date( 'Y年n月j日', strtotime( $payment['paid_at'] ) ) ) . "\n";
		}

		// 会場案内（会場参加の場合のみ）
		if ( 'onsite' === ( $registration['participation_type'] ?? 'onsite' ) ) {
			$body .= $this->build_venue_section( $event );
		}

		// 今後のご案内
		$body .= "\n" . __( '[Further Information]', 'conferencemanager' ) . "\n";
		$body .= __( 'You can download receipts from My Page.', 'conferencemanager' ) . "\n";
		$body .= __( 'Details about the event day will be sent by separate email.', 'conferencemanager' ) . "\n";

		// マイページ案内
		$body .= $this->build_mypage_section();

		// フッター
		$body .= $this->build_footer( $event );

		wp_mail( $email, $subject, $body );

		AuditLog::instance()->log( 'send_payment_email', 'payment', $payment_id, [
			'email_hash' => $member['email_hash'],
		] );
	}

	/**
	 * 団体申込確認メールを送信（代表者＋スタッフ）
	 *
	 * @param int   $registration_id 代表者の申込ID
	 * @param int   $payment_id      決済ID
	 * @param array $staff_ids       スタッフID配列
	 */
	public function send_group_registration_confirmation( int $registration_id, int $payment_id, array $staff_ids ): void {
		// 代表者に通常の申込確認メールを送信
		$this->send_registration_confirmation( $registration_id, $payment_id );

		// イベント情報を取得
		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$event = EventManager::instance()->get( (int) $registration['event_id'] );
		if ( null === $event ) {
			return;
		}

		$representative = MemberManager::instance()->get( (int) $registration['member_id'] );
		$rep_name       = $representative ? $representative['name_sei'] . ' ' . $representative['name_mei'] : '';

		// 各スタッフに個別メール送信
		$staff_list = GroupRegistration::instance()->get_staff_list( $registration_id );
		$enc        = Encryption::instance();

		foreach ( $staff_list as $staff ) {
			$staff_email = '';
			if ( ! empty( $staff['email'] ) ) {
				// 暗号化されている場合は復号、平文の場合はそのまま使用
				if ( is_email( $staff['email'] ) ) {
					$staff_email = $staff['email'];
				} else {
					try {
						$staff_email = $enc->decrypt( $staff['email'] );
					} catch ( \RuntimeException $e ) {
						continue;
					}
				}
			}

			if ( empty( $staff_email ) || ! is_email( $staff_email ) ) {
				continue;
			}

			$staff_name = $staff['name'] ?? '';

			$subject = sprintf(
				/* translators: %s: event title */
				__( '[%s] Registration Notification', 'conferencemanager' ),
				$event['title']
			);

			$staff_participation = $staff['participation_type'] ?? 'onsite';
			$staff_participation_label = 'online' === $staff_participation
				? __( 'Online', 'conferencemanager' )
				: __( 'On-site', 'conferencemanager' );

			/* translators: %s: staff member name */
			$body = sprintf( __( 'Dear %s,', 'conferencemanager' ), $staff_name ) . "\n\n";
			$body .= sprintf(
				/* translators: %s: representative member name */
				__( 'You have been registered for the following event by %s.', 'conferencemanager' ),
				$rep_name
			) . "\n\n";
			/* translators: %s: event title */
			$body .= sprintf( __( 'Event: %s', 'conferencemanager' ), $event['title'] ) . "\n";
			/* translators: %s: event date(s) */
			$body .= sprintf( __( 'Date: %s', 'conferencemanager' ), $this->format_event_dates( $event ) ) . "\n";
			/* translators: %s: participation type label */
			$body .= sprintf( __( 'Participation type: %s', 'conferencemanager' ), $staff_participation_label ) . "\n";
			/* translators: %s: group registration number */
			$body .= sprintf( __( 'Group Registration No.: %s', 'conferencemanager' ), $registration['registration_no'] ) . "\n";

			// 会場案内（会場参加の場合のみ）
			if ( 'onsite' === $staff_participation ) {
				$body .= $this->build_venue_section( $event );
			}

			// 今後のご案内
			$body .= "\n" . __( '[Further Information]', 'conferencemanager' ) . "\n";
			$body .= sprintf(
				/* translators: %s: representative name */
				__( 'For inquiries about participation, please contact the group representative (%s).', 'conferencemanager' ),
				$rep_name
			) . "\n";
			$body .= __( 'You can check your participation status from My Page.', 'conferencemanager' ) . "\n";

			// マイページ案内
			$body .= $this->build_mypage_section();

			// フッター
			$body .= $this->build_footer( $event );

			wp_mail( $staff_email, $subject, $body );
		}

		AuditLog::instance()->log( 'send_group_registration_email', 'registration', $registration_id, [
			'staff_count' => count( $staff_ids ),
		] );
	}

	/**
	 * 新規申込時に管理者へ通知メールを送信
	 *
	 * @param int       $registration_id 申込ID
	 * @param int|array $second_param    決済ID or スタッフID配列（団体申込の場合）
	 */
	public function notify_admin_new_registration( int $registration_id, int|array $second_param = 0 ): void {
		if ( ! get_option( 'conf_manager_admin_notify_registration', true ) ) {
			return;
		}

		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$member = MemberManager::instance()->get( (int) $registration['member_id'] );
		$event  = EventManager::instance()->get( (int) $registration['event_id'] );

		$name  = $member ? $member['name_sei'] . ' ' . $member['name_mei'] : __( 'Unknown', 'conferencemanager' );
		$total = (int) $registration['amount'] + (int) $registration['tax'];

		$is_group  = is_array( $second_param );
		$staff_cnt = $is_group ? count( $second_param ) : 0;

		$subject = sprintf(
			/* translators: 1: event title, 2: member name */
			__( '[%1$s] New Registration: %2$s', 'conferencemanager' ),
			$event ? $event['title'] : '',
			$name
		);

		$body  = __( 'A new registration has been received.', 'conferencemanager' ) . "\n\n";
		/* translators: %s: event title */
		$body .= sprintf( __( 'Event: %s', 'conferencemanager' ), $event ? $event['title'] : '' ) . "\n";
		/* translators: %s: member full name */
		$body .= sprintf( __( 'Name: %s', 'conferencemanager' ), $name ) . "\n";
		/* translators: %s: registration number */
		$body .= sprintf( __( 'Registration No.: %s', 'conferencemanager' ), $registration['registration_no'] ) . "\n";
		/* translators: %s: payment amount */
		$body .= sprintf( __( 'Amount: ¥%s', 'conferencemanager' ), number_format( $total ) ) . "\n";

		if ( $is_group && $staff_cnt > 0 ) {
			/* translators: %d: number of staff members */
			$body .= sprintf( __( 'Group registration (%d staff members)', 'conferencemanager' ), $staff_cnt ) . "\n";
		}

		// PII: do NOT include email / phone / address in the notification body.
		// Personal contact details must be viewed only from the authenticated admin screen.
		$admin_url = esc_url_raw( admin_url( 'admin.php?page=conferencemanager#/registrations' ) );
		$body     .= "\n";
		/* translators: %s: admin dashboard URL */
		$body .= sprintf( __( 'View details in the admin dashboard: %s', 'conferencemanager' ), $admin_url ) . "\n";

		wp_mail( $this->resolve_admin_recipients(), $subject, $body );
	}

	/**
	 * 管理者通知の宛先を解決する。
	 *
	 * conf_manager_notification_email（カンマ区切り）の有効アドレスを優先し、
	 * 1件も無ければ WordPress の admin_email にフォールバックする。
	 *
	 * @return string[] 宛先メールアドレスの配列
	 */
	private function resolve_admin_recipients(): array {
		$configured = (string) get_option( 'conf_manager_notification_email', '' );
		$recipients = [];
		foreach ( explode( ',', $configured ) as $candidate ) {
			$candidate = trim( $candidate );
			if ( '' !== $candidate && is_email( $candidate ) ) {
				$recipients[] = $candidate;
			}
		}

		if ( empty( $recipients ) ) {
			$recipients[] = get_option( 'admin_email' );
		}

		return array_values( array_unique( $recipients ) );
	}

	/**
	 * 申込作成時に請求書を自動生成
	 */
	public function auto_generate_invoice( int $registration_id, int $payment_id ): void {
		if ( 0 === $payment_id ) {
			return;
		}

		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$member = MemberManager::instance()->get( (int) $registration['member_id'] );
		if ( null === $member ) {
			return;
		}

		$inv = InvoiceGenerator::instance();

		// 既に存在する場合はスキップ
		if ( null !== $inv->get_by_payment( $payment_id ) ) {
			return;
		}

		// ⭐ 戻り値は見ない。作れなかったときは呼び出し元が find_document() で気づく。
		//    PDF はディスクに書かず、必要になった時点で DB から描画する。
		$inv->create( [
			'payment_id'          => $payment_id,
			'addressee_name'      => $member['name_sei'] . ' ' . $member['name_mei'],
			'addressee_dept'      => $member['org_name'] ?? '',
			'honorific'           => '様',
			'tax_registration_no' => (string) get_option( 'conf_manager_tax_registration_no', '' ),
		] );
	}

	/**
	 * 団体申込作成時に請求書を自動生成
	 */
	public function auto_generate_invoice_for_group( int $registration_id, int $payment_id, array $staff_ids ): void {
		$this->auto_generate_invoice( $registration_id, $payment_id );
	}

	/**
	 * 入金確認時に領収書を自動生成
	 */
	public function auto_generate_receipt( int $payment_id, int $registration_id ): void {
		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration ) {
			return;
		}

		$member = MemberManager::instance()->get( (int) $registration['member_id'] );
		if ( null === $member ) {
			return;
		}

		$event = EventManager::instance()->get( (int) $registration['event_id'] );

		$rcpt = ReceiptGenerator::instance();

		// 既に存在する場合はスキップ
		if ( null !== $rcpt->get_by_payment( $payment_id ) ) {
			return;
		}

		$proviso = $event ? $event['title'] . ' 参加費として' : '参加費として';

		// ⭐ 戻り値は見ない（上と同じ理由）。
		$rcpt->create( [
			'payment_id'          => $payment_id,
			'addressee_name'      => $member['name_sei'] . ' ' . $member['name_mei'],
			'addressee_dept'      => $member['org_name'] ?? '',
			'proviso'             => $proviso,
			'tax_registration_no' => (string) get_option( 'conf_manager_tax_registration_no', '' ),
		] );
	}
}
