<?php

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

class AIEO_Carts_Capture {

	const CRON_HOOK  = 'aieo_carts_capture_abandoned';
	const LAST_OPT   = 'aieo_carts_last_capture';
	const STALE_DAYS = 14; 

	
	private static $instance = null;

	
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	
	public function register_hooks() {
		add_action( 'plugins_loaded', array( $this, 'maybe_schedule' ), 28 );
		add_action( self::CRON_HOOK, array( $this, 'run_capture' ) );
	}

	
	public function maybe_schedule() {
		if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
			wp_schedule_event( time() + 600, 'hourly', self::CRON_HOOK );
		}
	}

	
	public static function orders_table() {
		global $wpdb;
		return $wpdb->prefix . 'wc_orders';
	}

	
	private static function hpos_enabled() {
		return AIEO_Carts_Store::table_exists( self::orders_table() );
	}

	
	public function run_capture( $capture_limit = 300, $resolve_limit = 800, $force = false ) {
		
		
		
		if ( ! $force && '1' !== (string) get_option( 'aieo_carts_capture_enabled', '1' ) ) {
			return array(
				'captured' => 0,
				'resolved' => 0,
			);
		}
		if ( ! class_exists( 'AIEO_Carts_Store' ) || ! function_exists( 'wc_get_order' ) ) {
			return array(
				'captured' => 0,
				'resolved' => 0,
			);
		}
		if ( class_exists( 'AIEO_Carts' ) ) {
			AIEO_Carts::maybe_install();
		}
		if ( ! AIEO_Carts_Store::table_exists( AIEO_Carts_Store::carts_table() ) ) {
			return array(
				'captured' => 0,
				'resolved' => 0,
			);
		}

		$restocked = $this->restocked_product_ids();
		$captured  = $this->capture_new_drafts( (int) $capture_limit, $restocked );
		$resolved  = $this->resolve_outcomes( (int) $resolve_limit );

		update_option(
			self::LAST_OPT,
			array(
				'time'     => current_time( 'mysql', true ),
				'captured' => $captured,
				'resolved' => $resolved,
			),
			false
		);

		return array(
			'captured' => $captured,
			'resolved' => $resolved,
		);
	}

	
	private function capture_new_drafts( $limit, array $restocked ) {
		global $wpdb;
		$carts = AIEO_Carts_Store::carts_table();

		if ( self::hpos_enabled() ) {
			$o    = self::orders_table();
			$rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT o.id
					 FROM `{$o}` o
					 LEFT JOIN `{$carts}` c ON c.wc_order_id = o.id
					 WHERE o.type = 'shop_order'
					   AND o.status = 'wc-checkout-draft'
					   AND c.id IS NULL
					 ORDER BY o.id DESC
					 LIMIT %d",
					$limit
				)
			);
		} else {
			$p    = $wpdb->posts;
			$rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT p.ID AS id
					 FROM `{$p}` p
					 LEFT JOIN `{$carts}` c ON c.wc_order_id = p.ID
					 WHERE p.post_type = 'shop_order'
					   AND p.post_status = 'wc-checkout-draft'
					   AND c.id IS NULL
					 ORDER BY p.ID DESC
					 LIMIT %d",
					$limit
				)
			);
		}

		if ( empty( $rows ) ) {
			return 0;
		}

		$now = current_time( 'mysql', true );
		$n   = 0;

		foreach ( $rows as $r ) {
			$order = wc_get_order( (int) $r->id );
			if ( ! $order ) {
				continue;
			}

			$lines       = array();
			$items_blob  = array();
			$has_restock = false;
			$count       = 0;
			$subtotal    = 0.0;

			foreach ( $order->get_items() as $item ) {
				if ( ! is_a( $item, 'WC_Order_Item_Product' ) ) {
					continue;
				}
				$pid        = (int) $item->get_product_id();
				$vid        = (int) $item->get_variation_id();
				$qty        = (int) $item->get_quantity();
				$line_total = (float) $item->get_total() + (float) $item->get_total_tax();
				$product    = $item->get_product();
				$key        = $vid ? $vid : $pid;
				$is_r       = isset( $restocked[ $key ] ) || isset( $restocked[ $pid ] );

				if ( $is_r ) {
					$has_restock = true;
				}
				$count    += $qty;
				$subtotal += $line_total;

				$lines[]      = array(
					'product_id'   => $pid,
					'variation_id' => $vid,
					'sku'          => $product ? (string) $product->get_sku() : '',
					'name'         => (string) $item->get_name(),
					'quantity'     => $qty,
					'line_total'   => $line_total,
					'is_restocked' => $is_r ? 1 : 0,
				);
				$items_blob[] = array(
					'product_id'   => $pid,
					'variation_id' => $vid,
					'quantity'     => $qty,
				);
			}

			if ( empty( $lines ) ) {
				continue; 
			}

			$created    = $order->get_date_created();
			$created_at = $created ? gmdate( 'Y-m-d H:i:s', $created->getTimestamp() ) : $now;

			AIEO_Carts_Store::insert_cart(
				array(
					'token'              => null,
					'kind'               => 'abandoned',
					'source'             => 'checkout_draft',
					'wc_order_id'        => (int) $r->id,
					'user_id'            => (int) $order->get_customer_id(),
					'customer_email'     => (string) $order->get_billing_email(),
					'name'               => null,
					'items'              => $items_blob,
					'coupons'            => $order->get_coupon_codes(),
					'currency'           => $order->get_currency(),
					'item_count'         => $count,
					'subtotal'           => $subtotal,
					'total'              => (float) $order->get_total(),
					'status'             => 'abandoned',
					'outcome'            => 'open',
					'contains_restocked' => $has_restock ? 1 : 0,
					'created_at'         => $created_at,
					'last_seen_at'       => $now,
				),
				$lines
			);
			$n++;
		}

		return $n;
	}

	
	private function resolve_outcomes( $limit ) {
		global $wpdb;
		$carts = AIEO_Carts_Store::carts_table();

		$open = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, wc_order_id, created_at
				 FROM `{$carts}`
				 WHERE kind = 'abandoned' AND outcome = 'open' AND wc_order_id IS NOT NULL
				 ORDER BY id ASC
				 LIMIT %d",
				$limit
			)
		);
		if ( empty( $open ) ) {
			return 0;
		}

		$statuses  = $this->order_statuses( wp_list_pluck( $open, 'wc_order_id' ) );
		$now       = current_time( 'mysql', true );
		$stale_cut = gmdate( 'Y-m-d H:i:s', time() - ( self::STALE_DAYS * DAY_IN_SECONDS ) );
		$drafts    = array( 'wc-checkout-draft', 'auto-draft' );
		$lost      = array( 'trash', 'wc-failed', 'wc-cancelled' );
		$n         = 0;

		foreach ( $open as $row ) {
			$oid = (int) $row->wc_order_id;
			$upd = array( 'last_seen_at' => $now );

			if ( ! isset( $statuses[ $oid ] ) ) {
				
				$upd['outcome'] = 'expired';
			} else {
				$st = $statuses[ $oid ];
				if ( in_array( $st, $drafts, true ) ) {
					if ( ! empty( $row->created_at ) && $row->created_at < $stale_cut ) {
						$upd['outcome'] = 'expired';
					}
				} elseif ( in_array( $st, $lost, true ) ) {
					$upd['outcome'] = 'expired';
				} else {
					
					$upd['outcome']            = 'converted';
					$upd['converted_order_id'] = $oid;
					$upd['converted_at']       = $now;
					$ord                       = wc_get_order( $oid );
					if ( $ord ) {
						$upd['total'] = (float) $ord->get_total();
					}
				}
			}

			$wpdb->update( $carts, $upd, array( 'id' => (int) $row->id ) );
			$n++;
		}

		return $n;
	}

	
	private function order_statuses( array $ids ) {
		global $wpdb;
		$ids = array_values( array_unique( array_filter( array_map( 'intval', $ids ) ) ) );
		if ( empty( $ids ) ) {
			return array();
		}

		$in  = implode( ',', $ids );
		$map = array();

		if ( self::hpos_enabled() ) {
			$o   = self::orders_table();
			$res = $wpdb->get_results( "SELECT id, status FROM `{$o}` WHERE id IN ({$in})" );
		} else {
			$p   = $wpdb->posts;
			$res = $wpdb->get_results( "SELECT ID AS id, post_status AS status FROM `{$p}` WHERE ID IN ({$in})" );
		}

		foreach ( (array) $res as $r ) {
			$map[ (int) $r->id ] = (string) $r->status;
		}
		return $map;
	}

	
	private function restocked_product_ids() {
		global $wpdb;
		$events = $wpdb->prefix . 'aieo_dmm_stock_recovery_events';
		if ( ! AIEO_Carts_Store::table_exists( $events ) ) {
			return array();
		}
		$ids = $wpdb->get_col( "SELECT DISTINCT product_id FROM `{$events}` WHERE reverted_at IS NULL" );
		$map = array();
		foreach ( (array) $ids as $id ) {
			$map[ (int) $id ] = true;
		}
		return $map;
	}
}
