<?php

namespace Dobare\Abandoned_Cart;

use Dobare\Utils\Dobare_Utils;

class Dobare_Abandoned_Cart_Service
{
	private static ?self $instance = null;
	private bool $bootstrapped = false;
	private const SESSION_HASH_KEY = 'dobare_last_abandoned_cart_hash';
	private const RESTORE_TTL = WEEK_IN_SECONDS;
	private const ORDER_META_CART_HASH = '_dobare_abandoned_cart_hash';

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

		return self::$instance;
	}

	public function bootstrap(): void
	{
		if ($this->bootstrapped) {
			return;
		}

		$this->bootstrapped = true;

		Dobare_Abandoned_Cart_Migrator::maybe_migrate();
		Dobare_Abandoned_Cart_Guard::seed_defaults();
		add_action('init', array($this, 'register_runtime_hooks'));
		Dobare_Abandoned_Cart_Scheduler::instance()->register_hooks();
	}

	public function register_runtime_hooks(): void
	{
		add_action('woocommerce_cart_updated', array($this, 'maybe_handle_cart_mutation'), 40);
		add_action('woocommerce_after_calculate_totals', array($this, 'maybe_handle_cart_mutation'), 40);
		add_action('template_redirect', array($this, 'maybe_handle_template_redirect'), 1);
		add_action('woocommerce_checkout_create_order', array($this, 'maybe_attach_cart_hash_to_order'), 10, 2);
		add_action('woocommerce_checkout_order_processed', array($this, 'handle_order_processed'), 10, 3);
		add_action('woocommerce_payment_complete', array($this, 'handle_order_payment_complete'), 10, 1);
		add_action('woocommerce_order_status_processing', array($this, 'handle_order_status_transition'), 10, 1);
		add_action('woocommerce_order_status_completed', array($this, 'handle_order_status_transition'), 10, 1);
		add_action('woocommerce_order_status_on-hold', array($this, 'handle_order_status_transition'), 10, 1);
	}

	public function maybe_handle_cart_mutation(): void
	{
		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			$this->teardown_current_session_cart();
			return;
		}

		if (! Dobare_Abandoned_Cart_Guard::has_valid_session_phone()) {
			$this->teardown_current_session_cart();
			return;
		}

		$phone = Dobare_Abandoned_Cart_Guard::current_phone();
		if (empty($phone)) {
			$this->teardown_current_session_cart();
			return;
		}

		if (! function_exists('WC')) {
			return;
		}

		$cart = WC()->cart;
		if (! $cart) {
			return;
		}

		$cart_items = $cart->get_cart();
		$item_count = is_array($cart_items) ? count($cart_items) : 0;

		if ($item_count === 0) {
			$cart_hash = $this->get_session_cart_hash();

			if ($cart_hash) {
				Dobare_Abandoned_Cart_Scheduler::instance()->cancel_events_for_hash($cart_hash);

				global $wpdb;
				$wpdb->delete($this->get_table_name(), array('cart_hash' => $cart_hash));
			}

			$this->set_session_cart_hash(null);
			return;
		}

		$cart_hash = $this->get_session_cart_hash() ?? $this->generate_cart_hash();
		$this->set_session_cart_hash($cart_hash);

		if ($this->is_cart_abandoned($cart_hash)) {
			return;
		}

		$extra_details = $this->build_extra_details($cart_hash, $phone, $cart);
		$this->persist_active_cart($cart_hash, $phone, $extra_details);

		Dobare_Abandoned_Cart_Scheduler::instance()->schedule_abandonment_check(
			$cart_hash,
			Dobare_Abandoned_Cart_Guard::get_delay_minutes()
		);
	}

	public function maybe_handle_template_redirect(): void
	{
		if ($this->has_restore_query()) {
			$this->process_restore_request();

			return;
		}

		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		if (! Dobare_Abandoned_Cart_Guard::has_valid_session_phone()) {
			// Guard is responsible for suppressing all abandoned-cart behaviour when there is no phone.
			return;
		}

		// Abandoned notice removed per requirements.
		return;
	}

	public function maybe_attach_cart_hash_to_order(\WC_Order $order, array $data): void
	{
		unset($data);

		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		$cart_hash = $this->get_session_cart_hash();
		if (! $cart_hash) {
			return;
		}

		$existing_hash = $this->get_order_cart_hash($order);
		if ($existing_hash === $cart_hash) {
			return;
		}

		$order->update_meta_data(self::ORDER_META_CART_HASH, $cart_hash);
	}

	public function handle_order_processed(int $order_id, array $posted_data, \WC_Order $order): void
	{
		unset($order_id, $posted_data);

		$this->cleanup_cart_for_order($order, 'order_processed');
	}

	public function handle_order_payment_complete(int $order_id): void
	{
		if (! function_exists('wc_get_order')) {
			return;
		}

		$order = wc_get_order($order_id);
		if (! $order instanceof \WC_Order) {
			return;
		}

		$this->cleanup_cart_for_order($order, 'payment_complete');
	}

	public function handle_order_status_transition(int $order_id): void
	{
		if (! function_exists('wc_get_order')) {
			return;
		}

		$order = wc_get_order($order_id);
		if (! $order instanceof \WC_Order) {
			return;
		}

		$this->cleanup_cart_for_order($order, 'status_transition');
	}

	private function teardown_current_session_cart(): void
	{
		$cart_hash = $this->get_session_cart_hash();
		if (! $cart_hash) {
			return;
		}

		Dobare_Abandoned_Cart_Scheduler::instance()->cancel_events_for_hash($cart_hash);

		global $wpdb;
		$wpdb->delete($this->get_table_name(), array('cart_hash' => $cart_hash));

		$this->set_session_cart_hash(null);
	}

	private function persist_active_cart(string $cart_hash, string $phone, array $extra_details): void
	{
		global $wpdb;

		$table = $this->get_table_name();
		$now = gmdate('Y-m-d H:i:s');
		$serialized_details = wp_json_encode($extra_details, JSON_UNESCAPED_UNICODE);

		$data = array(
			'user_phone'     => $phone,
			'extra_details'  => $serialized_details,
			'status'         => 'active',
			'last_active_at' => $now,
			'abandoned_at'   => null,
			'last_error'     => null,
		);

		$where = array(
			'cart_hash' => $cart_hash,
			'status'    => 'active',
		);

		$updated = $wpdb->update($table, $data, $where);

		if ($updated === false || $updated === 0) {
			$wpdb->replace(
				$table,
				array_merge(
					$data,
					array(
						'cart_hash' => $cart_hash,
					)
				)
			);
		}
	}

	private function build_extra_details(string $cart_hash, string $phone, \WC_Cart $cart): array
	{
		$items = array();
		foreach ($cart->get_cart() as $cart_item) {
			$product = isset($cart_item['data']) ? $cart_item['data'] : null;
			$items[] = array(
				'product_id'   => isset($cart_item['product_id']) ? (int) $cart_item['product_id'] : 0,
				'variation_id' => isset($cart_item['variation_id']) ? (int) $cart_item['variation_id'] : 0,
				'name'         => $product && method_exists($product, 'get_name') ? $product->get_name() : '',
				'qty'          => isset($cart_item['quantity']) ? (float) $cart_item['quantity'] : 0,
				'line_subtotal'=> isset($cart_item['line_subtotal']) ? (float) $cart_item['line_subtotal'] : 0,
				'line_total'   => isset($cart_item['line_total']) ? (float) $cart_item['line_total'] : 0,
				'variation'    => isset($cart_item['variation']) && is_array($cart_item['variation']) ? $cart_item['variation'] : array(),
				'cart_item_data' => $this->extract_cart_item_data($cart_item),
			);
		}

		$extra_details = array(
			'items'        => $items,
			'coupon_codes' => $cart->get_applied_coupons(),
			'totals'       => array(
				'subtotal' => (float) $cart->get_subtotal(),
				'total'    => (float) $cart->get_total('edit'),
				'currency' => get_woocommerce_currency(),
			),
			'restore'      => $this->generate_restore_payload($cart_hash),
			'source'       => array(
				'business_id'   => $this->get_business_id(),
				'site_url'      => get_site_url(),
				'plugin_version'=> defined('DOBARE_VERSION') ? DOBARE_VERSION : null,
			),
			'timestamps'   => array(
				'last_active_at' => gmdate('c'),
				'abandoned_at'   => null,
			),
		);

		return $extra_details;
	}

	private function generate_cart_hash(): string
	{
		if (! function_exists('WC')) {
			return sha1(uniqid('dobare_cart_', true));
		}

		$session = WC()->session;
		$session_key = $session ? (string) $session->get_customer_id() : '';
		$user_id = get_current_user_id();
		$phone = Dobare_Abandoned_Cart_Guard::current_phone() ?? '';

		$source = implode('|', array($phone, $session_key, (string) $user_id));

		return sha1($source);
	}

	private function generate_restore_payload(string $cart_hash): array
	{
		$expires_at = time() + self::RESTORE_TTL;
		$token = hash_hmac('sha256', $cart_hash . '|' . $expires_at, wp_salt('dobare_abandoned_cart'));

		$url = add_query_arg(
			array(
				'dobare_cart'  => $cart_hash,
				'dobare_token' => $token,
				'dobare_exp'   => $expires_at,
			),
			home_url('/')
		);

		return array(
			'url'        => $url,
			'token'      => $token,
			'expires_at' => gmdate('c', $expires_at),
		);
	}

	private function get_business_id(): ?string
	{
		$cached = get_transient('dobare_abandoned_cart_business_id');
		if ($cached !== false) {
			return $cached ?: null;
		}

		$business_id = Dobare_Utils::get_dob_business_id();
		set_transient('dobare_abandoned_cart_business_id', $business_id, HOUR_IN_SECONDS);

		return $business_id ?: null;
	}

	private function get_table_name(): string
	{
		global $wpdb;

		return $wpdb->prefix . 'dobare_abandoned_carts';
	}

	private function is_cart_abandoned(string $cart_hash): bool
	{
		global $wpdb;

		$table = $this->get_table_name();
		$status = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT status FROM {$table} WHERE cart_hash = %s",
				$cart_hash
			)
		);

		return $status === 'abandoned';
	}

	private function set_session_cart_hash(?string $hash): void
	{
		if (! function_exists('WC')) {
			return;
		}

		$session = WC()->session;
		if (! $session) {
			return;
		}

		$session->set(self::SESSION_HASH_KEY, $hash);
	}

	private function get_session_cart_hash(): ?string
	{
		if (! function_exists('WC')) {
			return null;
		}

		$session = WC()->session;
		if (! $session) {
			return null;
		}

		$value = $session->get(self::SESSION_HASH_KEY);

		return is_string($value) && $value !== '' ? $value : null;
	}

	public function get_recent_rows(int $limit = 50): array
	{
		global $wpdb;

		$limit = max(1, min(200, $limit));
		$table = $this->get_table_name();
		// Return only the latest row per phone (latest activity wins) to avoid confusion from historical hashes.
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"
				SELECT c.cart_hash, c.user_phone, c.status, c.last_active_at, c.abandoned_at, c.last_error
				FROM {$table} c
				WHERE c.last_active_at = (
					SELECT MAX(c2.last_active_at)
					FROM {$table} c2
					WHERE c2.user_phone = c.user_phone
				)
				ORDER BY c.last_active_at DESC
				LIMIT %d
				",
				$limit
			),
			ARRAY_A
		);

		if (! is_array($rows)) {
			return array();
		}

		return array_map(
			static function ($row) {
				return array(
					'cart_hash'     => $row['cart_hash'],
					'user_phone'    => $row['user_phone'],
					'status'        => $row['status'],
					'last_active_at'=> $row['last_active_at'] ? gmdate('c', strtotime($row['last_active_at'])) : null,
					'abandoned_at'  => $row['abandoned_at'] ? gmdate('c', strtotime($row['abandoned_at'])) : null,
					'last_error'    => $row['last_error'],
				);
			},
			$rows
		);
	}

	public function purge_all_rows(): int
	{
		global $wpdb;

		$table = $this->get_table_name();
		$hashes = $wpdb->get_col("SELECT cart_hash FROM {$table}");

		if (is_array($hashes)) {
			foreach ($hashes as $hash) {
				Dobare_Abandoned_Cart_Scheduler::instance()->cancel_events_for_hash($hash);
			}
		}

		$deleted = $wpdb->query("DELETE FROM {$table}");

		$this->set_session_cart_hash(null);

		return $deleted === false ? 0 : (int) $deleted;
	}

	private function has_restore_query(): bool
	{
		return isset($_GET['dobare_cart'], $_GET['dobare_token'], $_GET['dobare_exp']);
	}

	private function process_restore_request(): void
	{
		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		$params = $this->parse_restore_params();
		if (! $params) {
			return;
		}

		$restored = $this->restore_cart_from_snapshot($params);

		$target = function_exists('wc_get_cart_url') ? wc_get_cart_url() : home_url('/');
		$target = remove_query_arg(array('dobare_cart', 'dobare_token', 'dobare_exp'), $target);

		if (function_exists('wc_add_notice')) {
			if ($restored) {
				wc_add_notice(__('سبد خرید شما با موفقیت بازیابی شد.', 'dobare'), 'success');
			} else {
				wc_add_notice(__('متاسفیم، لینک بازگردانی معتبر نیست یا منقضی شده است.', 'dobare'), 'error');
			}
		}

		wp_safe_redirect($target);
		exit;
	}

	private function parse_restore_params(): ?array
	{
		$cart_hash = isset($_GET['dobare_cart']) ? sanitize_text_field(wp_unslash($_GET['dobare_cart'])) : '';
		$token = isset($_GET['dobare_token']) ? sanitize_text_field(wp_unslash($_GET['dobare_token'])) : '';
		$expires = isset($_GET['dobare_exp']) ? absint($_GET['dobare_exp']) : 0;

		if ($cart_hash === '' || $token === '' || $expires <= 0) {
			return null;
		}

		return array(
			'cart_hash' => $cart_hash,
			'token'     => $token,
			'expires'   => $expires,
		);
	}

	private function restore_cart_from_snapshot(array $params): bool
	{
		$row = $this->find_cart_row($params['cart_hash']);
		if (! $row || $row['status'] !== 'abandoned') {
			return false;
		}

		$details = $this->decode_extra_details($row['extra_details']);
		$restore_meta = isset($details['restore']) && is_array($details['restore']) ? $details['restore'] : array();

		$stored_token = isset($restore_meta['token']) ? (string) $restore_meta['token'] : '';
		$stored_expiry = isset($restore_meta['expires_at']) ? strtotime($restore_meta['expires_at']) : 0;

		if ($stored_token === '' || $stored_token !== $params['token']) {
			return false;
		}

		if ($stored_expiry <= time() || $params['expires'] <= time()) {
			return false;
		}

		$this->seed_session_phone($row['user_phone']);
		$this->hydrate_cart($details['items'] ?? array());

		// Cancel any pending events tied to the abandoned hash.
		Dobare_Abandoned_Cart_Scheduler::instance()->cancel_events_for_hash($row['cart_hash']);

		// Promote to a fresh active snapshot with a new hash to avoid reviving the abandoned row.
		$new_hash = $this->generate_new_cart_hash_from_restore($row['cart_hash']);
		$this->set_session_cart_hash($new_hash);

		if (function_exists('WC') && WC()->cart) {
			$new_details = $this->build_extra_details($new_hash, $row['user_phone'], WC()->cart);
			$this->persist_active_cart($new_hash, $row['user_phone'], $new_details);
			Dobare_Abandoned_Cart_Scheduler::instance()->schedule_abandonment_check(
				$new_hash,
				Dobare_Abandoned_Cart_Guard::get_delay_minutes()
			);

			// Remove the abandoned row that was restored so admin views do not show stale abandoned carts.
			global $wpdb;
			$wpdb->delete($this->get_table_name(), array('cart_hash' => $params['cart_hash']));
		}

		return true;
	}

	private function seed_session_phone(string $phone): void
	{
		if (! function_exists('WC')) {
			return;
		}

		$session = WC()->session;
		if (! $session) {
			return;
		}

		$session->set('dobare_credit_applied_for_phone', $phone);
	}

	private function hydrate_cart(array $items): void
	{
		if (! function_exists('WC')) {
			return;
		}

		$cart = WC()->cart;
		if (! $cart) {
			return;
		}

		$cart->empty_cart(true);

		foreach ($items as $item) {
			$product_id = isset($item['product_id']) ? (int) $item['product_id'] : 0;
			if ($product_id <= 0) {
				continue;
			}

			$quantity = isset($item['qty']) ? (int) $item['qty'] : 1;
			$variation_id = isset($item['variation_id']) ? (int) $item['variation_id'] : 0;
			$variation = isset($item['variation']) && is_array($item['variation']) ? $item['variation'] : array();
			$cart_item_data = isset($item['cart_item_data']) && is_array($item['cart_item_data']) ? $item['cart_item_data'] : array();

			try {
				$cart->add_to_cart(
					$product_id,
					max(1, $quantity),
					$variation_id,
					$variation,
					$cart_item_data
				);
			} catch (\Throwable $throwable) {
				continue;
			}
		}
	}

	private function find_cart_row(string $cart_hash): ?array
	{
		global $wpdb;

		$table = $this->get_table_name();
		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$table} WHERE cart_hash = %s",
				$cart_hash
			),
			ARRAY_A
		);

		return $row ?: null;
	}

	private function decode_extra_details(?string $json): array
	{
		if (empty($json)) {
			return array();
		}

		$value = json_decode($json, true);

		return is_array($value) ? $value : array();
	}

	private function extract_cart_item_data(array $cart_item): array
	{
		$ignored_keys = array(
			'product_id',
			'variation_id',
			'variation',
			'quantity',
			'line_subtotal',
			'line_total',
			'line_tax',
			'data',
			'key',
		);

		$filtered = array();

		foreach ($cart_item as $key => $value) {
			if (in_array($key, $ignored_keys, true)) {
				continue;
			}

			if (is_object($value)) {
				continue;
			}

			$filtered[$key] = $value;
		}

		return $filtered;
	}

	private function generate_new_cart_hash_from_restore(string $old_hash): string
	{
		return sha1($old_hash . '|' . microtime(true) . '|' . wp_rand());
	}

	private function get_order_cart_hash(\WC_Order $order): ?string
	{
		$value = $order->get_meta(self::ORDER_META_CART_HASH);

		return is_string($value) && $value !== '' ? $value : null;
	}

	private function cleanup_cart_for_order(\WC_Order $order, string $reason): void
	{
		$cart_hash = $this->get_order_cart_hash($order);

		if (! $cart_hash) {
			$cart_hash = $this->get_session_cart_hash();
		}

		if (! $cart_hash) {
			return;
		}

		Dobare_Abandoned_Cart_Scheduler::instance()->cancel_events_for_hash($cart_hash);

		global $wpdb;
		$table = $this->get_table_name();
		$wpdb->delete($table, array('cart_hash' => $cart_hash));

		if ($cart_hash === $this->get_session_cart_hash()) {
			$this->set_session_cart_hash(null);
		}
	}
}


