<?php

namespace Dobare\Abandoned_Cart;

class Dobare_Abandoned_Cart_Scheduler
{
	private static ?self $instance = null;
	private bool $hooks_registered = false;
	private const MAX_RETRIES = 3;

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

		return self::$instance;
	}

	public function register_hooks(): void
	{
		if ($this->hooks_registered) {
			return;
		}

		$this->hooks_registered = true;

		add_action('dobare_mark_cart_abandoned', array($this, 'handle_abandonment_event'), 10, 1);
		add_action('dobare_abandoned_cart_retry_notification', array($this, 'handle_retry_event'), 10, 2);
	}

	public function schedule_abandonment_check(string $cart_hash, int $delay_minutes): void
	{
		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		$this->cancel_events_for_hash($cart_hash);

		wp_schedule_single_event(
			time() + ($delay_minutes * MINUTE_IN_SECONDS),
			'dobare_mark_cart_abandoned',
			array($cart_hash)
		);
	}

	public function cancel_events_for_hash(string $cart_hash): void
	{
		wp_clear_scheduled_hook('dobare_mark_cart_abandoned', array($cart_hash));

		for ($attempt = 1; $attempt <= 3; $attempt++) {
			wp_clear_scheduled_hook('dobare_abandoned_cart_retry_notification', array($cart_hash, $attempt));
		}
	}

	public function handle_abandonment_event(string $cart_hash): void
	{
		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		$row = $this->get_cart_row($cart_hash, 'active');
		if (! $row) {
			return;
		}

		$extra_details = $this->decode_extra_details($row['extra_details']);
		$extra_details['timestamps']['abandoned_at'] = gmdate('c');

		$updated = $this->update_row(
			$row['cart_hash'],
			array(
				'status'        => 'abandoned',
				'abandoned_at'  => gmdate('Y-m-d H:i:s'),
				'extra_details' => wp_json_encode($extra_details, JSON_UNESCAPED_UNICODE),
				'last_error'    => null,
			),
			array(
				'cart_hash' => $cart_hash,
				'status'    => 'active',
			)
		);

		if ($updated === false || $updated === 0) {
			return;
		}

		$payload = array(
			'user_phone'    => $row['user_phone'],
			'cart_hash'     => $row['cart_hash'],
			'extra_details' => $extra_details,
		);

		$notifier = Dobare_Abandoned_Cart_Notifier::instance();
		$sent = $notifier->dispatch($payload, 1);

		if (! $sent) {
			$this->set_last_error($cart_hash, $notifier->get_last_error());
			$notifier->schedule_retry($cart_hash, 2);
		}
	}

	public function handle_retry_event(string $cart_hash, int $attempt): void
	{
		if (! Dobare_Abandoned_Cart_Guard::is_feature_enabled()) {
			return;
		}

		if ($attempt > self::MAX_RETRIES) {
			return;
		}

		$row = $this->get_cart_row($cart_hash, 'abandoned');
		if (! $row) {
			return;
		}

		$extra_details = $this->decode_extra_details($row['extra_details']);
		$payload = array(
			'user_phone'    => $row['user_phone'],
			'cart_hash'     => $row['cart_hash'],
			'extra_details' => $extra_details,
		);

		$notifier = Dobare_Abandoned_Cart_Notifier::instance();
		$sent = $notifier->dispatch($payload, $attempt);

		if (! $sent && $attempt < self::MAX_RETRIES) {
			$this->set_last_error($cart_hash, $notifier->get_last_error());
			$notifier->schedule_retry($cart_hash, $attempt + 1);
		} elseif ($sent) {
			$this->set_last_error($cart_hash, null);
		}
	}

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

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

	private function get_cart_row(string $cart_hash, ?string $status = null): ?array
	{
		global $wpdb;

		$table = $this->get_table_name();

		if ($status) {
			$row = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT * FROM {$table} WHERE cart_hash = %s AND status = %s",
					$cart_hash,
					$status
				),
				ARRAY_A
			);
		} else {
			$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();
		}

		$decoded = json_decode($json, true);

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

	private function update_row(string $cart_hash, array $data, array $where)
	{
		global $wpdb;

		return $wpdb->update($this->get_table_name(), $data, $where);
	}

	private function set_last_error(string $cart_hash, ?string $message): void
	{
		$this->update_row(
			$cart_hash,
			array(
				'last_error' => $message,
			),
			array('cart_hash' => $cart_hash)
		);
	}

}


