<?php
/**
 * CommerceBird Quotes
 *
 * Registers the "on-quote" and "quoted" custom order statuses, the quotation
 * payment gateway, all related transactional emails, the Quotations tab on
 * the my-account page, and supporting admin/frontend hooks.
 *
 * Meta key used: _cmbird_quotation_validity  (Y-m-d 23:59:59)
 * Option keys  : cmbird_enable_quotations, cmbird_quotation_validity_days,
 *                cmbird_account_quotations_endpoint
 *
 * @package CommerceBird\Includes\Quotes
 */

defined( 'ABSPATH' ) || exit;

/**
 * CMBIRD_Quotes Class
 */
class CMBIRD_Quotes {

	/**
	 * Bootstrap all hooks. Call once from the main plugin file on plugins_loaded.
	 */
	public static function init(): void {
		$instance = new self();
		$instance->register_hooks();
	}

	/**
	 * Register all hooks and filters.
	 */
	private function register_hooks(): void {
		// Custom order statuses.
		add_filter( 'woocommerce_register_shop_order_post_statuses', array( $this, 'register_order_statuses' ) );
		add_filter( 'wc_order_statuses', array( $this, 'add_order_statuses' ) );
		add_filter( 'wc_order_is_editable', array( $this, 'make_order_statuses_editable' ), 10, 2 );

		// My-account rewrite endpoint (must fire on init).
		add_action( 'init', array( $this, 'add_quotations_endpoint' ) );

		// Payment gateway.
		add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateway' ) );

		// Transactional emails.
		add_filter( 'woocommerce_email_actions', array( $this, 'set_email_actions' ) );
		add_filter( 'woocommerce_email_classes', array( $this, 'set_email_classes' ) );
		add_action( 'woocommerce_email_footer', array( $this, 'quoted_email_validity' ) );

		// Stock management.
		add_filter( 'woocommerce_can_reduce_order_stock', array( $this, 'prevent_stock_reduction_on_quotation' ), 10, 2 );
		add_action( 'woocommerce_order_status_changed', array( $this, 'stock_reduction_on_quotation' ), 20, 4 );

		// Quotation validity.
		add_action( 'woocommerce_order_status_changed', array( $this, 'set_quotation_validity' ), 10, 4 );

		// Payment / cancellation eligibility.
		add_filter( 'woocommerce_valid_order_statuses_for_payment', array( $this, 'quotations_can_be_paid' ), 10, 2 );
		add_filter( 'woocommerce_valid_order_statuses_for_cancel', array( $this, 'quotations_can_be_cancelled' ) );

		// Suppress standard new-order admin email for quote requests.
		add_filter( 'woocommerce_email_recipient_new_order', array( $this, 'prevent_new_order_email' ), 20, 2 );

		// Admin order screen: show validity date.
		add_action( 'woocommerce_admin_order_data_after_payment_info', array( $this, 'display_quotation_validity_admin' ) );

		// My-account tab features (always enabled; set option to 'no' to disable).
		if ( 'no' !== get_option( 'cmbird_enable_quotations', 'yes' ) ) {
			add_action( 'woocommerce_account_quotations_endpoint', array( $this, 'quotations_endpoint_content' ) );
			add_filter( 'woocommerce_endpoint_quotations_title', array( $this, 'quotations_endpoint_title' ), 10, 2 );
			add_filter( 'woocommerce_get_endpoint_url', array( $this, 'quotations_pagination' ), 10, 4 );
			add_filter( 'woocommerce_get_query_vars', array( $this, 'add_query_vars' ) );
			add_filter( 'woocommerce_account_menu_items', array( $this, 'add_quotations_menu' ) );
			add_filter( 'woocommerce_account_menu_item_classes', array( $this, 'active_quotations_menu' ), 10, 2 );
			add_filter( 'woocommerce_my_account_my_orders_query', array( $this, 'remove_quotations_from_orders' ) );
			add_action( 'woocommerce_view_order', array( $this, 'add_payment_button' ), 99 );
			add_action( 'woocommerce_view_order', array( $this, 'add_quotation_validity' ), 99 );
			add_filter( 'woocommerce_get_order_item_totals', array( $this, 'remove_quotations_payment_method' ), 10, 3 );
			add_filter( 'woocommerce_get_return_url', array( $this, 'redirect_to_quotations' ), 10, 2 );
		}
	}

	// -------------------------------------------------------------------------.
	// Order status registration.
	// -------------------------------------------------------------------------.

	/**
	 * Register wc-on-quote and wc-quoted post statuses.
	 *
	 * @param array $order_statuses Existing custom statuses.
	 * @return array
	 */
	public function register_order_statuses( array $order_statuses ): array {
		$order_statuses['wc-on-quote'] = array(
			'label'                     => __( 'On quote', 'commercebird' ),
			'public'                    => true,
			'show_in_admin_status_list' => true,
			'show_in_admin_all_list'    => true,
			'exclude_from_search'       => false,
			/* translators: %s: Number of orders */
			'label_count'               => _n_noop(
				'On quote <span class="count">(%s)</span>',
				'On quote <span class="count">(%s)</span>',
				'commercebird'
			),
		);
		$order_statuses['wc-quoted']   = array(
			'label'                     => __( 'Quoted', 'commercebird' ),
			'public'                    => true,
			'show_in_admin_status_list' => true,
			'show_in_admin_all_list'    => true,
			'exclude_from_search'       => false,
			/* translators: %s: Number of orders */
			'label_count'               => _n_noop(
				'Quoted <span class="count">(%s)</span>',
				'Quoted <span class="count">(%s)</span>',
				'commercebird'
			),
		);
		return $order_statuses;
	}

	/**
	 * Add custom statuses to the WooCommerce order status list.
	 *
	 * @param array $order_statuses Existing statuses.
	 * @return array
	 */
	public function add_order_statuses( array $order_statuses ): array {
		$order_statuses['wc-on-quote'] = __( 'On quote', 'commercebird' );
		$order_statuses['wc-quoted']   = __( 'Quoted', 'commercebird' );
		return $order_statuses;
	}

	/**
	 * Allow on-quote orders to be edited in the admin.
	 *
	 * @param bool     $editable Whether the order is editable.
	 * @param WC_Order $order    Order object.
	 * @return bool
	 */
	public function make_order_statuses_editable( bool $editable, WC_Order $order ): bool {
		if ( in_array( $order->get_status(), array( 'on-quote' ), true ) ) {
			return true;
		}
		return $editable;
	}

	// -------------------------------------------------------------------------.
	// Payment gateway.
	// -------------------------------------------------------------------------.

	/**
	 * Register the quotation payment gateway with WooCommerce.
	 *
	 * @param array $gateways Registered gateway class names.
	 * @return array
	 */
	public function add_gateway( array $gateways ): array {
		$gateways[] = 'CMBIRD_Gateway_Quotation';
		return $gateways;
	}

	// -------------------------------------------------------------------------.
	// Transactional emails.
	// -------------------------------------------------------------------------.

	/**
	 * Register email trigger action hooks with WooCommerce mailer.
	 *
	 * @param array $emails Registered email action slugs.
	 * @return array
	 */
	public function set_email_actions( array $emails ): array {
		$emails[] = 'woocommerce_order_status_pending_to_on-quote';
		$emails[] = 'woocommerce_order_status_on-quote';
		$emails[] = 'woocommerce_order_status_quoted';
		return $emails;
	}

	/**
	 * Register quote email classes with WooCommerce.
	 *
	 * @param array $emails Registered WC_Email instances.
	 * @return array
	 */
	public function set_email_classes( array $emails ): array {
		$emails['CMBIRD_Email_New_Quote']              = new CMBIRD_Email_New_Quote();
		$emails['CMBIRD_Email_Customer_OnQuote_Order'] = new CMBIRD_Email_Customer_OnQuote_Order();
		$emails['CMBIRD_Email_Customer_Quoted_Order']  = new CMBIRD_Email_Customer_Quoted_Order();
		return $emails;
	}

	/**
	 * Append quotation validity date to the footer of quoted order emails.
	 *
	 * @param WC_Email $email Current email instance.
	 */
	public function quoted_email_validity( WC_Email $email ): void {
		if ( 'cmbird_customer_quoted_order' !== $email->id || ! is_a( $email->object, 'WC_Order' ) ) {
			return;
		}
		$order = wc_get_order( $email->object->get_id() );
		if ( ! $order || ! $order->has_status( 'quoted' ) ) {
			return;
		}
		$valid_until = $order->get_meta( '_cmbird_quotation_validity', true );
		if ( $valid_until ) {
			printf(
				'<p><strong>%s:</strong> %s</p>',
				esc_html__( 'Quotation valid until', 'commercebird' ),
				esc_html( date_i18n( get_option( 'date_format' ), strtotime( $valid_until ) ) )
			);
		}
	}

	// -------------------------------------------------------------------------.
	// Stock management.
	// -------------------------------------------------------------------------.

	/**
	 * Prevent stock from being reduced when an order is placed as a quote.
	 *
	 * @param bool     $reduce_stock Whether to reduce stock.
	 * @param WC_Order $order        Order object.
	 * @return bool
	 */
	public function prevent_stock_reduction_on_quotation( bool $reduce_stock, WC_Order $order ): bool {
		if ( $order->has_status( 'on-quote' ) ) {
			return false;
		}
		return $reduce_stock;
	}

	/**
	 * Reduce stock when an on-quote order transitions to quoted status.
	 *
	 * @param int      $order_id   Order ID.
	 * @param string   $old_status Previous status slug.
	 * @param string   $new_status New status slug.
	 * @param WC_Order $order      Order object.
	 */
	public function stock_reduction_on_quotation( int $order_id, string $old_status, string $new_status, WC_Order $order ): void {
		if ( 'quoted' === $new_status ) {
			$stock_reduced = $order->get_meta( '_order_stock_reduced', true );
			if ( empty( $stock_reduced ) ) {
				wc_maybe_reduce_stock_levels( $order_id );
			}
		}
	}

	// -------------------------------------------------------------------------.
	// Quotation validity.
	// -------------------------------------------------------------------------.

	/**
	 * Calculate and store the quotation expiry date when an order becomes quoted.
	 *
	 * @param int      $order_id   Order ID.
	 * @param string   $old_status Previous status slug.
	 * @param string   $new_status New status slug.
	 * @param WC_Order $order      Order object.
	 */
	public function set_quotation_validity( int $order_id, string $old_status, string $new_status, WC_Order $order ): void {
		if ( 'quoted' !== $new_status ) {
			return;
		}
		$validity_days = (int) get_option( 'cmbird_quotation_validity_days', 0 );
		if ( $validity_days > 0 ) {
			$valid_until = ( new DateTime( 'now' ) )
				->modify( '+' . $validity_days . ' days' )
				->format( 'Y-m-d 23:59:59' );
			$order->update_meta_data( '_cmbird_quotation_validity', $valid_until );
			$order->save();
		}
	}

	// -------------------------------------------------------------------------.
	// Payment / cancellation eligibility.
	// -------------------------------------------------------------------------.

	/**
	 * Allow quoted orders to be paid if the validity period has not expired.
	 *
	 * @param array    $statuses Payable status slugs.
	 * @param WC_Order $order    Order object.
	 * @return array
	 */
	public function quotations_can_be_paid( array $statuses, WC_Order $order ): array {
		if ( ! $order->has_status( 'quoted' ) ) {
			return $statuses;
		}
		$valid_until = $order->get_meta( '_cmbird_quotation_validity', true );
		if ( $valid_until ) {
			$now      = new DateTime();
			$validity = new DateTime( $valid_until );
			if ( $validity < $now ) {
				return $statuses; // Expired — do not allow payment.
			}
		}
		$statuses[] = 'quoted';
		return $statuses;
	}

	/**
	 * Allow on-quote and quoted orders to be cancelled by the customer.
	 *
	 * @param array $statuses Cancellable status slugs.
	 * @return array
	 */
	public function quotations_can_be_cancelled( array $statuses ): array {
		$statuses[] = 'on-quote';
		$statuses[] = 'quoted';
		return $statuses;
	}

	// -------------------------------------------------------------------------.
	// Email suppression.
	// -------------------------------------------------------------------------.

	/**
	 * Suppress the standard WooCommerce new-order admin email for quote requests.
	 *
	 * @param string   $recipient Comma-separated email recipients.
	 * @param WC_Order $order     Order object.
	 * @return string
	 */
	public function prevent_new_order_email( string $recipient, $order ): string {
		if ( is_a( $order, 'WC_Order' ) && $order->has_status( 'on-quote' ) ) {
			return '';
		}
		return $recipient;
	}

	// -------------------------------------------------------------------------.
	// Admin order screen.
	// -------------------------------------------------------------------------.

	/**
	 * Display the quotation validity date in the admin order edit screen.
	 *
	 * @param WC_Order $order Order object.
	 */
	public function display_quotation_validity_admin( WC_Order $order ): void {
		$valid_until = $order->get_meta( '_cmbird_quotation_validity', true );
		if ( $valid_until ) {
			printf(
				'<p><strong>%s:</strong> %s</p>',
				esc_html__( 'Quotation valid until', 'commercebird' ),
				esc_html( date_i18n( get_option( 'date_format' ), strtotime( $valid_until ) ) )
			);
		}
	}

	// -------------------------------------------------------------------------.
	// My-account: endpoint setup.
	// -------------------------------------------------------------------------.

	/**
	 * Register the quotations rewrite endpoint.
	 */
	public function add_quotations_endpoint(): void {
		add_rewrite_endpoint(
			get_option( 'cmbird_account_quotations_endpoint', 'quotations' ),
			EP_ROOT | EP_PAGES
		);
	}

	/**
	 * Register the quotations query variable with WooCommerce.
	 *
	 * @param array $query_vars Existing WC query vars.
	 * @return array
	 */
	public function add_query_vars( array $query_vars ): array {
		$query_vars['quotations'] = get_option( 'cmbird_account_quotations_endpoint', 'quotations' );
		return $query_vars;
	}

	// -------------------------------------------------------------------------.
	// My-account: menu.
	// -------------------------------------------------------------------------.

	/**
	 * Insert the Quotations link after Dashboard in the my-account menu.
	 *
	 * @param array $menu_links Existing menu items.
	 * @return array
	 */
	public function add_quotations_menu( array $menu_links ): array {
		$menu_links = array_slice( $menu_links, 0, 1, true )
			+ array( 'quotations' => __( 'Quotations', 'commercebird' ) )
			+ array_slice( $menu_links, 1, count( $menu_links ) - 1, true );
		return $menu_links;
	}

	/**
	 * Highlight the Quotations menu item when viewing a quote order detail page.
	 *
	 * @param array  $classes  CSS classes for the menu item.
	 * @param string $endpoint My-account endpoint slug.
	 * @return array
	 */
	public function active_quotations_menu( array $classes, string $endpoint ): array {
		global $wp;

		if ( ! isset( $wp->query_vars['view-order'] ) ) {
			return $classes;
		}

		$order = wc_get_order( $wp->query_vars['view-order'] );
		if ( ! $order ) {
			return $classes;
		}

		if ( 'quotations' === $endpoint && $order->has_status( array( 'quoted', 'on-quote' ) ) ) {
			$classes[] = 'is-active';
		}
		if ( 'orders' === $endpoint && $order->has_status( array( 'quoted', 'on-quote' ) ) ) {
			$key = array_search( 'is-active', $classes, true );
			if ( false !== $key ) {
				unset( $classes[ $key ] );
			}
		}

		return $classes;
	}

	// -------------------------------------------------------------------------.
	// My-account: content.
	// -------------------------------------------------------------------------.

	/**
	 * Render the quotations list on the my-account quotations page.
	 *
	 * @param int $current_page Current pagination page.
	 */
	public function quotations_endpoint_content( string $current_page ): void {
		$current_page    = empty( $current_page ) ? 1 : absint( $current_page );
		$customer_orders = wc_get_orders(
			array(
				'customer'    => get_current_user_id(),
				'page'        => $current_page,
				'paginate'    => true,
				'post_status' => array( 'wc-on-quote', 'wc-quoted' ),
			)
		);

		wc_get_template(
			'myaccount/orders.php',
			array(
				'current_page'    => absint( $current_page ),
				'customer_orders' => $customer_orders,
				'has_orders'      => 0 < $customer_orders->total,
				'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' )
					? ' ' . wc_wp_theme_get_element_class_name( 'button' )
					: '',
			)
		);
	}

	/**
	 * Set the page title for the quotations endpoint.
	 *
	 * @param string $title    Current title.
	 * @param string $endpoint Endpoint slug.
	 * @return string
	 */
	public function quotations_endpoint_title( string $title, string $endpoint ): string {
		return esc_html__( 'Quotations', 'commercebird' );
	}

	/**
	 * Rewrite pagination URLs to stay on the quotations endpoint.
	 *
	 * @param string $url       Current endpoint URL.
	 * @param string $endpoint  Endpoint slug.
	 * @param int    $value     Page number.
	 * @param string $permalink Current permalink.
	 * @return string
	 */
	public function quotations_pagination( string $url, string $endpoint, string $value, string $permalink ): string {
		if ( did_action( 'woocommerce_before_account_orders_pagination' ) ) {
			if ( 'orders' === $endpoint && is_wc_endpoint_url( 'quotations' ) ) {
				return wc_get_endpoint_url( 'quotations', $value );
			}
		}
		return $url;
	}

	/**
	 * Exclude on-quote and quoted orders from the standard my-account orders list.
	 *
	 * @param array $args wc_get_orders() arguments.
	 * @return array
	 */
	public function remove_quotations_from_orders( array $args ): array {
		$args['post_status'] = array_diff(
			array_keys( wc_get_order_statuses() ),
			array( 'wc-on-quote', 'wc-quoted', 'wc-checkout-draft' )
		);
		return $args;
	}

	// -------------------------------------------------------------------------.
	// My-account: order view page.
	// -------------------------------------------------------------------------.

	/**
	 * Show a Pay Now button on the order view page for valid quoted orders.
	 *
	 * @param int $order_id Order ID.
	 */
	public function add_payment_button( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( ! $order || ! $order->has_status( 'quoted' ) ) {
			return;
		}
		$valid_until = $order->get_meta( '_cmbird_quotation_validity', true );
		if ( $valid_until ) {
			$now      = new DateTime();
			$validity = new DateTime( $valid_until );
			if ( $validity < $now ) {
				return; // Expired.
			}
		}
		printf(
			'<a href="%s" class="button">%s</a>',
			esc_url( $order->get_checkout_payment_url() ),
			esc_html__( 'Pay now', 'commercebird' )
		);
	}

	/**
	 * Show the quotation validity date on the order view page.
	 *
	 * @param int $order_id Order ID.
	 */
	public function add_quotation_validity( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( ! $order || ! $order->has_status( 'quoted' ) ) {
			return;
		}
		$valid_until = $order->get_meta( '_cmbird_quotation_validity', true );
		if ( $valid_until ) {
			printf(
				'<p><strong>%s:</strong> %s</p>',
				esc_html__( 'Quotation valid until', 'commercebird' ),
				esc_html( date_i18n( get_option( 'date_format' ), strtotime( $valid_until ) ) )
			);
		}
	}

	/**
	 * Hide the payment method row in the order totals table for quote orders.
	 *
	 * @param array    $rows        Order total rows.
	 * @param WC_Order $order       Order object.
	 * @param bool     $tax_display Tax display setting.
	 * @return array
	 */
	public function remove_quotations_payment_method( array $rows, WC_Order $order, bool $tax_display ): array {
		if ( $order->has_status( array( 'quoted', 'on-quote' ) ) ) {
			unset( $rows['payment_method'] );
		}
		return $rows;
	}

	/**
	 * Redirect the customer to their quotations tab after placing a quote.
	 *
	 * @param string   $return_url Default return URL.
	 * @param WC_Order $order      Order object.
	 * @return string
	 */
	public function redirect_to_quotations( string $return_url, ?WC_Order $order ): string {
		if ( $order?->has_status( 'on-quote' ) ) {
			return wc_get_account_endpoint_url( 'quotations' );
		}
		return $return_url;
	}
}
