<?php
/**
 * Warranty offers where the customer is already buying.
 *
 * @package WarrantyCart
 */

namespace WarrantyCart\Frontend;

use WarrantyCart\Core\Coverage;
use WarrantyCart\Core\Repository;
use WC_Product;

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

/**
 * Offers cover on the single product page and in the cart.
 *
 * The `[warranty_cart]` selector sells warranties from a page of their own,
 * which asks a customer to go and find it and then choose cover for something
 * they are not looking at. Nobody browses for a warranty; they buy one
 * alongside the thing it protects. This puts the offer there.
 */
class ProductOffer {

	/**
	 * Constructor
	 */
	public function __construct() {
		// After the button, not before: the offer is a follow-up to the decision
		// to buy, and above the button it reads as an obstacle in front of it.
		\add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'render_product_offer' ) );

		\add_action( 'woocommerce_after_cart_table', array( $this, 'render_cart_offer' ) );
	}

	/**
	 * The offer beneath Add to cart.
	 *
	 * @return void
	 */
	public function render_product_offer(): void {
		if ( ! Coverage::shows_on_product() ) {
			return;
		}

		global $product;

		if ( ! $product instanceof WC_Product ) {
			return;
		}

		$this->render_for( $product, 'product' );
	}

	/**
	 * The offer under the cart table, for items with no cover attached.
	 *
	 * @return void
	 */
	public function render_cart_offer(): void {
		if ( ! Coverage::shows_in_cart() || ! function_exists( 'WC' ) || null === WC()->cart ) {
			return;
		}

		foreach ( $this->uncovered_cart_items() as $product ) {
			$this->render_for( $product, 'cart' );
		}
	}

	/**
	 * Cart items that could be covered and are not.
	 *
	 * Both halves matter. Offering cover for something the customer has already
	 * covered in this same cart is the fastest way to make the feature feel
	 * broken, and it is the case a cart-page offer runs into constantly —
	 * because the product page is where they just bought it.
	 *
	 * @return array<int, WC_Product>
	 */
	private function uncovered_cart_items(): array {
		$covered  = array();
		$products = array();

		foreach ( WC()->cart->get_cart() as $item ) {
			$covers = (int) ( $item['warranty_cart_covers'] ?? 0 );

			if ( $covers > 0 ) {
				$covered[] = $covers;
			}
		}

		foreach ( WC()->cart->get_cart() as $item ) {
			$product = $item['data'] ?? null;

			if ( ! $product instanceof WC_Product ) {
				continue;
			}

			// A warranty is not something to sell a warranty for.
			if ( '' !== Repository::product_type( $product ) ) {
				continue;
			}

			if ( in_array( $product->get_id(), $covered, true ) ) {
				continue;
			}

			$products[ $product->get_id() ] = $product;
		}

		return array_values( $products );
	}

	/**
	 * Render the offer for one product.
	 *
	 * @param WC_Product $product Product being viewed or in the cart.
	 * @param string     $context `product` or `cart`.
	 *
	 * @return void
	 */
	private function render_for( WC_Product $product, string $context ): void {
		$offers = Coverage::for_product( $product );

		/*
		 * Cover the customer already has, stated rather than sold.
		 *
		 * Worth the space on its own: a shop whose whole use of this plugin is
		 * "one year included, claim it here" has nothing to sell, and without
		 * this the product page would say nothing at all.
		 */
		$included = Coverage::included_for_product( $product );

		if ( empty( $offers ) && empty( $included ) ) {
			return;
		}

		\wc_get_template(
			'frontend/product-warranty-offer.php',
			array(
				'product'  => $product,
				'offers'   => $offers,
				'included' => $included,
				'context'  => $context,
			),
			'warranty-cart',
			WARRANTY_CART_PATH . 'templates/'
		);
	}
}
