<?php
/**
 * File Path: includes/import/class-mbs-amazon-paapi-parser.php
 *
 * Amazon PA-API v5 — Response Parser & Input Validators
 *
 * Pure-static class that extracts MBS candidate data from a raw PA-API v5
 * Item array, and provides ASIN/ISBN-13 format validators used by the
 * orchestrator. Extracted from MBS_Amazon_PAAPI (R-04, 2026-04-25).
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Import
 * @since      1.0.0
 */

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

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

	/**
	 * Parse a single PA-API v5 Item array into the MBS candidate key format.
	 *
	 * Only raw extraction and light type coercion happen here. Sanitization,
	 * escaping, and ISBN normalization are done by the caller (normalise_candidate()).
	 *
	 * @param array  $item       A single element from ItemsResult.Items or SearchResult.Items.
	 * @param string $isbn_hint  ISBN-13 digits from the original lookup code (may be empty).
	 * @return array|false Parsed data array, or false if the item lacks a title.
	 */
	public static function parse_item( array $item, string $isbn_hint ): array|false {

		// Title is required — if absent the candidate will be discarded by normalise_candidate().
		$title = $item['ItemInfo']['Title']['DisplayValue'] ?? '';
		if ( empty( trim( $title ) ) ) {
			return false;
		}

		// ── Author ─────────────────────────────────────────────────────────────
		// PA-API v5 returns Contributors as an array; pick the first 'author' role,
		// or the first contributor of any role if no explicit author is present.

		$author        = '';
		$contributors  = $item['ItemInfo']['ByLineInfo']['Contributors'] ?? array();
		$by_line_brand = $item['ItemInfo']['ByLineInfo']['Brand']['DisplayValue'] ?? '';

		if ( ! empty( $contributors ) ) {
			// Prefer an explicit author role.
			foreach ( $contributors as $contributor ) {
				$role = strtolower( $contributor['RoleType'] ?? $contributor['Role'] ?? '' );
				if ( in_array( $role, array( 'author', 'authors' ), true ) ) {
					$author = $contributor['Name'] ?? '';
					break;
				}
			}
			// Fall back to first contributor if no author role found.
			if ( '' === $author ) {
				$author = $contributors[0]['Name'] ?? '';
			}
		}

		if ( '' === $author && '' !== $by_line_brand ) {
			$author = $by_line_brand;
		}

		// ── ASIN ───────────────────────────────────────────────────────────────

		$asin = $item['ASIN'] ?? '';

		// ── ISBN-13 ────────────────────────────────────────────────────────────
		// ExternalIds may carry ISBN-13 when returned; use the lookup hint as fallback.

		$isbn13        = '';
		$isbn13_values = $item['ItemInfo']['ExternalIds']['ISBNs']['DisplayValues'] ?? array();
		if ( ! empty( $isbn13_values ) ) {
			foreach ( $isbn13_values as $candidate_isbn ) {
				$digits = MBS_ISBN_Helper::normalize_isbn( (string) $candidate_isbn );
				if ( false !== $digits ) {
					$isbn13 = $digits;
					break;
				}
			}
		}
		if ( '' === $isbn13 && '' !== $isbn_hint ) {
			$isbn13 = $isbn_hint;
		}

		// ── Publisher ──────────────────────────────────────────────────────────

		$publisher = $item['ItemInfo']['ManufactureInfo']['Manufacturer']['DisplayValue'] ?? '';
		if ( '' === $publisher ) {
			$publisher = $item['ItemInfo']['ByLineInfo']['Manufacturer']['DisplayValue'] ?? '';
		}

		// ── Publication date ───────────────────────────────────────────────────

		$pub_date = $item['ItemInfo']['ContentInfo']['PublicationDate']['DisplayValue'] ?? '';

		// ── Page count ─────────────────────────────────────────────────────────

		$page_length = (string) ( $item['ItemInfo']['ContentInfo']['PagesCount']['DisplayValue'] ?? '' );

		// ── Format / binding ───────────────────────────────────────────────────
		// Map Amazon binding strings to MBS format keys (best-effort).

		$binding_raw = $item['ItemInfo']['Classifications']['Binding']['DisplayValue'] ?? '';
		$format      = self::map_binding_to_format( $binding_raw );

		// ── Cover image ────────────────────────────────────────────────────────

		$cover_url = $item['Images']['Primary']['Large']['URL'] ?? '';
		if ( '' === $cover_url ) {
			$cover_url = $item['Images']['Primary']['Medium']['URL'] ?? '';
		}

		// ── Price ──────────────────────────────────────────────────────────────
		// Amount is a float in the API currency; return as-is for MBS_Formatting to handle.

		$price    = '';
		$listings = $item['Offers']['Listings'] ?? array();
		if ( ! empty( $listings ) ) {
			$price = (string) ( $listings[0]['Price']['Amount'] ?? '' );
		}

		return array(
			'title'       => $title,
			'author'      => $author,
			'asin'        => $asin,
			'isbn13'      => $isbn13,
			'publisher'   => $publisher,
			'pub_date'    => $pub_date,
			'page_length' => $page_length,
			'format'      => $format,
			'cover_url'   => $cover_url,
			'price'       => $price,
		);
	}

	/**
	 * Map an Amazon binding string to an MBS format key.
	 *
	 * Returns 'paperback' as the default when the binding is unrecognized or absent.
	 *
	 * @param string $binding PA-API Binding.DisplayValue (e.g. "Hardcover").
	 * @return string MBS format key.
	 */
	public static function map_binding_to_format( string $binding ): string {
		$binding_lower = strtolower( trim( $binding ) );

		$map = array(
			'hardcover'              => 'hardcover',
			'library binding'        => 'hardcover',
			'board book'             => 'hardcover',
			'kindle edition'         => 'ebook',
			'ebook'                  => 'ebook',
			'digital'                => 'ebook',
			'audiobook'              => 'audiobook',
			'audio cd'               => 'audiobook',
			'mp3 cd'                 => 'audiobook',
			'audio cassette'         => 'audiobook',
			'audible audiobook'      => 'audiobook',
			'paperback'              => 'paperback',
			'mass market paperback'  => 'paperback',
			'perfect paperback'      => 'paperback',
			'spiral-bound'           => 'paperback',
			'loose leaf'             => 'paperback',
		);

		return $map[ $binding_lower ] ?? 'paperback';
	}

	/**
	 * Return true when the code looks like an Amazon ASIN.
	 *
	 * ASINs are exactly 10 alphanumeric characters (uppercase). This simple
	 * pattern accepts both B-prefixed digital ASINs (B0XXXXXXXX) and older
	 * numeric ASINs (0XXXXXXXXX).
	 *
	 * @param string $code Raw code value.
	 * @return bool
	 */
	public static function is_asin( string $code ): bool {
		return (bool) preg_match( '/^[A-Z0-9]{10}$/i', $code );
	}

	/**
	 * Return true when the digit-stripped code is a valid ISBN-13.
	 *
	 * Only checks length and 978/979 prefix; check-digit validation is handled
	 * downstream by MBS_ISBN_Helper::normalize_isbn().
	 *
	 * @param string $digits Digit-only string (dashes/spaces already stripped).
	 * @return bool
	 */
	public static function is_isbn13( string $digits ): bool {
		return 13 === strlen( $digits )
			&& ( str_starts_with( $digits, '978' ) || str_starts_with( $digits, '979' ) );
	}
}
