<?php

declare( strict_types=1 );

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

/**
 * Auth routes: OTP send/verify, JWT-based login.
 *
 * POST /auth/send-otp    — generate and deliver OTP
 * POST /auth/verify-otp  — verify OTP and return JWT
 */
class Playard_API_Auth {

	private Playard_Customer $customer;

	public function __construct( Playard_Customer $customer ) {
		$this->customer = $customer;
	}

	public function register_routes(): void {
		$namespace = 'playard/v1';

		register_rest_route( $namespace, '/auth/send-otp', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'send_otp' ],
			// Public: guest customers must be able to request OTP without auth.
			'permission_callback' => '__return_true',
			'args'                => [
				'phone' => [
					'required'          => true,
					'type'              => 'string',
					'sanitize_callback' => 'sanitize_text_field',
				],
			],
		] );

		register_rest_route( $namespace, '/auth/login', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'login' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'login'    => [ 'required' => true,  'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'password' => [ 'required' => true,  'type' => 'string' ],
				'type'     => [ 'required' => false, 'type' => 'string', 'default' => 'phone',
				                'enum'     => [ 'phone', 'email' ] ],
			],
		] );

		register_rest_route( $namespace, '/auth/verify-otp', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'verify_otp' ],
			// Public: OTP verification is the auth mechanism itself.
			'permission_callback' => '__return_true',
			'args'                => [
				'phone'   => [ 'required' => true,  'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'otp'     => [ 'required' => true,  'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'purpose' => [ 'required' => false, 'type' => 'string', 'default' => 'login',
				               'enum'     => [ 'login', 'reset' ] ],
			],
		] );

		register_rest_route( $namespace, '/auth/reset-password', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'reset_password' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'phone'        => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'reset_token'  => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'new_password' => [ 'required' => true, 'type' => 'string' ],
			],
		] );

		register_rest_route( $namespace, '/auth/refresh-token', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'refresh_token' ],
			'permission_callback' => '__return_true',
		] );

		// ── Account registration ─────────────────────────────────────────────────

		register_rest_route( $namespace, '/auth/register', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'register' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'name'  => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'email' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
				'phone' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
			],
		] );

		// ── Email OTP login ───────────────────────────────────────────────────────

		register_rest_route( $namespace, '/auth/send-login-otp', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'send_login_otp' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'email' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
			],
		] );

		register_rest_route( $namespace, '/auth/verify-login-otp', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'verify_login_otp' ],
			// Public: login OTP verification grants the JWT token.
			'permission_callback' => '__return_true',
			'args'                => [
				'email' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
				'otp'   => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
			],
		] );

		// ── Email-based forgot password ───────────────────────────────────────────

		register_rest_route( $namespace, '/auth/forgot-password', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'forgot_password' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'email' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
			],
		] );

		register_rest_route( $namespace, '/auth/verify-forgot-password', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'verify_forgot_password' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'email' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
				'otp'   => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
			],
		] );

		register_rest_route( $namespace, '/auth/reset-password-email', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'reset_password_email' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'email'        => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email' ],
				'reset_token'  => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
				'new_password' => [ 'required' => true, 'type' => 'string' ],
			],
		] );
	}

	// ── Account registration ─────────────────────────────────────────────────────

	public function register( WP_REST_Request $request ): WP_REST_Response {
		$name  = sanitize_text_field( (string) $request->get_param( 'name' ) );
		$email = sanitize_email( (string) $request->get_param( 'email' ) );
		$phone = sanitize_text_field( (string) $request->get_param( 'phone' ) );

		if ( empty( $name ) || ! is_email( $email ) || empty( $phone ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'missing_fields', __( 'Name, email and phone are required.', 'playard' ) ),
				422
			);
		}

		$result = $this->customer->create_account( [
			'name'  => $name,
			'email' => $email,
			'phone' => $phone,
		] );

		if ( is_wp_error( $result ) ) {
			$code = $result->get_error_code();
			$http = in_array( $code, [ 'email_exists', 'phone_exists' ], true ) ? 409 : 422;
			return new WP_REST_Response(
				playard_rest_error( $code, $result->get_error_message() ),
				$http
			);
		}

		return new WP_REST_Response(
			playard_rest_success( $result, __( 'Account created successfully.', 'playard' ) ),
			201
		);
	}

	// ── Email OTP login ───────────────────────────────────────────────────────────

	public function send_login_otp( WP_REST_Request $request ): WP_REST_Response {
		$email = sanitize_email( (string) $request->get_param( 'email' ) );

		if ( ! is_email( $email ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_email', __( 'Enter a valid email address.', 'playard' ) ),
				422
			);
		}

		$result = $this->customer->send_login_otp( $email );

		if ( is_wp_error( $result ) ) {
			$code = $result->get_error_code();
			$http = $code === 'too_many_attempts' ? 429 : 422;
			return new WP_REST_Response(
				playard_rest_error( $code, $result->get_error_message() ),
				$http
			);
		}

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

	public function verify_login_otp( WP_REST_Request $request ): WP_REST_Response {
		$email = sanitize_email( (string) $request->get_param( 'email' ) );
		$otp   = sanitize_text_field( (string) $request->get_param( 'otp' ) );

		if ( ! is_email( $email ) || empty( $otp ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'missing_fields', __( 'Email and code are required.', 'playard' ) ),
				422
			);
		}

		if ( ! preg_match( '/^\d{6}$/', $otp ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_otp_format', __( 'Code must be 6 digits.', 'playard' ) ),
				422
			);
		}

		$result = $this->customer->verify_login_otp( $email, $otp );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				422
			);
		}

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

	// ── Password login ────────────────────────────────────────────────────────────

	public function login( WP_REST_Request $request ): WP_REST_Response {
		$login    = sanitize_text_field( (string) $request->get_param( 'login' ) );
		$password = (string) $request->get_param( 'password' );
		$type     = (string) $request->get_param( 'type' );

		if ( empty( $login ) || empty( $password ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'missing_fields', __( 'Login and password are required.', 'playard' ) ),
				422
			);
		}

		$result = $this->customer->login_with_password( $login, $password, $type );

		if ( is_wp_error( $result ) ) {
			$code = $result->get_error_code();
			$http = $code === 'too_many_attempts' ? 429 : 401;
			return new WP_REST_Response(
				playard_rest_error( $code, $result->get_error_message() ),
				$http
			);
		}

		// 2FA: password correct but email OTP required — tell the frontend to prompt for the code
		if ( ! empty( $result['requires_otp'] ) ) {
			return new WP_REST_Response( playard_rest_success( $result ), 200 );
		}

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

	// ── Send OTP ─────────────────────────────────────────────────────────────────

	public function send_otp( WP_REST_Request $request ): WP_REST_Response {
		$phone = sanitize_text_field( (string) $request->get_param( 'phone' ) );

		if ( empty( $phone ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'missing_phone', __( 'Phone number is required.', 'playard' ) ),
				422
			);
		}

		$result = $this->customer->send_otp( $phone );

		if ( is_wp_error( $result ) ) {
			$code = $result->get_error_code();
			$http = $code === 'otp_rate_limited' ? 429 : 422;
			return new WP_REST_Response(
				playard_rest_error( $code, $result->get_error_message() ),
				$http
			);
		}

		return new WP_REST_Response( $result, 200 );
	}

	// ── Verify OTP ───────────────────────────────────────────────────────────────

	public function verify_otp( WP_REST_Request $request ): WP_REST_Response {
		$phone   = sanitize_text_field( (string) $request->get_param( 'phone' ) );
		$otp     = sanitize_text_field( (string) $request->get_param( 'otp' ) );
		$purpose = sanitize_text_field( (string) $request->get_param( 'purpose' ) );

		if ( empty( $phone ) || empty( $otp ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'missing_fields', __( 'Phone and OTP are required.', 'playard' ) ),
				422
			);
		}

		if ( ! preg_match( '/^\d{6}$/', $otp ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_otp_format', __( 'OTP must be 6 digits.', 'playard' ) ),
				422
			);
		}

		$result = $purpose === 'reset'
			? $this->customer->verify_otp_for_reset( $phone, $otp )
			: $this->customer->verify_otp( $phone, $otp );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				422
			);
		}

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

	// ── Refresh token ─────────────────────────────────────────────────────────────

	public function refresh_token( WP_REST_Request $request ): WP_REST_Response {
		$header = (string) $request->get_header( 'authorization' );

		if ( empty( $header ) || ! str_starts_with( $header, 'Bearer ' ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'auth_required', __( 'Authorization header required.', 'playard' ) ),
				401
			);
		}

		$token       = trim( substr( $header, 7 ) );
		$customer_id = $this->customer->decode_jwt_ignore_expiry( $token );

		if ( ! $customer_id ) {
			return new WP_REST_Response(
				playard_rest_error( 'auth_invalid', __( 'Cannot refresh — token signature invalid.', 'playard' ) ),
				401
			);
		}

		$new_token  = $this->customer->generate_jwt( $customer_id );
		$expires_at = gmdate( 'Y-m-d\TH:i:s\Z', time() + ( 7 * DAY_IN_SECONDS ) );

		return new WP_REST_Response( playard_rest_success( [
			'token'      => $new_token,
			'expires_at' => $expires_at,
		] ), 200 );
	}

	// ── Reset password ────────────────────────────────────────────────────────────

	public function reset_password( WP_REST_Request $request ): WP_REST_Response {
		$phone        = sanitize_text_field( (string) $request->get_param( 'phone' ) );
		$reset_token  = sanitize_text_field( (string) $request->get_param( 'reset_token' ) );
		$new_password = (string) $request->get_param( 'new_password' );

		$result = $this->customer->reset_password( $phone, $reset_token, $new_password );

		if ( is_wp_error( $result ) ) {
			$http = $result->get_error_code() === 'token_invalid' ? 410 : 422;
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				$http
			);
		}

		return new WP_REST_Response(
			playard_rest_success( [], __( 'Password updated successfully.', 'playard' ) ),
			200
		);
	}

	// ── Email forgot-password endpoints ───────────────────────────────────────────

	public function forgot_password( WP_REST_Request $request ): WP_REST_Response {
		$email  = sanitize_email( (string) $request->get_param( 'email' ) );
		$result = $this->customer->send_email_reset( $email );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				422
			);
		}

		return new WP_REST_Response(
			playard_rest_success( [], __( 'If that email is registered, a reset code has been sent.', 'playard' ) ),
			200
		);
	}

	public function verify_forgot_password( WP_REST_Request $request ): WP_REST_Response {
		$email  = sanitize_email( (string) $request->get_param( 'email' ) );
		$otp    = sanitize_text_field( (string) $request->get_param( 'otp' ) );
		$result = $this->customer->verify_email_reset( $email, $otp );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				422
			);
		}

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

	public function reset_password_email( WP_REST_Request $request ): WP_REST_Response {
		$email        = sanitize_email( (string) $request->get_param( 'email' ) );
		$reset_token  = sanitize_text_field( (string) $request->get_param( 'reset_token' ) );
		$new_password = (string) $request->get_param( 'new_password' );

		$result = $this->customer->reset_password_by_email( $email, $reset_token, $new_password );

		if ( is_wp_error( $result ) ) {
			$http = $result->get_error_code() === 'token_invalid' ? 410 : 422;
			return new WP_REST_Response(
				playard_rest_error( $result->get_error_code(), $result->get_error_message() ),
				$http
			);
		}

		return new WP_REST_Response(
			playard_rest_success( [], __( 'Password updated successfully.', 'playard' ) ),
			200
		);
	}
}
