<?php
/**
 * File Path: includes/import/class-mbs-importer-novelist.php
 *
 * MBS_Importer_Novelist -- Competitor WXR importer for Novelist.
 *
 * Imports books exported by the Novelist plugin (CPT: book) into My Book
 * Showroom. Because RS WP Books Showcase also registers a 'book' CPT,
 * matches_source() is overridden to gate on Novelist-specific meta keys
 * ('novelist_title' or 'novelist_isbn'), matching the disambiguation added
 * to detect_source() in class-mbs-importer-wxr.php (C-001 fix).
 *
 * Meta keys read:
 *   novelist_title          Book title (also used for C-001 disambiguation).
 *   novelist_isbn           ISBN-13 string.
 *   novelist_asin           ASIN (B-prefix = Kindle ebook).
 *   novelist_publisher      Publisher name.
 *   novelist_pub_date       Publication date (free-text string).
 *   novelist_pages          Page count (integer string).
 *   novelist_cover          Attachment ID -- NOT portable across sites; ignored.
 *                           The WXR featured-image mechanism (_thumbnail_id) is
 *                           used instead when present in the export.
 *   novelist_series         Series position number.
 *   novelist_purchase_links PHP-serialised associative array:
 *                           {'retailer_slug' => 'https://...', ...}
 *                           Retailer slugs (e.g. 'amazon', 'barnes-noble') are
 *                           passed as labels; URL-based retailer detection
 *                           applies in insert_book().
 *
 * Taxonomy read:
 *   novelist-series         Series name (first term name used).
 *
 * @since  1.3.0
 * @package MyBookShowroom\Import
 *
 * Class MBS_Importer_Novelist
 *
 * source_key()      -- Returns 'novelist'.
 * target_cpt()      -- Returns 'book' (the Novelist CPT slug).
 * matches_source()  -- Confirms 'novelist_title' or 'novelist_isbn' key present.
 * map_item()        -- Maps Novelist WXR item to MBS insert_book() args.
 */

defined( 'ABSPATH' ) || exit;

/**
 * MBS_Importer_Novelist
 *
 * @since 1.3.0
 */
class MBS_Importer_Novelist extends MBS_Competitor_WXR_Base { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	// ── Abstract method implementations ─────────────────────────────────────

	/**
	 * @inheritDoc
	 * @return string
	 */
	protected static function source_key(): string {
		return 'novelist';
	}

	/**
	 * @inheritDoc
	 * @return string
	 */
	protected static function target_cpt(): string {
		return 'book';
	}

	// ── CPT collision guard ──────────────────────────────────────────────────

	/**
	 * Confirm this item is a Novelist book, not an RS WP Books Showcase book.
	 *
	 * Both plugins register the 'book' CPT. We verify that at least one of
	 * the Novelist-specific meta keys is present in $meta_keys.
	 * Mirrors the detect_source() logic in class-mbs-importer-wxr.php (C-001).
	 *
	 * @since  1.3.0
	 * @param  array<int, string> $meta_keys All meta keys present on this item.
	 * @return bool True when at least one Novelist key is found.
	 */
	protected static function matches_source( array $meta_keys ): bool {
		return in_array( 'novelist_title', $meta_keys, true )
			|| in_array( 'novelist_isbn', $meta_keys, true );
	}

	// ── Core mapping ─────────────────────────────────────────────────────────

	/**
	 * Map a single Novelist WXR item to MBS insert_book() argument arrays.
	 *
	 * @since  1.3.0
	 * @param  array<string, mixed> $item_data Normalised WXR item (title, content,
	 *                                          meta, categories, attachment_url).
	 * @param  array<string, mixed> $mapping   User field-mapping (unused — all
	 *                                          fields are mapped automatically).
	 * @return array{post: array, format: array, buy_links: array, post_meta: array}
	 *         Argument arrays ready for insert_book().
	 */
	protected static function map_item( array $item_data, array $mapping ): array {
		$meta  = $item_data['meta'] ?? array();
		$cats  = $item_data['categories'] ?? array();
		$notes = array();

		// ── ISBN ──────────────────────────────────────────────────────────────
		$raw_isbn = $meta['novelist_isbn'] ?? '';
		$isbn13   = '' !== $raw_isbn
			? MBS_ISBN_Helper::normalize_isbn( $raw_isbn )
			: '';

		// ── ASIN ─────────────────────────────────────────────────────────────
		$raw_asin = $meta['novelist_asin'] ?? '';
		$asin     = '' !== $raw_asin
			? MBS_ISBN_Helper::normalize_asin( $raw_asin )
			: '';

		// ── Publication date ──────────────────────────────────────────────────
		$pub_date = sanitize_text_field( $meta['novelist_pub_date'] ?? '' );

		// ── Page count ────────────────────────────────────────────────────────
		$page_length = 0;
		$raw_pages   = $meta['novelist_pages'] ?? '';
		if ( '' !== $raw_pages && preg_match( '/(\d+)/', $raw_pages, $pm ) ) {
			$page_length = (int) $pm[1];
		}

		// ── Publisher ─────────────────────────────────────────────────────────
		$publisher = sanitize_text_field( $meta['novelist_publisher'] ?? '' );

		// ── Series ────────────────────────────────────────────────────────────
		// Series name comes from the 'novelist-series' taxonomy term.
		// Series position number is stored in the 'novelist_series' meta key.
		$series_name     = '';
		$series_position = '';

		foreach ( $cats as $cat ) {
			if ( isset( $cat['taxonomy'] ) && 'novelist-series' === $cat['taxonomy'] ) {
				$series_name = sanitize_text_field( $cat['name'] ?? '' );
				break;
			}
		}

		$raw_pos = $meta['novelist_series'] ?? '';
		if ( '' !== $raw_pos ) {
			$series_position = sanitize_text_field( $raw_pos );
		}

		// ── Format row ────────────────────────────────────────────────────────
		// Novelist stores a single ISBN and ASIN for the whole book (no per-format
		// repeater). build_isbn_asin_format_rows() applies the B-ASIN gate:
		//   B-ASIN + ISBN  → two rows (paperback + ebook)
		//   B-ASIN only    → one ebook row
		//   ISBN only      → one paperback row
		//   neither        → paperback fallback
		$format_rows = static::build_isbn_asin_format_rows(
			$isbn13,
			$asin,
			'paperback',     // Novelist has no per-book format field
			$pub_date,
			$page_length,
			array()          // no shared scalars beyond what build_* sets
		);

		// ── Buy links ─────────────────────────────────────────────────────────
		// novelist_purchase_links is a PHP-serialised assoc array where each
		// key is a retailer slug (e.g. 'amazon', 'barnes-noble') and each
		// value is the URL. maybe_unserialize is used directly here because
		// the value is a single associative array, not a repeater (array of
		// arrays), so unserialize_repeater() is not appropriate.
		$raw_links   = $meta['novelist_purchase_links'] ?? '';
		$links_assoc = array();
		if ( '' !== (string) $raw_links ) {
			// SA-M6: Restrict unserialize() to non-object types to prevent object injection.
			$unserialized = is_serialized( $raw_links )
				? unserialize( $raw_links, array( 'allowed_classes' => false ) )
				: $raw_links;
			if ( is_array( $unserialized ) ) {
				$links_assoc = $unserialized;
			}
		}

		$buy_links = array();
		foreach ( $links_assoc as $retailer_slug => $url ) {
			$url = esc_url_raw( (string) $url );
			if ( '' === $url ) {
				continue;
			}
			$buy_links[] = array(
				'url'          => $url,
				'retailer_key' => '',   // URL-based retailer detection in insert_book()
				'label'        => sanitize_text_field( (string) $retailer_slug ),
			);
		}

		// Deduplicate by URL (handles any overlap between retailer slugs).
		$buy_links = static::deduplicate_buy_links( $buy_links );

		// ── Post meta ─────────────────────────────────────────────────────────
		$post_meta = array();

		if ( '' !== $publisher ) {
			$post_meta['mbs_publisher'] = $publisher;
		}
		if ( '' !== $series_name ) {
			$post_meta['mbs_series_name'] = $series_name;
		}
		if ( '' !== $series_position ) {
			$post_meta['mbs_series_position'] = $series_position;
		}

		// novelist_cover stores a source-site attachment ID and is not portable.
		// The WXR <wp:attachment_url> mechanism (stored in $item_data['attachment_url'])
		// or the standard _thumbnail_id featured-image export handles cover art.

		return array(
			'post'      => array(
				'post_title'   => sanitize_text_field( $item_data['title'] ?? '' ),
				'post_content' => wp_kses_post( $item_data['content'] ?? '' ),
				'post_status'  => 'publish',
				'post_type'    => 'mbs_book',
			),
			'format'    => $format_rows,
			'buy_links' => $buy_links,
			'post_meta' => $post_meta,
			'notes'     => $notes,
		);
	}
}
