<?php
/**
 * File Path: includes/compat/class-mbs-pos-rest-controller.php
 *
 * MBS POS REST Controller
 *
 * Registers two read-only endpoints for point-of-sale integrations
 * (FooSales, Square, OpenPOS) under the mbs/v1 namespace.
 *
 * Endpoints
 * ---------
 * GET /wp-json/mbs/v1/pos/book/{id}
 *   Returns book metadata + all active format rows with POS-relevant fields.
 *   {id} is the mbs_book post ID.
 *
 * GET /wp-json/mbs/v1/pos/isbn/{isbn}
 *   Looks up a book by ISBN-13 and delegates to the book endpoint.
 *   Useful when FooSales or a barcode scanner supplies an ISBN.
 *
 * Authentication
 * --------------
 * Both endpoints require current_user_can( 'edit_posts' ).  External POS
 * apps authenticate via WordPress Application Passwords (WP 5.6+) —
 * no MBS-specific auth scheme is needed.
 *
 * Security
 * --------
 * - No financial data passes through these endpoints.
 * - mbs_wc_product_id is a WooCommerce product reference only.
 * - All output is returned as JSON; no raw SQL or sensitive meta exposed.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Compat
 * @since      1.1.9
 */

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

class MBS_POS_REST_Controller { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	const NAMESPACE = 'mbs/v1';

	/**
	 * Register REST routes.  Called on rest_api_init.
	 */
	/**
	 * Register the rest_api_init hook for POS endpoints.
	 * Moved from MBS_Plugin::load_admin() — REFACTOR-PLAN-ARCH-001 Batch E.
	 */
	public static function register_hooks(): void {
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
	}

	public static function register_routes(): void {
		register_rest_route(
			self::NAMESPACE,
			'/pos/book/(?P<id>\d+)',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( __CLASS__, 'get_book' ),
				'permission_callback' => array( __CLASS__, 'permissions_check' ),
				'schema'              => array( __CLASS__, 'get_item_schema' ),
				'args'                => array(
					'id' => array(
						'required'          => true,
						'validate_callback' => static function ( $value ) {
							return is_numeric( $value ) && (int) $value > 0;
						},
						'sanitize_callback' => 'absint',
					),
				),
			)
		);

		register_rest_route(
			self::NAMESPACE,
			'/pos/isbn/(?P<isbn>[0-9X]{10,17})',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( __CLASS__, 'get_book_by_isbn' ),
				'permission_callback' => array( __CLASS__, 'permissions_check' ),
				'schema'              => array( __CLASS__, 'get_item_schema' ),
				'args'                => array(
					'isbn' => array(
						'required'          => true,
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
			)
		);

		register_rest_route(
			self::NAMESPACE,
			'/pos/location/(?P<id>\\d+)/inventory',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( __CLASS__, 'get_location_inventory' ),
				'permission_callback' => array( __CLASS__, 'permissions_check' ),
				'schema'              => array( __CLASS__, 'get_item_schema' ),
				'args'                => array(
					'id'       => array(
						'required'          => true,
						'validate_callback' => static function ( $value ) {
							return is_numeric( $value ) && (int) $value > 0;
						},
						'sanitize_callback' => 'absint',
					),
					'per_page' => array(
						'default'           => 50,
						'sanitize_callback' => 'absint',
					),
					'page'     => array(
						'default'           => 1,
						'sanitize_callback' => 'absint',
					),
				),
			)
		);
	}

	/**
	 * Permission callback: requires edit_posts capability.
	 * POS apps authenticate via WordPress Application Passwords.
	 *
	 * @return bool|\WP_Error
	 */
	public static function permissions_check(): bool|\WP_Error {
		if ( ! current_user_can( 'edit_posts' ) ) {
			return new WP_Error(
				'mbs_pos_forbidden',
				__( 'You do not have permission to access POS data.', 'my-book-showroom' ),
				array( 'status' => 403 )
			);
		}
		return true;
	}

	/**
	 * GET /mbs/v1/pos/book/{id}
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response|\WP_Error
	 */
	public static function get_book( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$post_id = $request->get_param( 'id' );
		$post    = get_post( $post_id );

		if ( ! $post || MBS_POST_TYPE !== $post->post_type ) {
			return new WP_Error(
				'mbs_pos_not_found',
				__( 'Book not found.', 'my-book-showroom' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( self::build_book_payload( $post ) );
	}

	/**
	 * GET /mbs/v1/pos/isbn/{isbn}
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response|\WP_Error
	 */
	public static function get_book_by_isbn( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$isbn_raw  = $request->get_param( 'isbn' );
		$normalized = MBS_Validation::normalize_isbn13( $isbn_raw );

		if ( ! $normalized ) {
			return new WP_Error(
				'mbs_pos_invalid_isbn',
				__( 'Invalid ISBN-13.', 'my-book-showroom' ),
				array( 'status' => 400 )
			);
		}

		$post_id = MBS_Format_Row_DAO::get_book_id_by_isbn( $normalized );
		if ( ! $post_id ) {
			return new WP_Error(
				'mbs_pos_not_found',
				/* translators: %s: ISBN-13 */
				sprintf( __( 'No book found with ISBN %s.', 'my-book-showroom' ), esc_html( $normalized ) ),
				array( 'status' => 404 )
			);
		}

		$post = get_post( $post_id );
		if ( ! $post || MBS_POST_TYPE !== $post->post_type ) {
			return new WP_Error(
				'mbs_pos_not_found',
				__( 'Book not found.', 'my-book-showroom' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( self::build_book_payload( $post ) );
	}

	/**
	 * Build the JSON payload for a book post.
	 *
	 * @param \WP_Post $post
	 * @return array
	 */
	private static function build_book_payload( \WP_Post $post ): array {
		$wc_product_id   = (int) get_post_meta( $post->ID, 'mbs_wc_product_id', true );
		$wc_enabled      = (bool) get_post_meta( $post->ID, 'mbs_wc_enabled',    true );
		$lifecycle_state = (string) MBS_Book_Meta_DAO::get( $post->ID, 'mbs_lifecycle_state', '' );

		$rows    = MBS_Format_Row_DAO::get_rows_for_post( $post->ID ) ?: array();
		$formats = array();
		foreach ( $rows as $row ) {
			$formats[] = array(
				'row_id'         => (int) $row->id,
				'format'         => sanitize_text_field( $row->format ?? '' ),
				'format_label'   => sanitize_text_field( $row->format_label ?? '' ),
				'isbn13'         => sanitize_text_field( $row->isbn13 ?? '' ),
				'price'          => null !== $row->price ? (float) $row->price : null,
				'currency_code'  => sanitize_text_field( $row->currency_code ?? 'USD' ),
				'square_item_id' => sanitize_text_field( $row->square_item_id ?? '' ),
			);
		}

		return array(
			'post_id'        => $post->ID,
			'title'          => get_the_title( $post ),
			'status'         => $post->post_status,
			'lifecycle_state'=> $lifecycle_state,
			'wc_enabled'     => $wc_enabled,
			'wc_product_id'  => $wc_product_id > 0 ? $wc_product_id : null,
			'formats'        => $formats,
		);
	}

	/**
	 * GET /mbs/v1/pos/location/{id}/inventory
	 *
	 * Returns all format-row stock entries for a location. Intended for OpenPOS
	 * polling to refresh on-hand counts before or during a sales event.
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response|\WP_Error
	 */
	public static function get_location_inventory( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error {
		$location_id = $request->get_param( 'id' );
		$location    = MBS_POS_Location_DAO::get( $location_id );

		if ( ! $location ) {
			return new WP_Error(
				'mbs_pos_not_found',
				__( 'Location not found.', 'my-book-showroom' ),
				array( 'status' => 404 )
			);
		}

		$rows  = MBS_POS_Inventory_DAO::get_by_location( $location_id );

		// V7-D-002: Prime post object cache to eliminate N+1 queries.
		$_post_ids = array_unique( array_column( $rows, 'post_id' ) );
		if ( ! empty( $_post_ids ) ) {
			_prime_post_caches( array_map( 'absint', $_post_ids ), false, false );
		}

		// V7-D-004: Pagination.
		$per_page = (int) ( $request->get_param( 'per_page' ) ?? 50 );
		$page     = (int) ( $request->get_param( 'page' ) ?? 1 );
		$total    = count( $rows );
		$rows     = array_slice( $rows, ( $page - 1 ) * $per_page, $per_page );

		$items = array();
		foreach ( $rows as $row ) {
			$items[] = array(
				'format_row_id' => (int) $row->format_row_id,
				'post_id'       => (int) $row->post_id,
				'title'         => get_the_title( (int) $row->post_id ),
				'format'        => sanitize_text_field( $row->format ?? '' ),
				'format_label'  => sanitize_text_field( $row->format_label ?? '' ),
				'isbn13'        => sanitize_text_field( $row->isbn13 ?? '' ),
				'price'         => null !== $row->price ? (float) $row->price : null,
				'currency_code' => sanitize_text_field( $row->currency_code ?? 'USD' ),
				'stock_qty'     => (int) $row->stock_qty,
				'updated_at'    => $row->updated_at,
			);
		}

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

	/**
	 * Returns a basic JSON schema for POS endpoint responses.
	 * Used by the 'schema' key in register_rest_route().
	 *
	 * @return array
	 */
	public static function get_item_schema(): array {
		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'mbs-pos',
			'type'       => 'object',
			'properties' => array(
				'post_id' => array(
					'description' => __( 'Unique post ID of the book.', 'my-book-showroom' ),
					'type'        => 'integer',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
				'title'   => array(
					'description' => __( 'Book title.', 'my-book-showroom' ),
					'type'        => 'string',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
			),
		);
	}
}
