<?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' ),
			],
		] );

		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;
			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'] );

		return new \WP_REST_Response( [
			'registration_no'    => $reg['registration_no'],
			'member_name'        => $member ? $member['name_sei'] . ' ' . $member['name_mei'] : '',
			'member_org'         => $member['org_name'] ?? '',
			'participation_type' => $reg['participation_type'],
			'status'             => 'attended',
			'already_attended'   => $already_attended,
		], 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' ) );

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

		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,
				'label' => 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'] );

		$has_completed_payment = false;
		foreach ( \ConfManager\Payment\PaymentManager::instance()->get_by_registration( $id ) as $payment ) {
			if ( 'completed' === $payment['status'] ) {
				$has_completed_payment = true;
				break;
			}
		}

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

		return new \WP_REST_Response( [
			'current'             => [
				'fee_type'           => $registration['fee_type'],
				'fee_type_label'     => $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 );
	}
}
