<?php
/**
 * Product panel class.
 *
 * @package HBFWC
 */

namespace HBFWC\Admin;

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

use HBFWC\Utils\Constants;
use function HBFWC\HBF_WC;

/**
 * Class HBFWC Admin
 */
class HBFWC_Admin {
	/**
	 * Class constructor.
	 */
	public function __construct() {
		$this->include_files();

		add_action( 'init', array( $this, 'connect_page' ) );
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );
		add_filter( 'woocommerce_screen_ids', array( $this, 'register_screen_id' ) );
		add_filter( 'post_row_actions', array( $this, 'manage_accommodation_link' ), 10, 2 );
		add_action( 'hbfwc_bulk_global_action', array( $this, 'bulk_global_action' ), 10, 3 );
		add_action( 'hbfwc_bulk_rate_action', array( $this, 'bulk_rate_action' ), 10, 4 );
		add_action( 'admin_head', array( $this, 'add_help_tabs' ) );
	}

	/**
	 * Include all required files.
	 */
	private function include_files() {
		include_once __DIR__ . '/hbfwc-admin-functions.php';
		include_once __DIR__ . '/class-admin-menus.php';
		include_once __DIR__ . '/class-admin-amenities.php';
		include_once __DIR__ . '/class-admin-rate-plans.php';
		include_once __DIR__ . '/class-admin-availability.php';
		include_once __DIR__ . '/class-product-panel.php';
	}

	/**
	 * Connect our page as WC page.
	 */
	public function connect_page() {
		if ( function_exists( '\wc_admin_connect_page' ) ) {
			\wc_admin_connect_page(
				array(
					'id'        => 'woocommerce-product-amenities',
					'title'     => __( 'Amenities', 'hotel-booking-for-woocommerce' ),
					'parent'    => 'woocommerce-products',
					'screen_id' => 'product_page_amenities',
					'path'      => 'edit.php?post_type=product&page=amenities',
				)
			);

			\wc_admin_connect_page(
				array(
					'id'        => 'woocommerce-product-rate-plans',
					'title'     => __( 'Rate Plans', 'hotel-booking-for-woocommerce' ),
					'parent'    => 'woocommerce-products',
					'screen_id' => 'product_page_rate_plans',
					'path'      => 'edit.php?post_type=product&page=rate_plans',
				)
			);

			\wc_admin_connect_page(
				array(
					'id'        => 'woocommerce-product-availability',
					'title'     => __( 'Availability &amp; Prices', 'hotel-booking-for-woocommerce' ),
					'parent'    => 'woocommerce-products',
					'screen_id' => 'product_page_availability',
					'path'      => 'edit.php?post_type=product&page=availability',
				)
			);
		}
	}

	/**
	 * Register new screen id for WC.
	 *
	 * @param array $ids The current screen ids.
	 * @return array
	 */
	public function register_screen_id( $ids ) {
		array_push( $ids, 'product_page_availability' );
		return $ids;
	}

	/**
	 * Show the "Availability & Prices" link in admin products list.
	 *
	 * @param array   $actions Array of actions.
	 * @param WP_Post $post Post object.
	 * @return array
	 */
	public function manage_accommodation_link( $actions, $post ) {
		if ( ! current_user_can( 'edit_products' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
			return $actions;
		}

		if ( 'product' !== $post->post_type ) {
			return $actions;
		}

		$product = \wc_get_product( $post );

		if ( $product && ! $product->is_type( 'accommodation' ) ) {
			return $actions;
		}

		if ( 'publish' === $post->post_status && $product ) {
			$actions['hbfwc'] = '<a href="' . esc_url( admin_url( 'edit.php?post_type=product&page=availability&amp;accommodation_id=' . $post->ID ) ) . '" aria-label="' . esc_attr__( 'Manage availability and prices for this product', 'hotel-booking-for-woocommerce' ) . '" rel="permalink">' . esc_html__( 'Availability & Prices', 'hotel-booking-for-woocommerce' ) . '</a>';
		}

		return $actions;
	}

	/**
	 * Bulk global action (availability | unit status).
	 *
	 * @param string $act_value The action value.
	 * @param int    $product_id The product/unit id.
	 * @param array  $args The additional arguments.
	 *
	 * @return mixed
	 */
	public function bulk_global_action( $act_value, $product_id, $args ) {
		$value      = $args['value'];
		$start_date = new \DateTime( $args['from'] );
		$end_date   = new \DateTime( $args['end'] );
		$week_days  = array_map( 'absint', $args['weekdays'] );

		$product = \wc_get_product( $product_id );

		if ( ! $product ) {
			return false;
		}

		$allotment = $product->get_allotment();

		if ( 'availability' === $act_value ) {
			$from_zero = [];
			$to_zero   = [];
			while ( $start_date <= $end_date ) {
				$current_day = $start_date->format( 'Y-m-d' );
				$day_of_week = absint( $start_date->format( 'w' ) );

				if ( ! in_array( $day_of_week, $week_days, true ) ) {
					$start_date->modify( '+1 day' );
					continue;
				}

				$maybe_update = HBF_WC()->db->get_single_date_entry( $product_id, $current_day );

				if ( $maybe_update ) { // Update value.
					$current_qty = $maybe_update->qty;

					if ( 'plus' === $args['set'] ) {
						$new_qty = intval( $current_qty ) + intval( $value );
						if ( '' !== $allotment && $new_qty > $allotment ) {
							$new_qty = $allotment;
						}
					} elseif ( 'minus' === $args['set'] ) {
						$new_qty = intval( $current_qty ) - intval( $value );
						if ( $new_qty < 0 ) {
							$new_qty = 0;
						}
					} else {
						$new_qty = intval( $value );
						if ( '' !== $allotment && $new_qty > $allotment ) {
							$new_qty = $allotment;
						}
					}

					if ( 1 > intval( $current_qty ) && 0 < intval($new_qty) ) {
						$from_zero[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					if ( 1 > intval($new_qty) ) {
						$to_zero[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					if ( absint( $new_qty ) === absint( $allotment ) && absint( $maybe_update ) > 0 ) {
						HBF_WC()->db->delete_entry( $maybe_update->id );
					} else {
						HBF_WC()->db->update(
							$maybe_update->id,
							array(
								'qty' => absint( $new_qty ),
							),
							array( '%d' ),
						);
					}
				} else {

					// No record. So default should be the allotment.
					$current_qty = '' === $allotment ? 0 : $allotment;

					if ( 'plus' === $args['set'] ) {
						$new_qty = intval( $current_qty ) + intval( $value );
						if ( '' !== $allotment && $new_qty > $allotment ) {
							$new_qty = $allotment;
						}
					} elseif ( 'minus' === $args['set'] ) {
						$new_qty = intval( $current_qty ) - intval( $value );
						if ( $new_qty < 0 ) {
							$new_qty = 0;
						}
					} else {
						$new_qty = intval( $value );
						if ( '' !== $allotment && $new_qty > $allotment ) {
							$new_qty = $allotment;
						}
					}

					if ( 1 > intval( $current_qty ) && 0 < intval($new_qty) ) {
						$from_zero[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					if ( 1 > intval($new_qty) ) {
						$to_zero[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					// Skip if the new quantity is same as default allotment.
					if ( absint( $new_qty ) === absint( $allotment ) ) {
						$start_date->modify( '+1 day' );
						continue;
					}

					HBF_WC()->db->add_new(
						array(
							'post_id'      => $product_id,
							'qty'          => $new_qty,
							'booking_date' => $current_day,
						),
						array( '%d', '%d', '%s' ),
					);
				}

				$start_date->modify( '+1 day' );
			}

			do_action( 'hbfwc_reduced_stock_zero', $to_zero );

			do_action( 'hbfwc_release_stock_zero', $from_zero );

		} elseif ( 'status' === $act_value ) {
			$from_closed = [];
			$to_closed   = [];

			while ( $start_date <= $end_date ) {
				$current_day = $start_date->format( 'Y-m-d' );
				$day_of_week = absint( $start_date->format( 'w' ) );

				if ( ! in_array( $day_of_week, $week_days, true ) ) {
					$start_date->modify( '+1 day' );
					continue;
				}

				$maybe_update = HBF_WC()->db->get_single_date_entry( $product_id, $current_day );

				$allow_to_update = apply_filters( 'hbfwc_bulk_update_status_check', true, $product_id, $current_day );
				if ( ! $allow_to_update ) {
					$start_date->modify( '+1 day' );
					continue;
				}

				if ( $maybe_update ) {
					if ( absint( $maybe_update->status ) === absint( $value ) ) {
						$start_date->modify( '+1 day' );
						continue;
					}

					if ( 1 > intval( $maybe_update->status ) ) {
						$from_closed[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					if ( 1 > intval( $value ) ) {
						$to_closed[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					if ( absint( $value ) > 0 && absint( $maybe_update->qty ) === $allotment ) {
						HBF_WC()->db->delete_entry( $maybe_update->id );
					} else {
						HBF_WC()->db->update(
							$maybe_update->id,
							array(
								'status' => absint( $value ),
							),
							array( '%d' ),
						);
					}
				} else {
					// No record in DB yet. Means the availability also on the default state.
					// If the data send to us is the default one (active), we let it.
					// To keep the DB clean from unnecessary data.
					if ( absint( $value ) > 0 ) {
						$start_date->modify( '+1 day' );
						continue;
					}

					if ( 1 > intval( $value ) ) {
						$to_closed[] = [
							'id'   => $product_id,
							'date' => $current_day
						];
					}

					HBF_WC()->db->add_new(
						array(
							'post_id'      => $product_id,
							'qty'          => absint( $allotment ),
							'booking_date' => $current_day,
							'status'       => absint( $value ),
						),
						array( '%d', '%d', '%s', '%d' ),
					);
				}

				$start_date->modify( '+1 day' );
			}

			do_action( 'hbfwc_reduced_stock_zero', $to_closed );

			do_action( 'hbfwc_release_stock_zero', $from_closed );
		}
	}

	/**
	 * Bulk rate actions.
	 *
	 * @param string $action The action value.
	 * @param int    $product_id The product/unit id.
	 * @param int    $rate_id The rate id.
	 * @param array  $args The additional arguments.
	 *
	 * @return mixed
	 */
	public function bulk_rate_action( $action, $product_id, $rate_id, $args ) {
		// Pro has own method.
		if ( hbfwc_is_pro() ) {
			return false;
		}

		$value      = $args['value'];
		$set        = $args['set'];
		$start_date = new \DateTime( $args['from'] );
		$end_date   = new \DateTime( $args['end'] );
		$week_days  = array_map( 'absint', $args['weekdays'] );

		$product = \wc_get_product( $product_id );

		if ( ! $product ) {
			return false;
		}

		// Process the default rate.
		if ( absint( $rate_id ) === absint( get_option( 'hbfwc_default_rate_id', 0 ) ) ) {
			$current       = $product->get_meta( '_default_rate_prices' );
			$default_price = $product->get_rate_default_price( absint( $rate_id ), true, false );

			// Normalize once
			$default_price = wc_format_decimal( $default_price, wc_get_price_decimals() );

			if ( ! is_array( $current ) ) {
				$current = array();
			}

			while ( $start_date <= $end_date ) {
				$current_day = $start_date->format( 'Y-m-d' );
				$day_key     = $start_date->format( 'Ymd' );
				$day_of_week = absint( $start_date->format( 'w' ) );

				if ( ! in_array( $day_of_week, $week_days, true ) ) {
					$start_date->modify( '+1 day' );
					continue;
				}

				$data_before = isset( $current[ $day_key ] ) ? $current[ $day_key ] : array();
				$price       = isset( $data_before['price'] ) ? wc_format_decimal( $data_before['price'], wc_get_price_decimals() ) : $default_price;
				$status      = 'rate_status' === $action ? absint( $value ) : ( isset( $data_before['status'] ) ? absint( $data_before['status'] ) : 1 );

				if ( 'price' === $action ) {
					$before_price = $price;

					if ( 'to' === $set ) {
						$price = wc_format_decimal( $value, wc_get_price_decimals() );
					} else {
						$value  = wc_format_decimal( $value, wc_get_price_decimals() );
						$price  = 'minus' === $set 
							? bcsub( $before_price, $value, Constants::MATH_SCALE ) 
							: bcadd( $before_price, $value, Constants::MATH_SCALE );
					}
				}

				// Final format before storing
				$price = wc_format_decimal( $price, wc_get_price_decimals() );

				if ( ! empty( $data_before ) && ( 0 < $status && $default_price === (float) $price ) ) {
					// Let's keep it clean.
					unset( $current[ $day_key ] );
				} else {
					$current[ $day_key ] = array(
						'date'   => $current_day,
						'price'  => $price,
						'status' => $status,
					);
				}

				$start_date->modify( '+1 day' );
			}

			update_post_meta( $product_id, '_default_rate_prices', $current );
		}
	}

	/**
	 * Admin notices.
	 */
	public function admin_notices() {
		$manage_stock = get_option( 'woocommerce_manage_stock' );
		$hold_stock   = get_option( 'woocommerce_hold_stock_minutes' );
		if ( $manage_stock !== 'yes' || $hold_stock === '' ) {
			echo '<div class="notice notice-warning is-dismissible">';
			echo '<p><strong>' . __( 'Hotel Booking for WooCommerce', 'hotel-booking-for-woocommerce' ) . '</strong> ';
			echo __( 'relies on WooCommerce\'s stock system. Please make sure:', 'hotel-booking-for-woocommerce' ) . '</p>';
			echo '<ul style="margin-left:20px;list-style:disc;">';
			echo '<li>' . __( '"Manage stock" is enabled', 'hotel-booking-for-woocommerce' ) . '</li>';
			echo '<li>' . __( '"Hold stock (minutes)" has a value set', 'hotel-booking-for-woocommerce' ) . '</li>';
			echo '</ul>';
			echo '<p>' . __( 'To ensure bookings are held and availability is updated correctly. You can find these settings under', 'hotel-booking-for-woocommerce' ) . ' ';
			echo '<strong><a href="' . esc_url( admin_url('admin.php?page=wc-settings&tab=products&section=inventory') ) . '">' . __( 'WooCommerce > Settings > Products > Inventory', 'hotel-booking-for-woocommerce' ) . '</a></strong>.</p>';
			echo '</div>';
		}
	}

	/**
	 * Add help tabs to the booking add-on screen.
	 * @since 1.0.06
	 */
	public function add_help_tabs() {
		$screen = get_current_screen();
		if ( $screen->post_type !== 'booking_addon' ) {
			return;
		}

		$types_help = array(
			__( 'Fixed', 'hotel-booking-for-woocommerce' )          => __( 'Applies a fixed price, either per room or per booking, depending on your selection.', 'hotel-booking-for-woocommerce' ),
			__( 'Quantity based', 'hotel-booking-for-woocommerce' ) => __( 'Lets the guest select a quantity for the add-on. Useful for extras like towels or meal packages.', 'hotel-booking-for-woocommerce' ),
		);

		$types_help_li = '';
		foreach( $types_help as $type => $description ) {
			$types_help_li .= '<li><strong>' . esc_html( $type ) . '</strong><br/>' . esc_html( $description ) . '</li>';
		}
		
		$addon_types = '<ul>' . $types_help_li . '</ul>';

		$screen->add_help_tab(
			array(
				'id'      => 'hbfwc_booking_addon_help_tab',
				'title'   => __( 'Booking Add-on', 'hotel-booking-for-woocommerce' ),
				'content' => '<p>' . __( 'This screen allows you to create and manage extra services or upsells that can be attached to bookings.', 'hotel-booking-for-woocommerce' ) . '</p>' . $addon_types,
			)
		);
	}

	/**
	 * Notice to inform user to update the PRO addon.
	 *
	 * @since 1.0.10
	 * @return void
	 */
	private function notice_pro_need_update() {
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
		$all_plugins = get_plugins();

		if ( isset($all_plugins['hbfwc-pro-addon/hbfwc-pro-addon.php']) ) {
			$version = $all_plugins['hbfwc-pro-addon/hbfwc-pro-addon.php']['Version'];
			if ( version_compare( $version, HBFWC_VERSION, '<' ) ) {
				$this->create_notice(
					'Update "Hotel Booking for WooCommerce Pro Add-on" to the latest version to ensure the PRO add-on works properly.',
					'error',
					false
				);
			}
		}
	}

	private function create_notice( $message, $type = 'info', $dissmisable = true ) {
		print '<div class="notice notice-' . esc_attr( $type ) . ' ' . ( $dissmisable ? 'is-dismissible' : '' ) .'">';
		print '<p>' . wp_kses_post( $message ) . '</p>';
		print '</div>';
	}
}

new HBFWC_Admin();
