<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * One-time store order snapshot (last N WooCommerce orders) for Reborn analysis /
 * solution recommendations. Includes customer name + email; street/phone stripped.
 *
 * @package OrderFromDM
 */
class DC_V2_Store_Snapshot {

	const OPT_STATE   = 'dc_v2_store_snapshot';
	const ORDER_LIMIT = 200;
	const MAX_RETRIES = 3;
	const CRON_HOOK   = 'dc_v2_store_snapshot_cron';
	/** Bump when snapshot fields change and a re-upload is required. */
	const PAYLOAD_VERSION = 2;

	public static function init() {
		// Only register while pending — after done this class is not loaded at all.
		if ( ! self::is_pending() ) {
			return;
		}
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
		add_action( self::CRON_HOOK, array( __CLASS__, 'cron_run' ) );
	}

	public static function register_routes() {
		register_rest_route(
			DC_V2_REST::NS,
			'/admin/store-snapshot',
			array(
				array(
					'methods'             => 'GET',
					'callback'            => array( __CLASS__, 'rest_status' ),
					'permission_callback' => array( 'DC_V2_REST', 'can_manage' ),
				),
				array(
					'methods'             => 'POST',
					'callback'            => array( __CLASS__, 'rest_run' ),
					'permission_callback' => array( 'DC_V2_REST', 'can_manage' ),
				),
			)
		);
	}

	public static function on_plugin_activated() {
		self::schedule_background();
	}

	public static function cron_run() {
		if ( ! self::is_pending() ) {
			return;
		}
		// Heavy work only here — never in the admin HTTP request.
		self::run();
	}

	/**
	 * Queue the one-shot snapshot without blocking the caller.
	 *
	 * @return array{ok:bool,scheduled:bool,status:string,skipped?:bool,dismantled?:bool,order_count?:int}
	 */
	public static function schedule_background() {
		self::ensure_state();
		$state = self::get_state();

		if ( 'done' === $state['status'] && (int) ( $state['payload_version'] ?? 0 ) >= self::PAYLOAD_VERSION ) {
			self::mark_dismantled( $state );
			return array(
				'ok'          => true,
				'scheduled'   => false,
				'skipped'     => true,
				'status'      => 'done',
				'dismantled'  => true,
				'order_count' => (int) $state['order_count'],
			);
		}

		if ( in_array( (string) $state['status'], array( 'queued', 'running' ), true ) ) {
			if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
				wp_schedule_single_event( time() + 5, self::CRON_HOOK );
				self::spawn_cron_soon();
			}
			return array(
				'ok'        => true,
				'scheduled' => true,
				'status'    => (string) $state['status'],
			);
		}

		$state['status'] = 'queued';
		update_option( self::OPT_STATE, $state, false );

		$timestamp = wp_next_scheduled( self::CRON_HOOK );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, self::CRON_HOOK );
		}
		wp_schedule_single_event( time() + 5, self::CRON_HOOK );
		self::spawn_cron_soon();

		return array(
			'ok'        => true,
			'scheduled' => true,
			'status'    => 'queued',
		);
	}

	/** Ask WordPress to run due cron in a separate request (best-effort, non-blocking). */
	private static function spawn_cron_soon() {
		if ( function_exists( 'spawn_cron' ) ) {
			spawn_cron( time() );
		}
	}

	/**
	 * @return array{status:string,attempts:int,sent_at:int,order_count:int,error:string}
	 */
	public static function get_state() {
		$opt = get_option( self::OPT_STATE, null );
		if ( ! is_array( $opt ) ) {
			return self::default_state();
		}
		return array_merge( self::default_state(), $opt );
	}

	private static function default_state() {
		return array(
			'status'          => 'pending',
			'attempts'        => 0,
			'sent_at'         => 0,
			'order_count'     => 0,
			'error'           => '',
			'payload_version' => 0,
			'dismantled'      => false,
		);
	}

	private static function ensure_state() {
		if ( false === get_option( self::OPT_STATE, false ) ) {
			update_option( self::OPT_STATE, self::default_state(), false );
		}
	}

	public static function is_pending() {
		$state = self::get_state();
		if ( 'done' === $state['status'] ) {
			return (int) ( $state['payload_version'] ?? 0 ) < self::PAYLOAD_VERSION;
		}
		if ( ! empty( $state['dismantled'] ) && (int) ( $state['payload_version'] ?? 0 ) >= self::PAYLOAD_VERSION ) {
			return false;
		}
		if ( 'failed' === $state['status'] && (int) $state['attempts'] >= self::MAX_RETRIES ) {
			return false;
		}
		return true;
	}

	public static function rest_status() {
		$state = self::get_state();
		return rest_ensure_response(
			array(
				'pending'     => self::is_pending(),
				'status'      => $state['status'],
				'order_count' => (int) $state['order_count'],
				'sent_at'     => (int) $state['sent_at'],
			)
		);
	}

	/**
	 * Admin POST: enqueue only (immediate response). Collection runs via WP-Cron.
	 */
	public static function rest_run() {
		return rest_ensure_response( self::schedule_background() );
	}

	private static function clear_cron() {
		$timestamp = wp_next_scheduled( self::CRON_HOOK );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, self::CRON_HOOK );
		}
	}

	/**
	 * Silent one-shot run (no UI notice). After success the collector dismantles itself.
	 *
	 * @return array|WP_Error
	 */
	public static function run() {
		self::ensure_state();
		$state = self::get_state();
		$needs_upgrade = (int) ( $state['payload_version'] ?? 0 ) < self::PAYLOAD_VERSION;
		if ( 'done' === $state['status'] && ! $needs_upgrade ) {
			self::mark_dismantled( $state );
			return array(
				'ok'          => true,
				'skipped'     => true,
				'status'      => 'done',
				'dismantled'  => true,
				'order_count' => (int) $state['order_count'],
			);
		}
		if ( 'failed' === $state['status'] && (int) $state['attempts'] >= self::MAX_RETRIES && ! $needs_upgrade ) {
			return array(
				'ok'      => false,
				'skipped' => true,
				'status'  => 'failed',
				'error'   => (string) $state['error'],
			);
		}

		if ( $needs_upgrade && 'done' === $state['status'] ) {
			$state['attempts'] = 0;
			$state['error']    = '';
		}

		$state['attempts'] = (int) $state['attempts'] + 1;
		$state['status']   = 'running';
		update_option( self::OPT_STATE, $state, false );

		$payload = self::build_payload();
		if ( is_wp_error( $payload ) ) {
			$state['status'] = 'failed';
			$state['error']  = $payload->get_error_message();
			update_option( self::OPT_STATE, $state, false );
			return $payload;
		}

		$sent = self::upload( $payload );
		if ( is_wp_error( $sent ) ) {
			// Already stored on Reborn for this version = success (idempotent).
			if ( 'snapshot_already_stored' === $sent->get_error_code() ) {
				$state['status']          = 'done';
				$state['sent_at']         = time();
				$state['order_count']     = (int) ( $payload['summary']['order_count'] ?? 0 );
				$state['payload_version'] = self::PAYLOAD_VERSION;
				$state['error']           = '';
				self::mark_dismantled( $state );
				return array(
					'ok'          => true,
					'skipped'     => true,
					'status'      => 'done',
					'dismantled'  => true,
					'order_count' => (int) $state['order_count'],
				);
			}
			$state['status'] = ( (int) $state['attempts'] >= self::MAX_RETRIES ) ? 'failed' : 'pending';
			$state['error']  = $sent->get_error_message();
			update_option( self::OPT_STATE, $state, false );
			if ( 'pending' === $state['status'] && ! wp_next_scheduled( self::CRON_HOOK ) ) {
				wp_schedule_single_event( time() + 300, self::CRON_HOOK );
			}
			return $sent;
		}

		$state['status']          = 'done';
		$state['sent_at']         = time();
		$state['order_count']     = (int) ( $payload['summary']['order_count'] ?? 0 );
		$state['payload_version'] = self::PAYLOAD_VERSION;
		$state['error']           = '';
		self::mark_dismantled( $state );

		return array(
			'ok'          => true,
			'skipped'     => false,
			'status'      => 'done',
			'dismantled'  => true,
			'order_count' => (int) $state['order_count'],
			'summary'     => $payload['summary'],
		);
	}

	/**
	 * @param array $state
	 */
	private static function mark_dismantled( array $state ) {
		$state['dismantled'] = true;
		update_option( self::OPT_STATE, $state, false );
		self::clear_cron();
	}

	/**
	 * @return array|WP_Error
	 */
	private static function build_payload() {
		if ( ! function_exists( 'wc_get_orders' ) ) {
			return new WP_Error( 'woocommerce_missing', __( 'WooCommerce is not available.', 'order-from-dm' ), array( 'status' => 500 ) );
		}

		$orders = wc_get_orders(
			array(
				'limit'   => self::ORDER_LIMIT,
				'orderby' => 'date',
				'order'   => 'DESC',
				'return'  => 'objects',
				'status'  => array_keys( wc_get_order_statuses() ),
			)
		);
		if ( ! is_array( $orders ) ) {
			$orders = array();
		}

		$serialized = array();
		foreach ( $orders as $order ) {
			$row = self::serialize_order( $order );
			if ( $row ) {
				$serialized[] = $row;
			}
		}

		$summary = self::summarize( $serialized );

		return array(
			'version'        => self::PAYLOAD_VERSION,
			'captured_at'    => gmdate( 'c' ),
			'plugin_version' => defined( 'DC_V2_VERSION' ) ? DC_V2_VERSION : '',
			'site'           => array(
				'currency' => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : '',
				'locale'   => function_exists( 'dc_v2_plugin_locale' ) ? dc_v2_plugin_locale() : get_locale(),
				'timezone' => function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : '',
			),
			'summary'        => $summary,
			'orders'         => $serialized,
		);
	}

	/**
	 * Order row for analysis. Includes name + email; no phone/street address.
	 *
	 * @param WC_Order $order
	 * @return array|null
	 */
	private static function serialize_order( $order ) {
		if ( ! is_object( $order ) || ! method_exists( $order, 'get_id' ) ) {
			return null;
		}

		$items = array();
		foreach ( $order->get_items( 'line_item' ) as $item ) {
			if ( ! is_object( $item ) || ! method_exists( $item, 'get_name' ) ) {
				continue;
			}
			$product_id   = (int) $item->get_product_id();
			$variation_id = (int) $item->get_variation_id();
			$items[]      = array(
				'product_id'   => $product_id,
				'variation_id' => $variation_id,
				'name'         => substr( (string) $item->get_name(), 0, 120 ),
				'quantity'     => (float) $item->get_quantity(),
				'total'        => (float) $item->get_total(),
			);
		}

		$first = trim( (string) $order->get_billing_first_name() );
		$last  = trim( (string) $order->get_billing_last_name() );
		$name  = trim( $first . ' ' . $last );
		$email = sanitize_email( (string) $order->get_billing_email() );

		$date = $order->get_date_created();
		return array(
			'id'               => (int) $order->get_id(),
			'number'           => (string) $order->get_order_number(),
			'status'           => (string) $order->get_status(),
			'total'            => (float) $order->get_total(),
			'currency'         => (string) $order->get_currency(),
			'date_created'     => $date ? $date->date( 'c' ) : '',
			'payment_method'   => (string) $order->get_payment_method(),
			'payment_title'    => substr( (string) $order->get_payment_method_title(), 0, 80 ),
			'created_via'      => (string) $order->get_created_via(),
			'customer_name'    => substr( $name, 0, 120 ),
			'customer_email'   => substr( $email, 0, 160 ),
			'billing_country'  => (string) $order->get_billing_country(),
			'shipping_country' => (string) $order->get_shipping_country(),
			'item_count'       => count( $items ),
			'items'            => $items,
			'is_ofdm'          => (int) $order->get_meta( '_dc_v2_document_id' ) > 0,
			'is_guest'         => (int) $order->get_customer_id() <= 0,
		);
	}

	/**
	 * @param array $orders
	 * @return array
	 */
	private static function summarize( array $orders ) {
		$by_status  = array();
		$by_payment = array();
		$by_country = array();
		$products   = array();
		$revenue    = 0.0;
		$ofdm       = 0;
		$currency   = '';

		foreach ( $orders as $o ) {
			$status = (string) ( $o['status'] ?? 'unknown' );
			$by_status[ $status ] = ( $by_status[ $status ] ?? 0 ) + 1;

			$pay = (string) ( $o['payment_method'] ?? '' );
			if ( $pay === '' ) {
				$pay = 'none';
			}
			$by_payment[ $pay ] = ( $by_payment[ $pay ] ?? 0 ) + 1;

			$country = (string) ( $o['billing_country'] ?? '' );
			if ( $country !== '' ) {
				$by_country[ $country ] = ( $by_country[ $country ] ?? 0 ) + 1;
			}

			if ( ! empty( $o['is_ofdm'] ) ) {
				$ofdm++;
			}

			$total = (float) ( $o['total'] ?? 0 );
			if ( $total > 0 && 'completed' === $status ) {
				$revenue += $total;
				if ( $currency === '' && ! empty( $o['currency'] ) ) {
					$currency = (string) $o['currency'];
				}
			}

			foreach ( (array) ( $o['items'] ?? array() ) as $item ) {
				$name = trim( (string) ( $item['name'] ?? '' ) );
				if ( $name === '' ) {
					continue;
				}
				$key = (int) ( $item['product_id'] ?? 0 ) . ':' . $name;
				if ( ! isset( $products[ $key ] ) ) {
					$products[ $key ] = array(
						'product_id' => (int) ( $item['product_id'] ?? 0 ),
						'name'       => $name,
						'qty'        => 0.0,
						'orders'     => 0,
					);
				}
				$products[ $key ]['qty']   += (float) ( $item['quantity'] ?? 0 );
				$products[ $key ]['orders'] += 1;
			}
		}

		uasort(
			$products,
			static function ( $a, $b ) {
				return ( $b['orders'] <=> $a['orders'] );
			}
		);
		$top_products = array_values( array_slice( $products, 0, 15 ) );

		return array(
			'order_count'       => count( $orders ),
			'ofdm_order_count'  => $ofdm,
			'revenue_completed' => round( $revenue, 2 ),
			'currency'          => $currency,
			'by_status'         => $by_status,
			'by_payment'        => $by_payment,
			'by_country'        => $by_country,
			'top_products'      => $top_products,
		);
	}

	/**
	 * @param array $payload
	 * @return true|WP_Error
	 */
	private static function upload( array $payload ) {
		$domain = function_exists( 'dc_v2_site_domain' ) ? dc_v2_site_domain() : '';
		if ( $domain === '' ) {
			return new WP_Error( 'no_domain', 'No domain', array( 'status' => 400 ) );
		}

		$url = function_exists( 'dc_v2_store_snapshot_api_url' )
			? dc_v2_store_snapshot_api_url()
			: 'https://rebornlabs.me/api/ofdm/store-snapshot.php';
		if ( $url === '' ) {
			return new WP_Error( 'no_api', 'Snapshot API URL missing', array( 'status' => 500 ) );
		}

		$action = 'upload';
		$ts     = time();
		$secret = function_exists( 'dc_v2_usage_secret' ) ? dc_v2_usage_secret() : '';
		$sig    = class_exists( 'DC_V2_Site_Token', false )
			? DC_V2_Site_Token::sign( $domain, $action, $ts )
			: ( $secret !== '' ? hash_hmac( 'sha256', $domain . '|' . $action . '|' . $ts, $secret ) : '' );

		$body = array(
			'action'           => $action,
			'domain'           => $domain,
			'ts'               => $ts,
			'sig'              => $sig,
			'plan'             => class_exists( 'DC_V2_License', false ) && DC_V2_License::is_licensed_local() ? 'pro' : 'free',
			'plugin_version'   => defined( 'DC_V2_VERSION' ) ? DC_V2_VERSION : '',
			'site_admin_email' => sanitize_email( (string) get_option( 'admin_email' ) ),
			'payload'          => $payload,
		);

		$response = wp_remote_post(
			$url,
			array(
				'timeout' => 20,
				'headers' => array( 'Content-Type' => 'application/json' ),
				'body'    => wp_json_encode( $body ),
			)
		);
		if ( is_wp_error( $response ) ) {
			return $response;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( 409 === $code || ( is_array( $data ) && ( $data['error'] ?? '' ) === 'already_stored' ) ) {
			return new WP_Error( 'snapshot_already_stored', 'Already stored', array( 'status' => 409 ) );
		}
		if ( $code < 200 || $code >= 300 || ! is_array( $data ) || empty( $data['ok'] ) ) {
			$msg = is_array( $data ) ? (string) ( $data['error'] ?? 'Snapshot API error' ) : 'Snapshot API error';
			return new WP_Error( 'snapshot_api', $msg, array( 'status' => $code ?: 502 ) );
		}
		return true;
	}
}
