<?php
/**
 * File Path: includes/api/class-mbs-onix-parser-30.php
 *
 * ONIX 3.0 Product Parser -- My Book Showroom
 *
 * Receives a parsed SimpleXMLElement root from MBS_Onix_Parser and
 * normalises each <product> / <Product> element into a flat array
 * ready for reconciliation.
 *
 * Supports true ONIX 3.0 alphanumeric short tags (b221, x426, etc.),
 * descriptive short tags (<product>, <titletext>), and reference
 * tags (<Product>, <TitleText>). Short tags take priority in all reads.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Api
 * @since      1.2.0
 */

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

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

	/**
	 * Extract normalised product arrays from an ONIX 3.0 document root.
	 *
	 * @param SimpleXMLElement $xml   Parsed root element.
	 * @param array            &$stats Import stats; errors appended on failure.
	 * @return array[] Array of normalised product arrays.
	 */
	public function parse_products( SimpleXMLElement $xml, array &$stats ): array {
		// Short tags: <product>; reference tags: <Product>.
		$product_elements = $xml->product ?? $xml->Product ?? array();
		$products         = array();

		foreach ( $product_elements as $element ) {
			$product = $this->normalise_product( $element );
			if ( null !== $product ) {
				$products[] = $product;
			}
		}

		if ( empty( $products ) ) {
			$stats['errors'][] = __( 'No valid product records found in the ONIX 3.0 file.', 'my-book-showroom' );
		}

		return $products;
	}

	// -------------------------------------------------------------------------
	// Normalisation
	// -------------------------------------------------------------------------

	/**
	 * Extract a normalised product array from a single SimpleXML product element.
	 *
	 * Returns null if the element has no valid ISBN-13 (13 stripped digits).
	 *
	 * @param SimpleXMLElement $el
	 * @return array|null
	 */
	private function normalise_product( SimpleXMLElement $el ): ?array {

		// -- ISBN-13 (ProductIDType 15) -------------------------------------------
		$isbn        = '';
		$identifiers = $el->productidentifier ?? $el->ProductIdentifier ?? array();
		foreach ( $identifiers as $id ) {
			$type = (string) ( $id->b221 ?? $id->productidtype ?? $id->ProductIDType ?? '' ); // ProductIDType
			if ( '15' === $type ) {
				$isbn = (string) ( $id->b244 ?? $id->idvalue ?? $id->IDValue ?? '' ); // IDValue
				break;
			}
		}
		$isbn = MBS_ISBN_Helper::normalize_isbn( $isbn );
		if ( false === $isbn ) {
			return null; // No usable ISBN.
		}

		// -- DescriptiveDetail block ----------------------------------------------
		$desc = $el->descriptivedetail ?? $el->DescriptiveDetail ?? null;

		// ProductForm + ProductFormDetail.
		$product_form   = '';
		$product_detail = '';
		if ( null !== $desc ) {
			$product_form   = (string) ( $desc->b012 ?? $desc->productform ?? $desc->ProductForm ?? '' ); // ProductForm
			$product_detail = (string) ( $desc->b333 ?? $desc->productformdetail ?? $desc->ProductFormDetail ?? '' ); // ProductFormDetail
		}

		// Title + Subtitle.
		$title    = '';
		$subtitle = '';
		if ( null !== $desc ) {
			$title_detail = $desc->titledetail ?? $desc->TitleDetail ?? null;
			if ( null !== $title_detail ) {
				$title_el = $title_detail->titleelement ?? $title_detail->TitleElement ?? null;
				if ( null !== $title_el ) {
					$title    = (string) ( $title_el->b203 ?? $title_el->titletext ?? $title_el->TitleText ?? '' ); // TitleText
					$subtitle = (string) ( $title_el->b029 ?? $title_el->subtitle ?? $title_el->Subtitle ?? '' ); // Subtitle
				}
			}
		}

		// Author KeyNames (A01 contributor) + Narrator (E07 contributor).
		$author_key_names = '';
		$narrator         = '';
		if ( null !== $desc ) {
			$contributors = $desc->contributor ?? $desc->Contributor ?? array();
			foreach ( $contributors as $contrib ) {
				$role = (string) ( $contrib->b035 ?? $contrib->contributorrole ?? $contrib->ContributorRole ?? '' ); // ContributorRole
				if ( 'A01' === $role && '' === $author_key_names ) {
					$author_key_names = (string) ( $contrib->b040 ?? $contrib->keynames ?? $contrib->KeyNames ?? '' ); // KeyNames
				}
				if ( 'E07' === $role && '' === $narrator ) {
					$narrator = (string) ( $contrib->b037 ?? $contrib->personnameinverted ?? $contrib->PersonNameInverted
						?? $contrib->b040 ?? $contrib->keynames ?? $contrib->KeyNames ?? '' ); // PersonNameInverted / KeyNames
					if ( '' === $narrator ) {
						$nb       = (string) ( $contrib->b039 ?? $contrib->namesbeforekey ?? $contrib->NamesBeforeKey ?? '' ); // NamesBeforeKey
						$kn       = (string) ( $contrib->b040 ?? $contrib->keynames ?? $contrib->KeyNames ?? '' ); // KeyNames
						$narrator = trim( $nb . ' ' . $kn );
					}
				}
			}
		}

		// Contributor statement / BiographicalNote (b044) — first A01 contributor.
		$contributor_statement = '';
		if ( null !== $desc ) {
			$contributors = $desc->contributor ?? $desc->Contributor ?? array();
			foreach ( $contributors as $contrib ) {
				$role = (string) ( $contrib->b035 ?? $contrib->contributorrole ?? $contrib->ContributorRole ?? '' );
				if ( 'A01' === $role ) {
					$contributor_statement = (string) ( $contrib->b044 ?? $contrib->biographicalnote ?? $contrib->BiographicalNote ?? '' );
					break;
				}
			}
		}

		// Sibling ISBNs from RelatedProduct (relation code 06).
		$sibling_isbns = array();
		$related       = $el->relatedmaterial ?? $el->RelatedMaterial ?? null;
		if ( null !== $related ) {
			$related_products = $related->relatedproduct ?? $related->RelatedProduct ?? array();
			foreach ( $related_products as $rp ) {
				$rel_code = (string) ( $rp->x455 ?? $rp->productrelationcode ?? $rp->ProductRelationCode ?? '' ); // ProductRelationCode
				if ( '06' !== $rel_code ) {
					continue;
				}
				$rp_ids = $rp->productidentifier ?? $rp->ProductIdentifier ?? array();
				foreach ( $rp_ids as $rpid ) {
					$rpid_type = (string) ( $rpid->b221 ?? $rpid->productidtype ?? $rpid->ProductIDType ?? '' ); // ProductIDType
					if ( '15' === $rpid_type ) {
						$sib = MBS_ISBN_Helper::normalize_isbn( (string) ( $rpid->b244 ?? $rpid->idvalue ?? $rpid->IDValue ?? '' ) ); // IDValue
						if ( false !== $sib ) {
							$sibling_isbns[] = $sib;
						}
					}
				}
			}
		}

		// Text content (CollateralDetail).
		$collateral  = $el->collateraldetail ?? $el->CollateralDetail ?? null;
		$description = '';
		$short_desc  = '';
		$toc         = '';
		if ( null !== $collateral ) {
			$texts = $collateral->textcontent ?? $collateral->TextContent ?? array();
			foreach ( $texts as $tc ) {
				$text_type = (string) ( $tc->x426 ?? $tc->texttype ?? $tc->TextType ?? '' ); // TextType
				$text_val  = (string) ( $tc->d104 ?? $tc->text ?? $tc->Text ?? '' ); // Text
				if ( '03' === $text_type ) { $description = $text_val; }
				if ( '02' === $text_type ) { $short_desc  = $text_val; }
				if ( '04' === $text_type ) { $toc         = $text_val; }
			}
		}

		// PublishingDetail.
		$pub_detail = $el->publishingdetail ?? $el->PublishingDetail ?? null;
		$pub_status = '';
		$pub_date   = '';
		$pub_name   = '';
		if ( null !== $pub_detail ) {
			$pub_status = (string) ( $pub_detail->b394 ?? $pub_detail->publishingstatus ?? $pub_detail->PublishingStatus ?? '' ); // PublishingStatus
			$pub_dates  = $pub_detail->publishingdate ?? $pub_detail->PublishingDate ?? array();
			foreach ( $pub_dates as $pd ) {
				$role = (string) ( $pd->x448 ?? $pd->publishingdaterole ?? $pd->PublishingDateRole ?? '' ); // PublishingDateRole
				if ( '01' === $role ) {
					$pub_date = (string) ( $pd->b306 ?? $pd->date ?? $pd->Date ?? '' ); // Date
					break;
				}
			}
			$publishers = $pub_detail->publisher ?? $pub_detail->Publisher ?? array();
			foreach ( $publishers as $pub ) {
				$pub_role = (string) ( $pub->b291 ?? $pub->publishingrole ?? $pub->PublishingRole ?? '' ); // PublishingRole
				if ( '01' === $pub_role ) {
					$pub_name = (string) ( $pub->b081 ?? $pub->publishername ?? $pub->PublisherName ?? '' ); // PublisherName
					break;
				}
			}
		}

		// ProductContact (3.0 only — inside publishingdetail).
		$product_contact_role  = '';
		$product_contact_name  = '';
		$product_contact_email = '';
		if ( null !== $pub_detail ) {
			$contacts = $pub_detail->productcontact ?? $pub_detail->ProductContact ?? array();
			foreach ( $contacts as $pc ) {
				$product_contact_role = (string) ( $pc->b377 ?? $pc->productcontactrole ?? $pc->ProductContactRole ?? '' ); // ProductContactRole
				$product_contact_name = (string) ( $pc->b378 ?? $pc->productcontactname ?? $pc->ProductContactName ?? '' ); // ProductContactName
				// ContactDetail > EmailAddress.
				$cd = $pc->contactdetail ?? $pc->ContactDetail ?? null;
				if ( null !== $cd ) {
					$product_contact_email = (string) ( $cd->j272 ?? $cd->emailaddress ?? $cd->EmailAddress ?? '' ); // EmailAddress
				}
				break; // First contact only.
			}
		}

		// SalesRights — build territorial_rights_v2 JSON.
		$territorial_rights_v2 = '';
		$sales_rights_els      = $el->salesrights ?? $el->SalesRights ?? array();
		foreach ( $sales_rights_els as $sr ) {
			$sr_type   = (string) ( $sr->x161 ?? $sr->salesrightstype ?? $sr->SalesRightsType ?? '' ); // SalesRightsType
			$territory = $sr->territory ?? $sr->Territory ?? null;
			$included  = null !== $territory
				? (string) ( $territory->x449 ?? $territory->countriesincluded ?? $territory->CountriesIncluded ?? '' )
				: '';
			$excluded  = null !== $territory
				? (string) ( $territory->x450 ?? $territory->countriesexcluded ?? $territory->CountriesExcluded ?? '' )
				: '';
			$v2 = array( 'type' => 'world', 'countries' => array() );
			if ( '01' === $sr_type && '' === $included && '' === $excluded ) {
				$v2['type'] = 'world';
			} elseif ( '01' === $sr_type && '' !== $included ) {
				$v2['type']      = 'include';
				$v2['countries'] = array_values( array_filter( explode( ' ', $included ) ) );
			} elseif ( in_array( $sr_type, array( '02', '03', '04', '05', '06' ), true ) ) {
				$v2['type']      = 'exclude';
				$v2['countries'] = array_values( array_filter( explode( ' ', $excluded ) ) );
			}
			$territorial_rights_v2 = wp_json_encode( $v2 );
			break; // First SalesRights record only.
		}

		// Language (LanguageRole 01).
		$language = '';
		if ( null !== $desc ) {
			$langs = $desc->language ?? $desc->Language ?? array();
			foreach ( $langs as $lang ) {
				$lang_role = (string) ( $lang->b253 ?? $lang->languagerole ?? $lang->LanguageRole ?? '' ); // LanguageRole
				if ( '01' === $lang_role ) {
					$language = (string) ( $lang->b252 ?? $lang->languagecode ?? $lang->LanguageCode ?? '' ); // LanguageCode
					break;
				}
			}
		}

		// BISAC subject codes (SubjectSchemeIdentifier 10).
		$bisac_codes = array();
		if ( null !== $desc ) {
			$subjects = $desc->subject ?? $desc->Subject ?? array();
			foreach ( $subjects as $subj ) {
				$scheme = (string) ( $subj->b067 ?? $subj->subjectschemeidentifier ?? $subj->SubjectSchemeIdentifier ?? '' ); // SubjectSchemeIdentifier
				if ( '10' === $scheme ) {
					$code = sanitize_text_field( (string) ( $subj->b069 ?? $subj->subjectcode ?? $subj->SubjectCode ?? '' ) ); // SubjectCode
					if ( '' !== $code ) {
						$bisac_codes[] = $code;
					}
				}
			}
		}

		// Thema subject codes (SubjectSchemeIdentifier 93).
		$thema_codes = array();
		if ( null !== $desc ) {
			$subjects = $desc->subject ?? $desc->Subject ?? array();
			foreach ( $subjects as $subj ) {
				$scheme = (string) ( $subj->b067 ?? $subj->subjectschemeidentifier ?? $subj->SubjectSchemeIdentifier ?? '' ); // SubjectSchemeIdentifier
				if ( '93' === $scheme ) {
					$tcode = sanitize_text_field( (string) ( $subj->b069 ?? $subj->subjectcode ?? $subj->SubjectCode ?? '' ) ); // SubjectCode
					if ( '' !== $tcode ) {
						$thema_codes[] = $tcode;
					}
				}
			}
		}

		// Price (PriceType 01 = RRP excluding tax).
		$price    = '';
		$currency = '';
		$supply   = $el->productsupply ?? $el->ProductSupply ?? null;
		if ( null !== $supply ) {
			$supply_details = $supply->supplydetail ?? $supply->SupplyDetail ?? array();
			foreach ( $supply_details as $sd ) {
				$prices = $sd->price ?? $sd->Price ?? array();
				foreach ( $prices as $pr ) {
					$pr_type = (string) ( $pr->x462 ?? $pr->pricetype ?? $pr->PriceType ?? '' ); // PriceType
					if ( '01' === $pr_type ) {
						$price    = (string) ( $pr->j151 ?? $pr->priceamount ?? $pr->PriceAmount ?? '' ); // PriceAmount
						$currency = (string) ( $pr->j152 ?? $pr->currencycode ?? $pr->CurrencyCode ?? '' ); // CurrencyCode
						break 2;
					}
				}
			}
		}

		// Extent: page count (ExtentType 00 / unit 03 = pages) and
		//         duration (ExtentType 09 / unit 15 = HHMM).
		$page_length = 0;
		$duration_hh = 0;
		$duration_mm = 0;
		if ( null !== $desc ) {
			$extents = $desc->extent ?? $desc->Extent ?? array();
			foreach ( $extents as $ext ) {
				$ext_type  = (string) ( $ext->b218 ?? $ext->extenttype ?? $ext->ExtentType ?? '' ); // ExtentType
				$ext_unit  = (string) ( $ext->b220 ?? $ext->extentunit ?? $ext->ExtentUnit ?? '' ); // ExtentUnit
				$ext_value = (string) ( $ext->b219 ?? $ext->extentvalue ?? $ext->ExtentValue ?? '' ); // ExtentValue
				if ( '00' === $ext_type && '03' === $ext_unit ) {
					$page_length = absint( $ext_value );
				} elseif ( '09' === $ext_type && '15' === $ext_unit && 4 === strlen( $ext_value ) ) {
					// Duration HHMM: first two chars = hours, last two = minutes.
					$duration_hh = (int) substr( $ext_value, 0, 2 );
					$duration_mm = (int) substr( $ext_value, 2, 2 );
				}
			}
		}

		// Audience code.
		$audience_code = '';
		if ( null !== $desc ) {
			$audiences = $desc->audience ?? $desc->Audience ?? array();
			foreach ( $audiences as $aud ) {
				// F-009: prefer AudienceCodeValue (b206) over deprecated flat AudienceCode (b073).
				$audience_code = (string) ( $aud->b206 ?? $aud->audiencecodevalue ?? $aud->AudienceCodeValue
					?? $aud->b073 ?? $aud->audiencecode ?? $aud->AudienceCode ?? '' ); // AudienceCodeValue
				if ( '' !== $audience_code ) { break; }
			}
		}

		// Series (Collection, CollectionType 10 = series, Session 95 GAP-007).
		$series_name   = '';
		$series_number = '';
		if ( null !== $desc ) {
			$collections = $desc->collection ?? $desc->Collection ?? array();
			foreach ( $collections as $coll ) {
				$coll_type = (string) ( $coll->x329 ?? $coll->collectiontype ?? $coll->CollectionType ?? '' ); // CollectionType
				if ( '10' !== $coll_type ) { continue; }
				// Series name from TitleDetail/TitleElement/TitleText.
				$coll_td = $coll->titledetail ?? $coll->TitleDetail ?? null;
				if ( null !== $coll_td ) {
					$coll_te = $coll_td->titleelement ?? $coll_td->TitleElement ?? null;
					if ( null !== $coll_te ) {
						$series_name = (string) ( $coll_te->b203 ?? $coll_te->titletext ?? $coll_te->TitleText ?? '' ); // TitleText
					}
				}
				// Series number from CollectionSequence (SequenceType 03 = series).
				$coll_seqs = $coll->collectionsequence ?? $coll->CollectionSequence ?? array();
				foreach ( $coll_seqs as $cs ) {
					$cs_type = (string) ( $cs->x479 ?? $cs->collectionsequencetype ?? $cs->CollectionSequenceType ?? '' ); // CollectionSequenceType
					if ( '03' === $cs_type ) {
						$series_number = (string) ( $cs->x480 ?? $cs->collectionsequencenumber ?? $cs->CollectionSequenceNumber ?? '' ); // CollectionSequenceNumber
						break;
					}
				}
				break;
			}
		}

		// Edition fields.
		$edition_statement = '';
		$edition_number    = 0;
		$edition_type      = '';
		if ( null !== $desc ) {
			$edition_statement = (string) ( $desc->b058 ?? $desc->editionstatement ?? $desc->EditionStatement ?? '' ); // EditionStatement
			$edition_number    = absint( $desc->b057 ?? $desc->editionnumber ?? $desc->EditionNumber ?? 0 ); // EditionNumber
			$edition_type_raw  = (string) ( $desc->b056 ?? $desc->editiontypecode ?? $desc->EditionTypeCode ?? '' ); // EditionType/EditionTypeCode
			if ( MBS_ONIX_Lists::is_valid_edition_type( strtoupper( $edition_type_raw ) ) ) {
				$edition_type = strtoupper( $edition_type_raw );
			}
		}

		// -- Prize composites (inside <descriptivedetail> in ONIX 3.0) ---------------
		$prizes    = array();
		if ( null !== $desc ) {
			$prize_els = $desc->prize ?? $desc->Prize ?? array();
			foreach ( $prize_els as $prz ) {
				$prize_name = sanitize_text_field( (string) ( $prz->b274 ?? $prz->prizename ?? $prz->PrizeName ?? '' ) );
				if ( '' === $prize_name ) {
					continue;
				}
				$prize_year = sanitize_text_field( (string) ( $prz->b275 ?? $prz->prizeyear ?? $prz->PrizeYear ?? '' ) );
				$prize_code = sanitize_text_field( (string) ( $prz->b277 ?? $prz->prizecode ?? $prz->PrizeCode ?? '' ) );
				$prizes[]   = array(
					'name' => $prize_name,
					'year' => $prize_year,
					'code' => $prize_code,
				);
			}
		}

		// -- Assemble and sanitize ------------------------------------------------
		return array(
			'isbn'              => $isbn,
			'sibling_isbns'     => $sibling_isbns,
			'title'             => sanitize_text_field( $title ),
			'subtitle'          => sanitize_text_field( $subtitle ),
			'author_key_names'  => sanitize_text_field( $author_key_names ),
			'product_form'      => sanitize_text_field( $product_form ),
			'product_detail'    => sanitize_text_field( $product_detail ),
			'description'       => wp_kses_post( $description ),
			'short_desc'        => sanitize_text_field( $short_desc ),
			'toc'               => sanitize_text_field( $toc ),
			'pub_status'        => sanitize_text_field( $pub_status ),
			'pub_date'          => sanitize_text_field( $pub_date ),
			'publisher'         => sanitize_text_field( $pub_name ),
			'language'          => sanitize_text_field( $language ),
			'bisac_codes'       => $bisac_codes,
			'thema_codes'       => $thema_codes,
			'price'             => sanitize_text_field( $price ),
			'currency'          => sanitize_text_field( $currency ),
			'page_length'       => $page_length,
			'duration_hh'       => $duration_hh,
			'duration_mm'       => $duration_mm,
			'narrator'          => sanitize_text_field( $narrator ),
			'series_name'       => sanitize_text_field( $series_name ),
			'series_number'     => sanitize_text_field( $series_number ),
			'audience_code'     => sanitize_text_field( $audience_code ),
			'edition_statement' => sanitize_text_field( $edition_statement ),
			'edition_number'    => $edition_number,
			'edition_type'      => sanitize_text_field( $edition_type ),
			'prizes'                 => $prizes,
			'contributor_statement'  => sanitize_text_field( $contributor_statement ),
			'product_contact_role'   => sanitize_text_field( $product_contact_role ),
			'product_contact_name'   => sanitize_text_field( $product_contact_name ),
			'product_contact_email'  => sanitize_email( $product_contact_email ),
			'territorial_rights_v2'  => $territorial_rights_v2,
		);
	}
}
