<?php
/**
 * Postomatic Plugin - SureCart Product Source.
 *
 * PHP Version 5.6
 *
 * @package  Postomatic
 * @category Plugin
 * @license  GPL-2.0-or-later https://www.gnu.org/licenses/gpl-2.0.html
 * @link     https://buildomatics.com/postomatics
 * @since    1.3.0
 */

namespace Postomatic\Api;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Product_Source_Surecart.
 *
 * Reads products from SureCart. SureCart documents its product post type
 * (`sc_product`) and the `sc_get_product()` helper as the supported way to
 * query products through the regular WordPress Loop
 * (see https://developer.surecart.com/documentation/custom-loops).
 *
 * Price/image formatting relies on the SureCart Product model's public
 * attributes (`metrics`, `featured_product_media`) as documented in the
 * SureCart REST API reference. Because SureCart does not publish a fixed PHP
 * property contract for those attributes, every access here is defensive
 * (isset/property_exists checks with graceful fallbacks) so a future
 * SureCart update can only degrade the displayed price/image, not break
 * product retrieval.
 *
 * @since 1.3.0
 */
class Product_Source_Surecart extends Product_Source_Abstract {

	const PLATFORM_KEY = 'surecart';
	const POST_TYPE    = 'sc_product';

	/**
	 * {@inheritDoc}
	 */
	public static function is_active(): bool {
		// The sc_product post type and sc_get_product() helper are
		// documented as the stable, supported integration points.
		return function_exists( 'sc_get_product' ) && post_type_exists( self::POST_TYPE );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_platform_key(): string {
		return self::PLATFORM_KEY;
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_platform_label(): string {
		return 'SureCart';
	}

	/**
	 * {@inheritDoc}
	 */
	public function has_products(): bool {
		if ( ! self::is_active() ) {
			return false;
		}

		$ids = get_posts(
			array(
				'post_type'      => self::POST_TYPE,
				'post_status'    => 'publish',
				'posts_per_page' => 1,
				'fields'         => 'ids',
				'no_found_rows'  => true,
			)
		);

		return ! empty( $ids );
	}

	/**
	 * {@inheritDoc}
	 *
	 * @param string $search   Optional search term to filter by product name.
	 * @param int    $page     Page number (1-indexed).
	 * @param int    $per_page Number of products per page.
	 */
	public function get_products( string $search = '', int $page = 1, int $per_page = 20 ): array {
		if ( ! self::is_active() ) {
			return $this->empty_result( $page, $per_page );
		}

		$args = array(
			'post_type'      => self::POST_TYPE,
			'post_status'    => 'publish',
			'posts_per_page' => $per_page,
			'paged'          => $page,
		);

		if ( '' !== $search ) {
			$args['s'] = $search;
		}

		$query = new \WP_Query( $args );
		$total = (int) $query->found_posts;

		$products = array();

		if ( $query->have_posts() ) {
			foreach ( $query->posts as $post ) {
				$products[] = $this->format_product( $post );
			}
		}

		wp_reset_postdata();

		return array(
			'products'   => $products,
			'pagination' => $this->build_pagination( $total, $page, $per_page ),
		);
	}

	/**
	 * Normalize a single SureCart product post into Postomatic's shared shape.
	 *
	 * @param \WP_Post $post SureCart product post object.
	 *
	 * @return array<string, mixed>
	 */
	private function format_product( $post ): array {
		$product_id = $post->ID;

		// sc_get_product() hydrates the SureCart Product model for this post,
		// exposing the same attributes as the SureCart REST API response.
		$sc_product = function_exists( 'sc_get_product' ) ? sc_get_product( $post ) : null;

		$price_html = $this->extract_price_html( $sc_product );
		$image_url  = $this->extract_image_url( $sc_product, $product_id );

		$description = has_excerpt( $product_id ) ? get_the_excerpt( $product_id ) : wp_trim_words( wp_strip_all_tags( $post->post_content ), 30 );

		$product_meta = array(
			'id'                 => $product_id,
			'platform'           => self::PLATFORM_KEY,
			'name'               => $post->post_title,
			'sku'                => $this->extract_attribute( $sc_product, 'sku', '' ),
			'price'              => $this->extract_attribute( $sc_product, 'min_price_amount', '' ),
			'regular_price'      => $this->extract_attribute( $sc_product, 'min_price_amount', '' ),
			'sale_price'         => '',
			'price_html'         => $price_html,
			'on_sale'            => false,
			'stock_status'       => $this->extract_attribute( $sc_product, 'allow_out_of_stock_purchases', true ) ? 'instock' : 'outofstock',
			'stock_quantity'     => $this->extract_attribute( $sc_product, 'available_stock', '' ),
			'short_description'  => $description,
			'description'        => $post->post_content,
			'permalink'          => get_permalink( $product_id ),
			'categories'         => array(),
			'tags'               => array(),
			'attributes'         => array(),
			'dimensions'         => array(
				'length' => '',
				'width'  => '',
				'height' => '',
			),
			'weight'             => $this->extract_attribute( $sc_product, 'weight', '' ),
			'average_rating'     => $this->extract_attribute( $sc_product, 'average_stars', '' ),
			'review_count'       => '',
			'image_ids'          => array(),
			'image_urls'         => $image_url ? array( $image_url ) : array(),
			'article_image_ids'  => array(),
			'article_image_urls' => array(),
			'main_image'         => $image_url,
			'main_image_large'   => $image_url,
			'related_products'   => array(),
		);

		return $this->build_list_item(
			$product_id,
			$post->post_title,
			$price_html,
			$description,
			get_permalink( $product_id ),
			$image_url,
			$product_meta
		);
	}

	/**
	 * Safely read an attribute off the SureCart product model, regardless of
	 * whether it exposes attributes as public properties or via ArrayAccess.
	 *
	 * @param mixed  $sc_product     SureCart product model instance, or null.
	 * @param string $key            Attribute name.
	 * @param mixed  $fallback_value Value to return when the attribute is unavailable.
	 *
	 * @return mixed
	 */
	private function extract_attribute( $sc_product, string $key, $fallback_value ) {
		if ( ! $sc_product ) {
			return $fallback_value;
		}

		if ( is_object( $sc_product ) && isset( $sc_product->{$key} ) ) {
			return $sc_product->{$key};
		}

		if ( $sc_product instanceof \ArrayAccess && isset( $sc_product[ $key ] ) ) {
			return $sc_product[ $key ];
		}

		return $fallback_value;
	}

	/**
	 * Build a human-readable price string from the product's metrics, when
	 * available. Falls back to an empty string rather than guessing a format.
	 *
	 * @param mixed $sc_product SureCart product model instance, or null.
	 *
	 * @return string
	 */
	private function extract_price_html( $sc_product ): string {
		$metrics = $this->extract_attribute( $sc_product, 'metrics', null );

		if ( ! $metrics ) {
			return '';
		}

		$min_amount = is_object( $metrics ) ? ( $metrics->min_price_amount ?? null ) : ( $metrics['min_price_amount'] ?? null );
		$currency   = is_object( $metrics ) ? ( $metrics->currency ?? 'usd' ) : ( $metrics['currency'] ?? 'usd' );

		if ( null === $min_amount ) {
			return '';
		}

		// SureCart stores amounts in the smallest currency unit (cents).
		$formatted = number_format( ( (float) $min_amount ) / 100, 2 );

		return esc_html( strtoupper( $currency ) . ' ' . $formatted );
	}

	/**
	 * Resolve a display image URL for the product.
	 *
	 * Prefers the WordPress featured image (SureCart products are still a
	 * standard post type and commonly carry a normal WP featured image), and
	 * falls back to the SureCart product media relationship when present.
	 *
	 * @param mixed $sc_product SureCart product model instance, or null.
	 * @param int   $product_id Product post ID.
	 *
	 * @return string
	 */
	private function extract_image_url( $sc_product, int $product_id ): string {
		$thumbnail_url = get_the_post_thumbnail_url( $product_id, 'large' );
		if ( $thumbnail_url ) {
			return $thumbnail_url;
		}

		$gallery = $this->extract_attribute( $sc_product, 'gallery', array() );
		if ( is_array( $gallery ) && ! empty( $gallery ) ) {
			$first = $gallery[0];
			if ( is_object( $first ) && method_exists( $first, 'attributes' ) ) {
				$attributes = $first->attributes( 'thumbnail' );
				if ( isset( $attributes->src ) ) {
					return (string) $attributes->src;
				}
			}
		}

		return '';
	}
}
