<?php
/**
 * PS_Label_Autotrigger — automatic label session creation on payment complete.
 *
 * Hook: woocommerce_payment_complete fires after a customer pays for their order.
 * We build a payload from order meta (_ps_locker_*, shipping address, stored sender
 * data) and POST it to map-server /embed/label/session. The x21 daemon on VPS then
 * takes over: detects on-chain payment, triggers shipping provider, produces PDF.
 *
 * Admin sees progress via the metabox (polling Parcselect_Label_Session_Client::get_status)
 * and can download the PDF once status flips to "completed".
 *
 * @package ParcelSelector
 */

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

class Parcselect_Label_Autotrigger {

	public static function init() {
		// v4.2.1 BEZWZGLEDNY consent gate — Autotrigger NIE wywoluje Oracle/Pdf_Store bez consent.
		if ( ! class_exists( 'Parcselect_Label_Consent_Gate' ) || ! Parcselect_Label_Consent_Gate::is_valid() ) {
			if ( class_exists( 'Parcselect_Label_Debug' ) ) {
				Parcselect_Label_Debug::info( 0, 'autotrigger.skipped_no_consent', array() );
			}
			return;
		}
		// Label generation is strictly ADMIN-DRIVEN. The customer pays for their
		// WC order via whatever gateway the store uses (fiat or otherwise) —
		// that payment is independent of label crypto-payment. Only the admin,
		// inside wp-admin, ever sees or triggers the crypto label payment flow.
		//
		// Historically this class hooked woocommerce_payment_complete to auto-
		// create a label session on customer payment. That was removed per the
		// explicit requirement: customer has zero visibility into label crypto.
		//
		// The hourly cron that polls pending sessions is kept — it runs for
		// sessions an admin kicked off manually and then walked away from (e.g.
		// closed the browser tab after sending SOL).
		add_action( 'parcselect_label_poll_pending', array( __CLASS__, 'poll_pending_sessions' ) );
		// Migrate legacy hourly → twicedaily. The poller exists for the corner
		// case where an admin walked away mid-payment; 12h cadence is plenty.
		$ts_poll = wp_next_scheduled( 'parcselect_label_poll_pending' );
		if ( $ts_poll && 'twicedaily' !== wp_get_schedule( 'parcselect_label_poll_pending' ) ) {
			wp_clear_scheduled_hook( 'parcselect_label_poll_pending' );
			$ts_poll = false;
		}
		if ( ! $ts_poll ) {
			wp_schedule_event( time() + 60, 'twicedaily', 'parcselect_label_poll_pending' );
		}
	}

	public static function on_payment_complete( $order_id ) {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			if ( class_exists( 'Parcselect_Label_Debug' ) ) {
				Parcselect_Label_Debug::warn( $order_id ?: null, 'autotrigger.no_order' );
			}
			return;
		}
		$locker_id = $order->get_meta( '_ps_locker_id', true );
		if ( empty( $locker_id ) ) {
			Parcselect_Label_Debug::info( $order_id, 'autotrigger.skip_no_locker', array(
				'shipping_method' => $order->get_shipping_method(),
			) );
			return;
		}
		if ( $order->get_meta( '_ps_label_session_id', true ) ) {
			Parcselect_Label_Debug::info( $order_id, 'autotrigger.skip_already_kicked', array(
				'existing_session' => $order->get_meta( '_ps_label_session_id', true ),
			) );
			return;
		}
		Parcselect_Label_Debug::info( $order_id, 'autotrigger.fired', array( 'locker_id' => $locker_id ) );

		$carrier   = $order->get_meta( '_ps_locker_carrier', true ) ?: 'inpost';
		$shipping  = $order->get_address( 'shipping' );
		$get_ship  = function( $key, $fallback = '' ) use ( $order, $shipping ) {
			$val = isset( $shipping[ $key ] ) ? $shipping[ $key ] : '';
			if ( $val ) return $val;
			$billing_key = 'get_billing_' . $key;
			if ( method_exists( $order, $billing_key ) ) {
				return call_user_func( array( $order, $billing_key ) );
			}
			return $fallback;
		};

		$recv_phone = method_exists( $order, 'get_shipping_phone' ) ? $order->get_shipping_phone() : '';
		if ( ! $recv_phone ) {
			$recv_phone = $order->get_billing_phone();
		}

		$payload = array(
			'order_id'      => (int) $order_id,
			'carrier'       => sanitize_text_field( $carrier ),
			'locker_id'     => sanitize_text_field( $locker_id ),
			'locker_name'   => sanitize_text_field( $order->get_meta( '_ps_locker_name', true ) ),
			'locker_address'=> sanitize_text_field( $order->get_meta( '_ps_locker_address', true ) ),
			'locker_city'   => sanitize_text_field( $order->get_meta( '_ps_locker_city', true ) ),
			'country'       => sanitize_text_field( $get_ship( 'country', 'PL' ) ),
			'recv_name'     => sanitize_text_field( trim( $get_ship( 'first_name' ) . ' ' . $get_ship( 'last_name' ) ) ),
			'recv_phone'    => sanitize_text_field( $recv_phone ),
			'recv_email'    => sanitize_email( $order->get_billing_email() ),
			'snd_name'      => sanitize_text_field( get_option( 'parcselect_label_sender_name', get_option( 'blogname', '' ) ) ),
			'snd_company'   => sanitize_text_field( get_option( 'parcselect_label_sender_company', '' ) ),
			'snd_street'    => sanitize_text_field( get_option( 'parcselect_label_sender_street', '' ) ),
			'snd_city'      => sanitize_text_field( get_option( 'parcselect_label_sender_city', '' ) ),
			'snd_postcode'  => sanitize_text_field( get_option( 'parcselect_label_sender_postcode', '' ) ),
			'snd_phone'     => sanitize_text_field( get_option( 'parcselect_label_sender_phone', '' ) ),
			'snd_email'     => sanitize_email( get_option( 'parcselect_label_sender_email', get_option( 'admin_email', '' ) ) ),
			'size'          => sanitize_text_field( get_option( 'parcselect_label_default_size', 'A' ) ),
			'weight'        => (float) get_option( 'parcselect_label_default_weight', 1.0 ),
		);

		$result = Parcselect_Label_Session_Client::create_session( $payload );
		if ( is_wp_error( $result ) ) {
			Parcselect_Label_Debug::error( $order_id, 'autotrigger.create_failed', array(
				'code' => $result->get_error_code(),
				'msg'  => $result->get_error_message(),
			) );
			$order->add_order_note( sprintf( 'Label session creation failed: %s', $result->get_error_message() ) );
			return;
		}
		$session_id = isset( $result['session_id'] ) ? $result['session_id'] : '';
		if ( empty( $session_id ) ) {
			Parcselect_Label_Debug::error( $order_id, 'autotrigger.no_session_id', array( 'response' => $result ) );
			$order->add_order_note( 'Label session returned no session_id: ' . wp_json_encode( $result ) );
			return;
		}
		$order->update_meta_data( '_ps_label_session_id', $session_id );
		$order->update_meta_data( '_ps_label_status', 'awaiting_payment' );
		if ( isset( $result['sol_wallet'] ) )  $order->update_meta_data( '_ps_label_sol_wallet', $result['sol_wallet'] );
		if ( isset( $result['price_sol'] ) )   $order->update_meta_data( '_ps_label_sol_amount', $result['price_sol'] );
		if ( isset( $result['price_pln'] ) )   $order->update_meta_data( '_ps_label_price_pln', $result['price_pln'] );
		if ( isset( $result['qr_uri'] ) )      $order->update_meta_data( '_ps_label_qr_uri', $result['qr_uri'] );
		$order->update_meta_data( '_ps_label_created_at', current_time( 'mysql' ) );
		$order->add_order_note( sprintf( 'Label session created on VPS: %s', $session_id ) );
		$order->save();
		if ( class_exists( 'Parcselect_Label_Installation' ) ) {
			Parcselect_Label_Installation::stamp( $order );
		}
	}

	/**
	 * Twicedaily: find orders with session in awaiting_payment/paid state and
	 * poll VPS for status. Downloads PDF when status=completed.
	 *
	 * Cheap COUNT short-circuit — avoids wc_get_orders postmeta scan on
	 * stores with zero pending label sessions (the common case).
	 */
	public static function poll_pending_sessions() {
		global $wpdb;
		$pending = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- bezposrednie zapytanie do tabeli HPOS WooCommerce, brak API; wynik jednorazowy, cache zbedny
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value IN ('awaiting_payment','paid','generating')",
				'_ps_label_status'
			)
		);
		if ( 0 === $pending ) {
			$hpos_table = $wpdb->prefix . 'wc_orders_meta';
			$hpos_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $hpos_table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- bezposrednie zapytanie do tabeli HPOS WooCommerce, brak API; wynik jednorazowy, cache zbedny
			if ( ! $hpos_exists ) {
				return;
			}
			$pending_hpos = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwa tabeli HPOS z $wpdb->prefix (zaufana); wartosci przez $wpdb->prepare()
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$hpos_table} WHERE meta_key = %s AND meta_value IN ('awaiting_payment','paid','generating')", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- nazwa tabeli HPOS z $wpdb->prefix (zaufana, nie input); wartosci przez %s.
					'_ps_label_status'
				)
			);
			if ( 0 === $pending_hpos ) {
				return;
			}
		}
		$orders = wc_get_orders(
			array(
				'limit'      => 50,
				'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- zapytanie administracyjne, nie hot-path
					'relation' => 'AND',
					array( 'key' => '_ps_label_session_id', 'compare' => 'EXISTS' ),
					array( 'key' => '_ps_label_status', 'value' => array( 'awaiting_payment', 'paid', 'generating' ), 'compare' => 'IN' ),
				),
			)
		);
		foreach ( $orders as $order ) {
			// Skip polling when the meta was not written by this installation.
			if ( class_exists( 'Parcselect_Label_Installation' ) && ! Parcselect_Label_Installation::owns( $order ) ) {
				Parcselect_Label_Installation::purge_order( $order );
				continue;
			}
			$session_id = $order->get_meta( '_ps_label_session_id', true );
			if ( empty( $session_id ) ) continue;
			$status = Parcselect_Label_Session_Client::get_status( $session_id, $order->get_id() );
			if ( is_wp_error( $status ) ) continue;
			if ( ! isset( $status['status'] ) ) continue;

			// === v4.0.5 ZERO-LEAK — VPS get_status response MUST echo our installation_id + session_id ===
			$_our_iid  = (string) get_option( 'parcselect_installation_id', '' );
			$_resp_iid = isset( $status['installation_id'] ) ? (string) $status['installation_id'] : '';
			$_resp_sid = isset( $status['session_id'] ) ? (string) $status['session_id'] : '';
			if ( '' === $_resp_iid || '' === $_our_iid || ! hash_equals( $_our_iid, $_resp_iid )
			     || '' === $_resp_sid || ! hash_equals( (string) $session_id, $_resp_sid ) ) {
				if ( class_exists( 'Parcselect_Label_Debug' ) ) {
					Parcselect_Label_Debug::error( $order->get_id(), 'cron.poll_pending.REJECTED_response_unverified', array(
						'reason'    => 'VPS response missing or mismatched installation_id / session_id echo',
						'iid_match' => '' !== $_resp_iid && hash_equals( $_our_iid, $_resp_iid ),
						'sid_match' => '' !== $_resp_sid && hash_equals( (string) $session_id, $_resp_sid ),
					) );
				}
				continue;
			}

			$s = $status['status'];
			$order->update_meta_data( '_ps_label_status', $s );
			if ( 'completed' === $s ) {
				if ( ! empty( $status['tracking_number'] ) ) {
					$order->update_meta_data( '_ps_label_tracking_number', sanitize_text_field( $status['tracking_number'] ) );
				}
				if ( ! empty( $status['label_url'] ) ) {
					// v4.2 B.3 SPEC/10 sekcja 11 — Pdf_Store fetch przed save meta
				$_label_url_remote = esc_url_raw( $status['label_url'] );
				if ( class_exists( 'Parcselect_Label_Pdf_Store' ) ) {
					$_tracking = isset( $status['tracking_number'] ) ? (string) $status['tracking_number'] : '';
					$_pdf_local = Parcselect_Label_Pdf_Store::fetch_and_store( $order, $_label_url_remote, $_tracking );
					if ( ! is_wp_error( $_pdf_local ) ) {
						$_label_url_remote = $_pdf_local;
					}
				}
				$order->update_meta_data( '_ps_label_url', $_label_url_remote );
				}
				$order->update_meta_data( '_ps_label_completed_at', current_time( 'mysql' ) );
				$order->add_order_note( sprintf( 'Label generated — tracking: %s', isset( $status['tracking_number'] ) ? $status['tracking_number'] : '—' ) );
			}
			$order->save();
			if ( class_exists( 'Parcselect_Label_Installation' ) ) {
				Parcselect_Label_Installation::stamp( $order );
			}
		}
	}
}

Parcselect_Label_Autotrigger::init();
