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

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


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

/**
 * 参加申込 REST API
 *
 * @since 1.0.0
 */
class RegistrationApi {

	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, '/registrations', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_items' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_view_registrations' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/status', [
			[
				'methods'             => \WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'update_status' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );

		// 参加区分の修正（fee_type / participation_type）
		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/fee-options', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_fee_options' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_view_registrations' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/fee-classification', [
			[
				'methods'             => \WP_REST_Server::EDITABLE,
				'callback'            => [ $this, 'update_fee_classification' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );

		// 帳票の発行（PDF を base64 で返す）
		//
		// 🔴 メソッドは POST（帳票行が無ければ発番して作る＝状態を変える）。cap も edit。
		//    申込に決済が複数ぶら下がるときは 409 で断り、決済管理から選ばせる。
		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/invoice-pdf', [
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'issue_invoice_pdf' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/receipt-pdf', [
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'issue_receipt_pdf' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/registrations/check-in', [
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'check_in' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_checkin' ),
			],
		] );

		// 団体スタッフ管理
		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/staff', [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_staff' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_view_registrations' ),
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'add_staff' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );

		register_rest_route( self::NAMESPACE, '/registrations/(?P<id>[\d]+)/staff/(?P<staff_id>[\d]+)', [
			[
				'methods'             => \WP_REST_Server::DELETABLE,
				'callback'            => [ $this, 'cancel_staff' ],
				'permission_callback' => fn() => current_user_can( 'conf_manager_edit_registrations' ),
			],
		] );
	}

	public function get_items( \WP_REST_Request $request ): \WP_REST_Response {
		$result = RegistrationManager::instance()->search( [
			'event_id'           => $request->get_param( 'event_id' ),
			'status'             => $request->get_param( 'status' ),
			'participation_type' => $request->get_param( 'participation_type' ),
			'keyword'            => $request->get_param( 'keyword' ),
			'per_page'           => $request->get_param( 'per_page' ) ?? 20,
			'page'               => $request->get_param( 'page' ) ?? 1,
		] );

		// 会員名・イベント名を一括付与（N+1回避）
		$member_ids = array_unique( array_filter( array_map( fn( $r ) => (int) $r['member_id'], $result['items'] ) ) );
		$event_ids  = array_unique( array_filter( array_map( fn( $r ) => (int) $r['event_id'], $result['items'] ) ) );

		$member_map = [];
		foreach ( $member_ids as $mid ) {
			$m = \ConfManager\Members\MemberManager::instance()->get( $mid );
			if ( $m ) {
				$member_map[ $mid ] = $m;
			}
		}

		$event_map = [];
		foreach ( $event_ids as $eid ) {
			$e = \ConfManager\Events\EventManager::instance()->get( $eid );
			if ( $e ) {
				$event_map[ $eid ] = $e['title'];
			}
		}

		// 団体申込のスタッフ数を一括取得（N+1回避）
		$group_reg_ids = array_filter( array_map( fn( $r ) => ! empty( $r['group_id'] ) && $r['is_representative'] ? (int) $r['id'] : null, $result['items'] ) );
		$staff_count_map = [];
		if ( ! empty( $group_reg_ids ) ) {
			$group_reg = GroupRegistration::instance();
			foreach ( $group_reg_ids as $rid ) {
				$staff_count_map[ $rid ] = $group_reg->count_staff( $rid );
			}
		}

		$items = array_map( function ( $reg ) use ( $member_map, $event_map, $staff_count_map ) {
			$member = $member_map[ (int) $reg['member_id'] ] ?? null;
			$reg['member_name'] = $member ? $member['name_sei'] . ' ' . $member['name_mei'] : '';
			$reg['member_org']  = $member['org_name'] ?? '';
			$reg['event_title'] = $event_map[ (int) $reg['event_id'] ] ?? '';
			$reg['staff_count'] = $staff_count_map[ (int) $reg['id'] ] ?? 0;

			// 精算（要返金／要追加徴収）の状態
			$settlement               = \ConfManager\Payment\PaymentManager::instance()->get_registration_settlement( (int) $reg['id'] );
			$reg['settlement_state']  = $settlement['state'];
			$reg['settlement_amount'] = $settlement['amount'];

			// paid_at は GMT 保存。画面が現地日付を出せるよう別キーで添える
			// （GMT を直接出すと日付だけ見たとき最大 9 時間ずれて別の日に見える）。
			$reg['paid_at_local'] = \ConfManager\Support\LocalTime::from_gmt( $reg['paid_at'] ?? null );

			// 🔴 DB の焼き付け（fee_type_label）は書き換えず、**返す直前**に装飾する。
			// 設定より前に作られた申込も、キーが標準 5 区分ならこれで同じ表記になる。
			//
			// ⚠️ **二重に前置しないための取り決め**:
			//    サーバから来る値（fee_type_label）は**サーバ側でだけ**装飾する。
			//    JSX 側の装飾（src/common/member-type-label.js）は
			//    **JS の中で作ったラベル**（MEMBER_TYPES / MEMBER_TYPE_LABELS）にだけ通すこと。
			$reg['fee_type_label'] = \ConfManager\Members\MemberTypeLabel::decorate_registration(
				(string) ( $reg['fee_type'] ?? '' ),
				(string) ( $reg['fee_type_label'] ?? '' )
			);

			return $reg;
		}, $result['items'] );

		$response = new \WP_REST_Response( $items, 200 );
		$response->header( 'X-WP-Total', (string) $result['total'] );
		$response->header( 'X-WP-TotalPages', (string) max( 1, (int) ceil( $result['total'] / $result['per_page'] ) ) );

		return $response;
	}

	/**
	 * QRコードチェックイン処理
	 */
	public function check_in( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$registration_no = sanitize_text_field( $request->get_param( 'registration_no' ) ?? '' );
		$event_id        = $request->get_param( 'event_id' );

		if ( empty( $registration_no ) ) {
			return new \WP_Error( 'missing_param', __( 'Registration number is required.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		$reg = RegistrationManager::instance()->get_by_no( $registration_no );
		if ( null === $reg ) {
			return new \WP_Error( 'not_found', __( 'No registration found for this registration number.', 'conferencemanager' ), [ 'status' => 404 ] );
		}

		if ( $event_id && (int) $reg['event_id'] !== (int) $event_id ) {
			return new \WP_Error( 'event_mismatch', __( 'This QR code does not belong to a participant of the selected event.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		if ( 'cancelled' === $reg['status'] ) {
			return new \WP_Error( 'cancelled', __( 'This registration has been cancelled.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		$already_attended = 'attended' === $reg['status'];

		if ( ! $already_attended ) {
			RegistrationManager::instance()->update_status( (int) $reg['id'], 'attended' );

			\ConfManager\Security\AuditLog::instance()->log( 'checkin', 'registration', (int) $reg['id'], [
				'registration_no' => $registration_no,
			] );
		}

		$member = \ConfManager\Members\MemberManager::instance()->get( (int) $reg['member_id'] );

		// 受付で「返金が必要な参加者」と分かるよう精算状態を返す
		$settlement = \ConfManager\Payment\PaymentManager::instance()->get_registration_settlement( (int) $reg['id'] );

		return new \WP_REST_Response( [
			'registration_id'    => (int) $reg['id'],
			'registration_no'    => $reg['registration_no'],
			'member_name'        => $member ? $member['name_sei'] . ' ' . $member['name_mei'] : '',
			'member_org'         => $member['org_name'] ?? '',
			'participation_type' => $reg['participation_type'],
			'fee_type_label'     => \ConfManager\Members\MemberTypeLabel::decorate_registration(
				(string) ( $reg['fee_type'] ?? '' ),
				(string) ( $reg['fee_type_label'] ?? '' )
			),
			'status'             => 'attended',
			'already_attended'   => $already_attended,
			'settlement_state'   => $settlement['state'],
			'settlement_amount'  => $settlement['amount'],
		], 200 );
	}

	public function update_status( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$id     = (int) $request['id'];
		$status = sanitize_text_field( $request->get_param( 'status' ) );
		// 入金日（現地日付 Y-m-d）。未指定なら従来どおり「いま」を記録する。
		$paid_at = sanitize_text_field( $request->get_param( 'paid_at' ) ?? '' );

		$result = RegistrationManager::instance()->update_status( $id, $status, true, $paid_at );

		if ( is_wp_error( $result ) ) {
			$result->add_data( [ 'status' => 400 ] );
			return $result;
		}

		return new \WP_REST_Response( RegistrationManager::instance()->get( $id ), 200 );
	}

	/**
	 * GET /registrations/{id}/fee-options — 参加区分修正モーダル用の選択肢
	 */
	public function get_fee_options( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$id = (int) $request['id'];

		$registration = RegistrationManager::instance()->get( $id );
		if ( null === $registration ) {
			return new \WP_Error( 'not_found', __( 'Registration not found.', 'conferencemanager' ), [ 'status' => 404 ] );
		}

		$event = \ConfManager\Events\EventManager::instance()->get( (int) $registration['event_id'] );
		if ( null === $event ) {
			return new \WP_Error( 'not_found', __( 'Event not found.', 'conferencemanager' ), [ 'status' => 404 ] );
		}

		$resolved = \ConfManager\Events\EventManager::instance()->resolve_fee_rules( $event );

		$fee_types        = [];
		$fee_matrix       = [];
		$participation_on = [];
		foreach ( $resolved as $key => $prices ) {
			if ( str_starts_with( (string) $key, '_' ) || ! is_array( $prices ) ) {
				continue;
			}
			$fee_types[]        = [
				'key'   => $key,
				// 表示の直前で装飾する。**値（key）は変えない。**
				'label' => \ConfManager\Members\MemberTypeLabel::decorate(
					(string) $key,
					RegistrationManager::resolve_fee_type_label( (string) $key, $event )
				),
			];
			$fee_matrix[ $key ] = [
				'onsite' => (int) ( $prices['onsite'] ?? 0 ),
				'online' => (int) ( $prices['online'] ?? 0 ),
			];
			foreach ( [ 'onsite', 'online' ] as $pt ) {
				if ( isset( $prices[ $pt ] ) ) {
					$participation_on[ $pt ] = true;
				}
			}
		}

		$is_group = ! empty( $registration['group_id'] ) || ! empty( $registration['is_representative'] );

		// 返金処理済みの決済がある場合は会計記録が確定しているため訂正不可。
		// 入金済（paid）は精算（返金／追加徴収）を前提に訂正を許可する。
		$has_refunded_payment = false;
		foreach ( \ConfManager\Payment\PaymentManager::instance()->get_by_registration( $id ) as $payment ) {
			if ( in_array( $payment['status'], [ 'refunded', 'partial_refund' ], true ) ) {
				$has_refunded_payment = true;
				break;
			}
		}

		$editable = ! $is_group
			&& in_array( $registration['status'], [ 'pending', 'confirmed', 'paid' ], true )
			&& ! $has_refunded_payment;

		return new \WP_REST_Response( [
			'current'             => [
				'fee_type'           => $registration['fee_type'],
				// 🔴 DB の焼き付けは書き換えず、表示用に装飾したものを返す。
				'fee_type_label'     => \ConfManager\Members\MemberTypeLabel::decorate_registration(
					(string) $registration['fee_type'],
					(string) $registration['fee_type_label']
				),
				'participation_type' => $registration['participation_type'],
				'amount'             => (int) $registration['amount'],
				'tax'                => (int) $registration['tax'],
				'status'             => $registration['status'],
			],
			'fee_types'           => $fee_types,
			'participation_types' => array_keys( $participation_on ),
			'fee_matrix'          => $fee_matrix,
			'tax_rate'            => (float) ( $event['tax_rate'] ?? 10.0 ),
			'is_group'            => $is_group,
			'editable'            => $editable,
		], 200 );
	}

	/**
	 * PUT /registrations/{id}/fee-classification — 参加区分の修正
	 */
	public function update_fee_classification( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$id                 = (int) $request['id'];
		$fee_type           = sanitize_text_field( $request->get_param( 'fee_type' ) ?? '' );
		$participation_type = sanitize_text_field( $request->get_param( 'participation_type' ) ?? '' );

		if ( '' === $fee_type || '' === $participation_type ) {
			return new \WP_Error( 'missing_param', __( 'Fee classification and participation type are required.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		$result = RegistrationManager::instance()->update_fee_classification( $id, $fee_type, $participation_type );

		if ( is_wp_error( $result ) ) {
			$result->add_data( [ 'status' => 400 ] );
			return $result;
		}

		return new \WP_REST_Response( $result, 200 );
	}

	/**
	 * GET /registrations/{id}/staff — 団体スタッフ一覧
	 */
	public function get_staff( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$registration_id = (int) $request['id'];

		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration || empty( $registration['group_id'] ) ) {
			return new \WP_Error( 'not_group', __( 'This is not a group registration.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		$staff_list = GroupRegistration::instance()->get_staff_list( $registration_id );

		return new \WP_REST_Response( $staff_list, 200 );
	}

	/**
	 * POST /registrations/{id}/staff — スタッフ追加
	 */
	public function add_staff( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$registration_id = (int) $request['id'];

		$registration = RegistrationManager::instance()->get( $registration_id );
		if ( null === $registration || empty( $registration['group_id'] ) ) {
			return new \WP_Error( 'not_group', __( 'This is not a group registration.', 'conferencemanager' ), [ 'status' => 400 ] );
		}

		$staff_data = [
			'name'               => sanitize_text_field( $request->get_param( 'name' ) ?? '' ),
			'email'              => sanitize_email( $request->get_param( 'email' ) ?? '' ),
			'fee_type'           => sanitize_text_field( $request->get_param( 'fee_type' ) ?? '' ),
			'participation_type' => sanitize_text_field( $request->get_param( 'participation_type' ) ?? 'onsite' ),
			'session_options'    => $request->get_param( 'session_options' ) ?? [],
		];

		$member_id = $request->get_param( 'member_id' );
		if ( ! empty( $member_id ) ) {
			$staff_data['member_id'] = absint( $member_id );
		}

		$group = GroupRegistration::instance();
		$staff_id = $group->add_staff( $registration_id, $staff_data );

		if ( is_wp_error( $staff_id ) ) {
			$staff_id->add_data( [ 'status' => 400 ] );
			return $staff_id;
		}

		// 料金再計算
		$group->recalculate_group_amount( $registration_id );

		$staff = $group->get_staff( $staff_id );

		return new \WP_REST_Response( $staff, 201 );
	}

	/**
	 * DELETE /registrations/{id}/staff/{staff_id} — スタッフ削除（キャンセル）
	 */
	public function cancel_staff( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$registration_id = (int) $request['id'];
		$staff_id        = (int) $request['staff_id'];

		$group  = GroupRegistration::instance();
		$result = $group->cancel_staff( $staff_id );

		if ( is_wp_error( $result ) ) {
			$result->add_data( [ 'status' => 400 ] );
			return $result;
		}

		// 料金再計算
		$recalc = $group->recalculate_group_amount( $registration_id );

		return new \WP_REST_Response( [
			'success' => true,
			'amount'  => $recalc['amount'],
			'tax'     => $recalc['tax'],
			'diff'    => $recalc['diff'],
		], 200 );
	}

	/**
	 * POST /registrations/{id}/invoice-pdf — 請求書を発行して PDF を返す
	 */
	public function issue_invoice_pdf( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		return $this->issue_document( (int) $request['id'], \ConfManager\Payment\PaymentDocuments::TYPE_INVOICE );
	}

	/**
	 * POST /registrations/{id}/receipt-pdf — 領収書を発行して PDF を返す
	 */
	public function issue_receipt_pdf( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		return $this->issue_document( (int) $request['id'], \ConfManager\Payment\PaymentDocuments::TYPE_RECEIPT );
	}

	/**
	 * 帳票の発行を 1 箇所に寄せる
	 *
	 * @param int    $registration_id 申込ID
	 * @param string $type            帳票種別
	 */
	private function issue_document( int $registration_id, string $type ): \WP_REST_Response|\WP_Error {
		$result = \ConfManager\Payment\PaymentDocuments::instance()->issue_for_registration( $registration_id, $type );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return new \WP_REST_Response( $result, 200 );
	}
}
