<?php

/**
 * The public-facing functionality of the plugin.
 *
 * @link       #
 * @since      1.0.0
 *
 * @package    Esraw
 * @subpackage Esraw/public
 */

namespace EasyShippingRate\Frontend;

use EasyShippingRate\ShippingMethod;

/**
 * The public-facing functionality of the plugin.
 *
 * Defines the plugin name, version, and two examples hooks for how to
 * enqueue the public-facing stylesheet and JavaScript.
 *
 * @package    Esraw
 * @subpackage Esraw/public
 */
class Frontend {

	/**
	 * The ID of this plugin.
	 *
	 * @since    1.0.0
	 * @access   private
	 * @var      string    $plugin_name    The ID of this plugin.
	 */
	private $plugin_name;

	/**
	 * The version of this plugin.
	 *
	 * @since    1.0.0
	 * @access   private
	 * @var      string    $version    The current version of this plugin.
	 */
	private $version;

	/**
	 * Cached shipping-method instances, keyed by instance id.
	 *
	 * @var ShippingMethod[]
	 */
	private $methods = array();

	/**
	 * Initialize the class and set its properties.
	 *
	 * @since    1.0.0
	 * @param      string $plugin_name       The name of the plugin.
	 * @param      string $version    The version of this plugin.
	 */
	public function __construct( $plugin_name, $version ) {
		$this->plugin_name = $plugin_name;
		$this->version     = $version;
	}

	/**
	 * Reuse a single method instance per rate, instead of rebuilding it on every
	 * cart/checkout hook (each rebuild re-reads options from the database).
	 *
	 * @param int $instance_id Shipping method instance id.
	 * @return ShippingMethod
	 */
	private function get_method( $instance_id ) {
		$instance_id = absint( $instance_id );
		if ( ! isset( $this->methods[ $instance_id ] ) ) {
			$this->methods[ $instance_id ] = new ShippingMethod( $instance_id );
		}
		return $this->methods[ $instance_id ];
	}

	public function display_shipping_description_on_cart( $method, $index ) {
		$easy_shipping        = $this->get_method( $method->get_instance_id() );
		$description_for_user = $easy_shipping->get_translated_instance_option( ShippingMethod::METHOD_DESCRIPTION, 'Method description', '' );
		$delivery_days        = $easy_shipping->get_option( ShippingMethod::METHOD_ESTIMATED_DELIVERY, '' );

		if ( $description_for_user ) {
			?>
				<small><p class="shipping-method-description"><?php echo wp_kses_post( $description_for_user ); ?></p></small>
			<?php
		}
		if ( $delivery_days ) {
			$estimated_date = wp_date( 'l, d F Y', strtotime( '+' . $delivery_days . ' day' ) );
			if ( $estimated_date ) {
				?>
					<div>
						<small>
							<strong><?php esc_html_e( 'Estimated Delivery Date', 'easy-shipping-rate' ); ?>:</strong> <?php echo esc_html( $estimated_date ); ?>
						</small>
					</div>
				<?php
			}
		}
	}

	public function hide_shipping_when_free_is_available( $rates, $package ) {
		$esraw_meth_general = $this->get_method( 0 );
		if ( 'hide_all' !== $esraw_meth_general->get_option( ShippingMethod::CONFIG_HIDE_ALL ) ) {
			return $rates;
		}
		$free = array();

		foreach ( $rates as $method_key => $pack_method ) {
			if ( $pack_method->get_method_id() === ShippingMethod::METHOD_ID ) {
				$esraw_meth = $this->get_method( $pack_method->get_instance_id() );

				if ( $esraw_meth->is_available( $package ) && $esraw_meth->is_free_shipping ) {
					$free[ $method_key ] = $pack_method;
					break;
				}
			} elseif ( 'free_shipping' === $pack_method->method_id ) {
				$free[ $method_key ] = $pack_method;
				break;
			}
		}
		return ! empty( $free ) ? $free : $rates;
	}

	public function set_default_shipping_method( $method, $available_methods ) {
		foreach ( $available_methods as $method_key => $pack_method ) {
			if ( $pack_method->get_method_id() === ShippingMethod::METHOD_ID ) {
				$esraw_meth = $this->get_method( $pack_method->get_instance_id() );
				if ( 'yes' === $esraw_meth->get_option( ShippingMethod::METHOD_DEFAULT, 'no' ) ) {
					return $method_key;
				}
			}
		}
		return $method;
	}

	public function display_free_shipping_notification(): void {
		echo wp_kses_post( $this->free_shipping_notification_html() );
	}

	/**
	 * Load the compiled Tailwind stylesheet on the cart and checkout for
	 * administrators, so the debug report is styled. Never enqueued for
	 * customers.
	 */
	public function enqueue_debug_assets(): void {
		if ( ! current_user_can( 'manage_woocommerce' ) || ! ( is_cart() || is_checkout() ) ) {
			return;
		}
		wp_enqueue_style( 'esraw-tailwind', ESRAW_URL . 'src/Admin/css/tailwind.build.css', array(), ESRAW_VERSION );
	}

	/**
	 * Force WooCommerce to recalculate shipping (bypassing its per-package rate
	 * cache) while an administrator is debugging with ?debug_shipping=1, so the
	 * report always reflects the current cart.
	 *
	 * @param array $packages Shipping packages.
	 * @return array
	 */
	public function refresh_shipping_for_debug( $packages ) {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only, capability-gated display switch.
		if ( isset( $_GET['debug_shipping'] ) && current_user_can( 'manage_woocommerce' ) ) {
			foreach ( $packages as $index => $package ) {
				$packages[ $index ]['esraw_debug_refresh'] = wp_date( 'YmdHis' );
			}
		}
		return $packages;
	}

	/**
	 * Render the debug report above the classic cart and checkout.
	 */
	public function display_debug_report(): void {
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- values are individually escaped while the report is assembled.
		echo $this->debug_report_html();
	}

	/**
	 * The classic cart hook never fires for the Cart block, so inject the report
	 * there the same way the free shipping notice is injected.
	 *
	 * @param string               $block_content Rendered block HTML.
	 * @param array<string, mixed> $block         Block data.
	 * @return string
	 */
	public function inject_debug_in_cart_block( string $block_content, array $block ): string {
		if ( 'woocommerce/cart' !== $block['blockName'] ) {
			return $block_content;
		}
		$html = $this->debug_report_html();
		return '' !== $html ? $html . $block_content : $block_content;
	}

	/**
	 * woocommerce_before_cart_table never fires for the Cart block, so we inject here.
	 * Not applied to woocommerce/checkout: prepending HTML there breaks React hydration.
	 *
	 * @param string               $block_content Rendered block HTML.
	 * @param array<string, mixed> $block         Block data.
	 * @return string
	 */
	public function inject_notification_in_cart_block( string $block_content, array $block ): string {
		if ( 'woocommerce/cart' !== $block['blockName'] ) {
			return $block_content;
		}
		$html = $this->free_shipping_notification_html();
		return '' !== $html ? $html . $block_content : $block_content;
	}

	/**
	 * Remaining amount before free shipping unlocks, or null when no notice should show.
	 *
	 * Shared by the classic templates, the Cart block, and the Checkout block (StoreAPI).
	 */
	public function free_shipping_remaining(): ?float {
		if ( is_null( WC()->cart ) || is_null( WC()->shipping ) ) {
			return null;
		}
		$packages = WC()->shipping->get_packages();
		if ( empty( $packages[0]['rates'] ) ) {
			return null;
		}
		$current = WC()->cart->subtotal;
		foreach ( $packages[0]['rates'] as $id => $shipping_method ) {
			if ( $shipping_method->get_method_id() !== ShippingMethod::METHOD_ID ) {
				continue;
			}
			$esraw_meth = $this->get_method( $shipping_method->get_instance_id() );
			if ( 'yes' !== $esraw_meth->get_instance_option( ShippingMethod::METHOD_FREE_NOTIFICATION, 'no' ) ) {
				continue;
			}
			$min_amount = (float) $esraw_meth->get_instance_option( ShippingMethod::METHOD_FREE_MIN_AMOUNT, 0 );
			if ( $current < $min_amount ) {
				return $min_amount - $current;
			}
		}
		return null;
	}

	/**
	 * Build the free shipping notification HTML, or return '' when it should not show.
	 */
	private function free_shipping_notification_html(): string {
		$remaining = $this->free_shipping_remaining();
		if ( is_null( $remaining ) ) {
			return '';
		}
		return sprintf(
			'<div class="woocommerce-message">%s<br/><a href="%s">%s</a></div>',
			sprintf(
				/* translators: %s: formatted price amount */
				esc_html__( 'Spend %s more to get free shipping', 'easy-shipping-rate' ),
				wc_price( $remaining )
			),
			esc_url( wc_get_page_permalink( 'shop' ) ),
			esc_html__( 'Continue shopping', 'easy-shipping-rate' )
		);
	}

	/**
	 * Build the administrator-only shipping debug report, or '' when it should
	 * not show. Reads the per-method reports saved during shipping calculation
	 * and keeps only those whose method still has debugging switched on.
	 */
	private function debug_report_html(): string {
		if ( ! current_user_can( 'manage_woocommerce' ) || is_null( WC()->session ) ) {
			return '';
		}
		$reports = WC()->session->get( 'esraw_debug_reports', array() );
		if ( empty( $reports ) ) {
			return '';
		}

		$cards = '';
		foreach ( $reports as $instance_id => $report ) {
			if ( ! $this->get_method( (int) $instance_id )->debug_is_enabled() ) {
				continue;
			}
			$cards .= $this->debug_method_card_html( $report );
		}
		if ( '' === $cards ) {
			return '';
		}

		$icon  = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="8" y="6" width="8" height="14" rx="4"></rect><path d="M19 7l-3 2M5 7l3 2M20 13h-4M4 13h4M19 19l-3-2M5 19l3-2M12 6V4"></path></svg>';
		$title = esc_html__( 'Easy Shipping — rule debug', 'easy-shipping-rate' );
		$badge = esc_html__( 'Visible to administrators only', 'easy-shipping-rate' );

		return '<div class="esraw-my-4 esraw-rounded-lg esraw-border esraw-border-gray-200 esraw-bg-white esraw-p-4 esraw-text-sm esraw-text-gray-800 esraw-shadow-sm">'
			. '<div class="esraw-mb-3 esraw-flex esraw-items-center esraw-gap-2 esraw-text-brand">'
			. $icon
			. '<span class="esraw-font-semibold esraw-text-gray-900">' . $title . '</span>'
			. '<span class="esraw-ml-auto esraw-rounded-full esraw-bg-gray-100 esraw-px-2 esraw-py-0.5 esraw-text-xs esraw-font-medium esraw-text-gray-500">' . $badge . '</span>'
			. '</div>'
			. $cards
			. '</div>';
	}

	/**
	 * Render one method's report: its rules table, any notes and the final-cost
	 * summary.
	 *
	 * @param array $report Stored report for a single method.
	 * @return string
	 */
	private function debug_method_card_html( $report ): string {
		$name = '<div class="esraw-mb-2 esraw-font-semibold esraw-text-gray-900">' . esc_html( $report['method'] ) . '</div>';

		$notes = '';
		foreach ( (array) $report['notes'] as $note ) {
			$notes .= '<p class="esraw-my-1 esraw-text-gray-500">' . esc_html( $note ) . '</p>';
		}
		if ( ! empty( $report['hidden'] ) ) {
			$notes .= '<p class="esraw-my-1 esraw-font-medium esraw-text-red-600">' . esc_html__( 'Result: this shipping method is hidden from customers.', 'easy-shipping-rate' ) . '</p>';
		}

		$rows = '';
		foreach ( (array) $report['rules'] as $rule ) {
			if ( $rule['matched'] ) {
				$result = '<span class="esraw-inline-block esraw-rounded-full esraw-bg-green-100 esraw-px-2 esraw-py-0.5 esraw-text-xs esraw-font-medium esraw-text-green-800">' . esc_html__( 'Matched', 'easy-shipping-rate' ) . '</span>';
			} else {
				$result = '<span class="esraw-inline-block esraw-rounded-full esraw-bg-gray-100 esraw-px-2 esraw-py-0.5 esraw-text-xs esraw-font-medium esraw-text-gray-500">' . esc_html__( 'No match', 'easy-shipping-rate' ) . '</span>';
			}
			$cost = is_null( $rule['cost'] ) ? '<span class="esraw-text-gray-400">—</span>' : wp_kses_post( $rule['cost'] );
			$rows .= '<tr class="esraw-border-t esraw-border-gray-100">'
				. '<td class="esraw-py-2 esraw-pr-3 esraw-text-gray-400">' . esc_html( $rule['row'] ) . '</td>'
				. '<td class="esraw-py-2 esraw-pr-3 esraw-font-medium esraw-text-gray-900">' . esc_html( $rule['label'] ) . '</td>'
				. '<td class="esraw-py-2 esraw-pr-3 esraw-text-gray-600">' . esc_html( $rule['test'] ) . '</td>'
				. '<td class="esraw-py-2 esraw-pr-3 esraw-text-gray-600">' . esc_html( $rule['actual'] ) . '</td>'
				. '<td class="esraw-py-2 esraw-pr-3">' . $result . '</td>'
				. '<td class="esraw-py-2 esraw-text-right esraw-text-gray-900">' . $cost . '</td>'
				. '</tr>';
		}

		$table = '';
		if ( '' !== $rows ) {
			$head = '<thead><tr class="esraw-text-xs esraw-uppercase esraw-tracking-wide esraw-text-gray-400">'
				. '<th class="esraw-py-1 esraw-pr-3 esraw-text-left esraw-font-medium">#</th>'
				. '<th class="esraw-py-1 esraw-pr-3 esraw-text-left esraw-font-medium">' . esc_html__( 'Condition', 'easy-shipping-rate' ) . '</th>'
				. '<th class="esraw-py-1 esraw-pr-3 esraw-text-left esraw-font-medium">' . esc_html__( 'Test', 'easy-shipping-rate' ) . '</th>'
				. '<th class="esraw-py-1 esraw-pr-3 esraw-text-left esraw-font-medium">' . esc_html__( 'Cart value', 'easy-shipping-rate' ) . '</th>'
				. '<th class="esraw-py-1 esraw-pr-3 esraw-text-left esraw-font-medium">' . esc_html__( 'Result', 'easy-shipping-rate' ) . '</th>'
				. '<th class="esraw-py-1 esraw-text-right esraw-font-medium">' . esc_html__( 'Cost', 'easy-shipping-rate' ) . '</th>'
				. '</tr></thead>';
			$table = '<div class="esraw-overflow-x-auto"><table class="esraw-w-full esraw-border-collapse esraw-text-left">' . $head . '<tbody>' . $rows . '</tbody></table></div>';
		}

		return '<div class="esraw-mb-4 last:esraw-mb-0">' . $name . $notes . $table . $this->debug_summary_html( $report['summary'] ) . '</div>';
	}

	/**
	 * Render the final-cost summary line of a method's report.
	 *
	 * @param array $summary Stored summary.
	 * @return string
	 */
	private function debug_summary_html( $summary ): string {
		if ( empty( $summary ) ) {
			return '';
		}
		if ( isset( $summary['result'] ) ) {
			return '<p class="esraw-mt-2 esraw-text-gray-600">' . esc_html( $summary['result'] ) . '</p>';
		}

		$parts = array();
		if ( isset( $summary['final_cost'] ) ) {
			$parts[] = esc_html__( 'Final cost:', 'easy-shipping-rate' ) . ' <strong class="esraw-text-gray-900">' . wp_kses_post( $summary['final_cost'] ) . '</strong>';
		}
		if ( isset( $summary['calculation'] ) ) {
			$parts[] = esc_html__( 'Calculation:', 'easy-shipping-rate' ) . ' ' . esc_html( $summary['calculation'] );
		}
		if ( ! empty( $summary['max_cost_hit'] ) ) {
			$parts[] = esc_html__( 'capped at the maximum cost', 'easy-shipping-rate' );
		}
		if ( empty( $parts ) ) {
			return '';
		}
		return '<p class="esraw-mt-2 esraw-border-t esraw-border-gray-100 esraw-pt-2 esraw-text-gray-600">' . implode( ' <span class="esraw-text-gray-300">·</span> ', $parts ) . '</p>';
	}
}
