<?php
/**
 * WooCommerce Shipping Method: InPost Parcel Locker
 *
 * FIX v3.3.4:
 *  - Rejestracja metody przeniesiona na hook `woocommerce_shipping_init` (pewność że WC_Shipping_Method istnieje)
 *  - Dodano jawne `init_settings()` i obsługę błędu gdy klasa bazowa niedostępna
 *  - Dodano `tax_status` z instancji zamiast hardcode
 *  - Zabezpieczenie `calculate_shipping` przed null WC()->cart
 *
 * @package ParcelSelector
 */

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

/**
 * PS_Shipping_Method — WooCommerce shipping method for InPost parcel lockers.
 * Supports shipping zones, free shipping threshold, and 12 European countries.
 *
 * WAŻNE: Klasa musi być ładowana dopiero po `woocommerce_shipping_init`.
 * Rejestracja: parcselect_register_shipping_method() – hook woocommerce_shipping_methods.
 */
if ( ! class_exists( 'Parcselect_Shipping_Method' ) && class_exists( 'WC_Shipping_Method' ) ) :

class Parcselect_Shipping_Method extends WC_Shipping_Method {

	/** @var float Shipping cost. */
	public $cost = 0.0;

	/** @var float Minimum cart total for free shipping (0 = disabled). */
	public $free_shipping_min = 0.0;

	/** @var string Estimated delivery time shown in orders (e.g. "1-2 business days"). */
	public $delivery_time = '';

	/** @var string Additional custom text appended to order details. */
	public $order_note = '';

	/**
	 * Constructor.
	 *
	 * @param int $instance_id Shipping zone instance ID.
	 */
	public function __construct( $instance_id = 0 ) {
		$this->id                 = 'parcselect_inpost_paczkomat';
		$this->instance_id        = absint( $instance_id );
		$this->method_title       = __( 'InPost Parcel Locker', 'parcel-selector' );
		$this->method_description = __( 'Delivery to parcel lockers in 23 European countries. 8 carriers. Interactive map for pickup point selection. No API key required.', 'parcel-selector' );
		$this->supports           = array(
			'shipping-zones',
			'instance-settings',
			'instance-settings-modal',
		);

		$this->init_form_fields();
		$this->init_settings();

		$this->title             = $this->get_option( 'title', __( 'InPost Parcel Locker', 'parcel-selector' ) );
		$this->enabled           = $this->get_option( 'enabled', 'yes' );
		$this->cost              = (float) $this->get_option( 'cost', (string) apply_filters( 'parcselect_default_inpost_cost', '19.99' ) );
		$this->free_shipping_min = (float) $this->get_option( 'free_shipping_min', '0' );
		$this->tax_status        = $this->get_option( 'tax_status', 'taxable' );
		$this->delivery_time     = $this->get_option( 'delivery_time', '' );
		$this->order_note        = $this->get_option( 'order_note', '' );

		add_action(
			'woocommerce_update_options_shipping_' . $this->id,
			array( $this, 'process_admin_options' )
		);

		// Rejestrujemy filtr etykiety tylko raz globalnie (zabezpieczenie przed multi-instance)
		// Register label filter only once globally (prevents duplicate "(WAW01A)(WAW01A)" if multiple zones)
		if ( ! did_action( 'parcselect_locker_label_filter_registered' ) ) {
			add_filter( 'woocommerce_shipping_rate_label', array( $this, 'add_locker_to_label' ), 10, 2 );
			do_action( 'parcselect_locker_label_filter_registered' );
		}
	}

	/**
	 * Dynamically append locker ID to shipping label if selected.
	 *
	 * @param string            $label  Current label.
	 * @param WC_Shipping_Rate  $rate   Shipping rate.
	 * @return string
	 */
	public function add_locker_to_label( $label, $rate ) {
		if ( $rate->get_method_id() !== $this->id ) {
			return $label;
		}

		if ( ! function_exists( 'WC' ) || ! WC()->session ) {
			return $label;
		}

		$locker = WC()->session->get( 'parcselect_locker_name' );
		if ( ! empty( $locker ) ) {
			$label .= ' (' . esc_html( $locker ) . ')';
		}

		return $label;
	}

	/**
	 * Define instance form fields (shown in zone settings modal).
	 */
	public function init_form_fields() {
		$this->instance_form_fields = array(
			'title'             => array(
				'title'   => __( 'Method title', 'parcel-selector' ),
				'type'    => 'text',
				'default' => __( 'InPost Parcel Locker', 'parcel-selector' ),
			),
			'cost'              => array(
				'title'       => __( 'Shipping cost', 'parcel-selector' ),
				'type'        => 'price',
				'default'     => (string) apply_filters( 'parcselect_default_inpost_cost', '19.99' ),
				'placeholder' => '0.00',
				'description' => __( 'Default 19.99 PLN for InPost Parcel Locker. Set to 0 for free shipping.', 'parcel-selector' ),
				'desc_tip'    => true,
			),
			'free_shipping_min' => array(
				'title'       => __( 'Free shipping threshold', 'parcel-selector' ),
				'type'        => 'price',
				'default'     => '0',
				'description' => __( 'Minimum cart total for free shipping. Set to 0 to disable.', 'parcel-selector' ),
				'desc_tip'    => true,
			),
			'tax_status'        => array(
				'title'   => __( 'Tax status', 'parcel-selector' ),
				'type'    => 'select',
				'default' => 'taxable',
				'options' => array(
					'taxable' => __( 'Taxable', 'parcel-selector' ),
					'none'    => __( 'None', 'parcel-selector' ),
				),
			),
			'delivery_time'     => array(
				'title'       => __( 'Estimated delivery time', 'parcel-selector' ),
				'type'        => 'text',
				'default'     => '',
				'placeholder' => __( 'e.g. 1-2 business days', 'parcel-selector' ),
				'description' => __( 'Shown in order details and emails. Leave empty to hide.', 'parcel-selector' ),
				'desc_tip'    => true,
			),
			'order_note'        => array(
				'title'       => __( 'Additional order note', 'parcel-selector' ),
				'type'        => 'textarea',
				'default'     => '',
				'placeholder' => __( 'Optional text shown in order details (e.g. packing instructions).', 'parcel-selector' ),
				'description' => __( 'Extra text appended to order details. Leave empty to hide.', 'parcel-selector' ),
				'desc_tip'    => true,
			),
		);
	}

	/**
	 * Calculate the shipping cost.
	 *
	 * @param array $package Shipping package.
	 */
	public function calculate_shipping( $package = array() ) {
		// Admin panel global config — provides defaults / overrides for the zone instance settings
		$ship_cfg  = get_option( 'parcselect_shipping_config', array() );
		$cfg_free  = isset( $ship_cfg['free'] ) && 'yes' === $ship_cfg['free'];
		$cfg_price = isset( $ship_cfg['price'] ) && '' !== $ship_cfg['price'] ? (float) $ship_cfg['price'] : null;

		// Use instance cost; fall back to admin panel price if instance cost is 0 and admin panel has one
		$cost = $this->cost;
		if ( 0.0 === $cost && null !== $cfg_price ) {
			$cost = $cfg_price;
		}

		$free_min = $this->free_shipping_min;

		// Sprawdzamy czy koszyk istnieje i czy subtotal >= progu darmowej dostawy
		if ( $free_min > 0 && function_exists( 'WC' ) && WC()->cart && ! WC()->cart->is_empty() ) {
			$subtotal = (float) WC()->cart->get_subtotal();
			if ( $subtotal >= $free_min ) {
				$cost = 0.0;
			}
		}

		// Admin panel "Free Shipping" checkbox overrides everything
		if ( $cfg_free ) {
			$cost = 0.0;
		}

		// Niestandardowy punkt odbioru zawsze bezpłatny (bez kuriera)
		if ( function_exists( 'WC' ) && WC()->session
			&& '1' === (string) WC()->session->get( 'parcselect_is_custom_pickup', '0' ) ) {
			$cost = 0.0;
		}

		/**
		 * Filter the final shipping cost before WooCommerce applies tax.
		 * Useful for multi-currency plugins (e.g. WOOCS, Currency Switcher).
		 *
		 * @param float $cost    Calculated shipping cost.
		 * @param array $package WooCommerce shipping package.
		 */
		$cost = apply_filters( 'parcselect_shipping_cost', $cost, $package );

		/**
		 * Fires just before the shipping rate is added.
		 * Use this to attach extra meta or modify rate args.
		 *
		 * @param float  $cost    Final cost.
		 * @param array  $package Shipping package.
		 * @param static $this    Shipping method instance.
		 */
		do_action( 'parcselect_before_add_rate', $cost, $package, $this );

		$this->add_rate( array(
			'id'         => $this->get_rate_id(),
			'label'      => $this->title,
			'cost'       => $cost,
			'package'    => $package,
			'tax_status' => $this->tax_status,
			'meta_data'  => array(), // Required for WC 8+ HPOS compatibility
		) );
	}

	/**
	 * Check if this shipping method is available for the given package.
	 *
	 * @param array $package Shipping package.
	 * @return bool
	 */
	public function is_available( $package ) {
		if ( ! parent::is_available( $package ) ) {
			return false;
		}

		/**
		 * Supported countries for InPost shipping (Poland only in the free version).
		 *
		 * @param string[] $countries ISO 3166-1 alpha-2 country codes.
		 */
		$supported = apply_filters(
			'parcselect_inpost_supported_countries',
			array( 'PL' )
		);

		$country = isset( $package['destination']['country'] )
			? strtoupper( $package['destination']['country'] )
			: '';

		// Brak kraju w paczce — próbujemy odczytać z sesji klienta (WC 8+ Blocks/HPOS)
		if ( empty( $country ) && function_exists( 'WC' ) && WC()->customer ) {
			$country = strtoupper( (string) WC()->customer->get_shipping_country() );
			if ( empty( $country ) ) {
				$country = strtoupper( (string) WC()->customer->get_billing_country() );
			}
		}

		// Nadal brak kraju — pozwalamy, WC sam zadecyduje w strefach
		if ( empty( $country ) ) {
			return true;
		}

		if ( in_array( $country, $supported, true ) ) {
			return true;
		}

		// Niestandardowy punkt odbioru — omija ograniczenie krajowe (odbiór w sklepie)
		$pickup = get_option( 'parcselect_custom_pickup', array() );
		if ( ! empty( $pickup['enabled'] ) && 'yes' === $pickup['enabled']
			&& ! empty( $pickup['lat'] ) && ! empty( $pickup['lng'] ) ) {
			return true;
		}

		return false;
	}
}

endif; // class_exists PS_Shipping_Method
