<?php
/**
 * Live verification evidence evaluator.
 *
 * @package Carticy\Conversions\Diagnostics
 */

declare(strict_types=1);

namespace Carticy\Conversions\Diagnostics;

use Carticy\Conversions\Client\ClientTrackingSettings;
use Carticy\Conversions\Domain\ConversionEventType;
use Carticy\Conversions\Events\DispatchResult;
use Carticy\Conversions\Rest\PremiumServerBridge;
use Carticy\Conversions\Storage\OrderMetaStore;

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

/**
 * Decides whether onboarding may reach "complete" - and powers the verify
 * surface - from REAL evidence, never an asserted flag.
 *
 * Client delivery is verified when a purchase event was actually recorded on a
 * recent order AND a client destination is configured (the order-paid engine
 * fired into a real destination). Server delivery (Pro) is verified only when a
 * dispatch row reached `sent` (the provider accepted it). `blocked_*`, `queued`,
 * and `failed` never count as verified.
 */
final class VerificationStatus {

	/**
	 * Max recent orders scanned for purchase evidence.
	 */
	private const RECENT_ORDER_LIMIT = 25;

	/**
	 * Order meta store.
	 *
	 * @var OrderMetaStore
	 */
	private OrderMetaStore $store;

	/**
	 * Premium server bridge.
	 *
	 * @var PremiumServerBridge
	 */
	private PremiumServerBridge $server;

	/**
	 * Constructor.
	 *
	 * @param OrderMetaStore      $store Order meta store.
	 * @param PremiumServerBridge $server Premium server bridge.
	 */
	public function __construct( OrderMetaStore $store, PremiumServerBridge $server ) {
		$this->store  = $store;
		$this->server = $server;
	}

	/**
	 * Evaluate verification evidence.
	 *
	 * @return array<string, mixed>
	 */
	public function evaluate(): array {
		$client            = ClientTrackingSettings::from_options();
		$client_configured = $client->has_active_destinations();
		$purchase          = $this->latest_recorded_purchase();
		$client_event      = null !== $purchase;
		$client_verified   = $client_configured && $client_event;

		$server_available = $this->server->available();
		$server_sends     = $server_available ? $this->accepted_server_sends() : array();
		$server_sent      = array() !== $server_sends;
		$server_verified  = $server_available && $server_sent;

		$can_complete = $client_verified || $server_verified;

		return array(
			'canComplete' => $can_complete,
			'client'      => array(
				'configured' => $client_configured,
				'hasEvent'   => $client_event,
				'verified'   => $client_verified,
			),
			'server'      => array(
				'available' => $server_available,
				'accepted'  => $server_sent,
				'verified'  => $server_verified,
			),
			// Real evidence detail for the Verify surface: the actual recorded
			// purchase (order, time, value) and the accepted server sends.
			'evidence'    => array(
				'purchase'    => $purchase,
				'serverSends' => $server_sends,
			),
			'blockers'    => $this->blockers( $can_complete, $client_configured, $client_event, $server_available, $server_sent ),
		);
	}

	/**
	 * Whether verification evidence permits onboarding completion.
	 *
	 * @return bool
	 */
	public function can_complete(): bool {
		return (bool) $this->evaluate()['canComplete'];
	}

	/**
	 * The most recent recorded canonical purchase event, with its real
	 * order/value detail, or null when none exists.
	 *
	 * @return array<string, mixed>|null
	 */
	private function latest_recorded_purchase(): ?array {
		if ( ! function_exists( 'wc_get_orders' ) ) {
			return null;
		}

		$orders = wc_get_orders(
			array(
				'limit'   => self::RECENT_ORDER_LIMIT,
				'orderby' => 'date',
				'order'   => 'DESC',
				'return'  => 'objects',
			)
		);

		if ( is_object( $orders ) && isset( $orders->orders ) && is_array( $orders->orders ) ) {
			$orders = $orders->orders;
		}

		$latest = null;
		foreach ( is_array( $orders ) ? $orders : array() as $order ) {
			if ( ! is_object( $order ) ) {
				continue;
			}

			foreach ( $this->store->get_events( $order ) as $event ) {
				if ( ConversionEventType::PURCHASE !== $event->event_type() ) {
					continue;
				}

				$data      = $event->to_array();
				$candidate = array(
					'orderId'    => $event->order_id(),
					'eventId'    => $event->event_id(),
					'occurredAt' => (string) ( $data['occurred_at'] ?? '' ),
					'value'      => (string) ( $data['value']['amount'] ?? '' ),
					'currency'   => (string) ( $data['value']['currency'] ?? '' ),
				);

				if ( null === $latest || strcmp( $candidate['occurredAt'], (string) $latest['occurredAt'] ) > 0 ) {
					$latest = $candidate;
				}
			}
		}

		return $latest;
	}

	/**
	 * The accepted (`sent`) server dispatch rows, newest first, bounded.
	 *
	 * @return array<int, array<string, string>>
	 */
	private function accepted_server_sends(): array {
		$sends = array();
		foreach ( $this->store->get_recent_dispatches( 100 ) as $row ) {
			if ( DispatchResult::STATUS_SENT !== sanitize_key( (string) ( $row['status'] ?? '' ) ) ) {
				continue;
			}

			$sends[] = array(
				'destination' => (string) ( $row['destination'] ?? '' ),
				'eventType'   => (string) ( $row['event_type'] ?? '' ),
				'status'      => DispatchResult::STATUS_SENT,
				'updatedAt'   => (string) ( $row['updated_at'] ?? '' ),
			);

			if ( count( $sends ) >= 5 ) {
				break;
			}
		}

		return $sends;
	}

	/**
	 * Build the blockers surfaced when completion is not yet earned.
	 *
	 * @param bool $can_complete Whether completion is permitted.
	 * @param bool $client_configured Whether a client destination is configured.
	 * @param bool $client_event Whether a purchase event was recorded.
	 * @param bool $server_available Whether the server layer is available.
	 * @param bool $server_sent Whether a server send was accepted.
	 * @return array<int, array<string, string>>
	 */
	private function blockers( bool $can_complete, bool $client_configured, bool $client_event, bool $server_available, bool $server_sent ): array {
		if ( $can_complete ) {
			return array();
		}

		$blockers = array();
		if ( ! $client_configured && ! $server_available ) {
			$blockers[] = array(
				'step'   => 'connect',
				'reason' => 'no_destination_configured',
			);
		}
		if ( ! $client_event ) {
			$blockers[] = array(
				'step'   => 'verify',
				'reason' => 'no_recorded_purchase',
			);
		}
		if ( $server_available && ! $server_sent ) {
			$blockers[] = array(
				'step'   => 'verify',
				'reason' => 'no_accepted_server_send',
			);
		}

		return $blockers;
	}
}
