<?php

namespace SugarCalendarBookings\Integrations\Credits;

use SugarCalendarBookings\Options;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Vendor\ProductApi\Auth\HMACAuthStrategy;
use SugarCalendarBookings\Vendor\ProductApi\Auth\SiteRegistration;
use SugarCalendarBookings\Vendor\ProductApi\Context;
use SugarCalendarBookings\Vendor\ProductApi\ProductApi;

/**
 * Credits Service.
 *
 * Fetches, caches, and formats integration credit usage data from the Product API.
 *
 * @since 1.4.0
 */
class CreditsService {

	/**
	 * Transient key for cached credits data.
	 *
	 * @since 1.4.0
	 *
	 * @var string
	 */
	const TRANSIENT_KEY = 'sugar_calendar_bookings_integration_credits';

	/**
	 * Cache duration in seconds (5 minutes).
	 *
	 * @since 1.4.0
	 *
	 * @var int
	 */
	const CACHE_TTL = 5 * MINUTE_IN_SECONDS;

	/**
	 * Top-tier plan that should not see upgrade prompts.
	 *
	 * @since 1.4.0
	 *
	 * @var string
	 */
	const TOP_TIER_PLAN = 'elite';

	/**
	 * In-memory cache to avoid duplicate API calls within the same request.
	 *
	 * @since 1.4.0
	 *
	 * @var array|null|false False means not yet fetched.
	 */
	private $credits_memo = false;

	/**
	 * Get credits data, preferring a fresh API response and falling back to cache.
	 *
	 * The API is always queried first so the UI reflects current usage; the
	 * transient cache is only used when the API is unreachable or returns an
	 * error. Results are memoized per-request to avoid duplicate API calls.
	 *
	 * Returns null in any of these cases (callers hide the usage UI entirely):
	 *  - "Disable Integrations" toggled on.
	 *  - Site not registered with the Product API.
	 *  - API unreachable and no cached data available.
	 *
	 * Pro installs without a valid license fall back to the Lite credits endpoint
	 * so users keep a working integrations experience until they activate.
	 *
	 * @since 1.4.0
	 *
	 * @return array|null Formatted credits data, or null when unavailable.
	 */
	public function get_credits(): ?array {

		if ( $this->credits_memo !== false ) {
			return $this->credits_memo;
		}

		if ( Plugin::get( Options::class )->get( 'misc.disable_integrations' ) ) {
			$this->credits_memo = null;

			return null;
		}

		$registration = ProductApi::get( SiteRegistration::class );

		if ( ! $registration->is_registered() ) {
			$this->credits_memo = null;

			return null;
		}

		$fresh = $this->fetch_credits();

		if ( $fresh !== null ) {
			$this->credits_memo = $fresh;

			return $fresh;
		}

		// API unavailable — fall back to last known cached value if present.
		$cached = get_transient( $this->get_transient_key() );

		$this->credits_memo = $cached !== false ? $cached : null;

		return $this->credits_memo;
	}

	/**
	 * Fetch fresh credits data from the Product API and cache it.
	 *
	 * @since 1.4.0
	 *
	 * @return array|null Formatted credits data, or null on failure.
	 */
	private function fetch_credits(): ?array {

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->get( '/integrations/v1/' . $this->get_license_type() . '/credits' )
			->auth_strategy( $hmac_auth )
			->send();

		if ( is_wp_error( $response ) ) {
			return null;
		}

		if ( ! $response->is_successful() ) {
			return null;
		}

		$body = $response->get_body();

		if ( empty( $body ) || ! isset( $body['credits'] ) ) {
			return null;
		}

		// No meaningful allocation — nothing to display.
		if ( empty( $body['credits']['total'] ) ) {
			return null;
		}

		$formatted = $this->format_response( $body );

		set_transient( $this->get_transient_key(), $formatted, self::CACHE_TTL );

		Plugin::get( CreditAlertEmail::class )->maybe_send( $formatted );

		return $formatted;
	}

	/**
	 * Fetch and return the raw API response for debugging.
	 *
	 * @since 1.4.0
	 *
	 * @return array Debug info including raw response body, status, and any errors.
	 */
	public function fetch_raw_debug(): array {

		$registration = ProductApi::get( SiteRegistration::class );

		if ( ! $registration->is_registered() ) {
			return [ 'error' => 'Site is not registered with Product API.' ];
		}

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->get( '/integrations/v1/' . $this->get_license_type() . '/credits' )
			->auth_strategy( $hmac_auth )
			->send();

		if ( is_wp_error( $response ) ) {
			return [
				'error'   => 'WP_Error',
				'message' => $response->get_error_message(),
				'code'    => $response->get_error_code(),
			];
		}

		return [
			'status_code' => $response->get_status_code(),
			'body'        => $response->get_body(),
			'is_success'  => $response->is_successful(),
		];
	}

	/**
	 * Format the API response into the shape needed by the UI.
	 *
	 * @since 1.4.0
	 *
	 * @param array $response Raw API response body.
	 *
	 * @return array Formatted credits data.
	 */
	private function format_response( array $response ): array {

		$credits    = $response['credits'];
		$total      = (int) ( $credits['total'] ?? 0 );
		$used       = (int) ( $credits['used'] ?? 0 );
		$percentage = $total > 0 ? round( ( $used / $total ) * 100, 1 ) : 0;

		return [
			'plan'         => $response['plan'] ?? 'lite',
			'total'        => $total,
			'used'         => $used,
			'remaining'    => (int) ( $credits['remaining'] ?? 0 ),
			'resets_at'    => ! empty( $credits['resets_at'] ) && strtotime( $credits['resets_at'] ) !== false
				? sanitize_text_field( $credits['resets_at'] )
				: null,
			'percentage'   => min( $percentage, 100 ),
			'is_maxed'     => $used >= $total && $total > 0,
			'show_upgrade' => ( $response['plan'] ?? 'lite' ) !== self::TOP_TIER_PLAN,
			'usage'        => $this->format_usage( $response['usage'] ?? [], $total ),
		];
	}

	/**
	 * Format per-integration usage into an array with percentages.
	 *
	 * @since 1.4.0
	 *
	 * @param array $usage Raw usage keyed by integration slug.
	 * @param int   $total Total credits for percentage calculation.
	 *
	 * @return array Formatted usage entries.
	 */
	private function format_usage( array $usage, int $total ): array {

		if ( empty( $usage ) || $total <= 0 ) {
			return [];
		}

		$formatted = [];

		foreach ( $usage as $slug => $count ) {
			$count = (int) $count;

			if ( $count <= 0 ) {
				continue;
			}

			$formatted[] = [
				'slug'       => sanitize_key( $slug ),
				'count'      => $count,
				'percentage' => min( (int) round( ( $count / $total ) * 100 ), 100 ),
			];
		}

		// Sort by count descending.
		usort( $formatted, fn( $a, $b ) => $b['count'] <=> $a['count'] );

		return $formatted;
	}

	/**
	 * Get the effective license type for credits routing.
	 *
	 * Returns 'pro' only when the Pro plugin is active AND the license is valid.
	 * Pro installs without a valid license fall back to 'lite' so the feature
	 * keeps working on Lite credits until the license is activated.
	 *
	 * @since 1.4.0
	 *
	 * @return string 'pro' or 'lite'.
	 */
	private function get_license_type(): string {

		$context = ProductApi::get( Context::class );

		return ( $context->is_pro() && $context->is_license_valid() ) ? 'pro' : 'lite';
	}

	/**
	 * Get the transient key scoped to the current license type.
	 *
	 * Prevents cache poisoning when lite and pro endpoints return different data.
	 *
	 * @since 1.4.0
	 *
	 * @return string Transient key.
	 */
	private function get_transient_key(): string {

		return self::TRANSIENT_KEY . '_' . $this->get_license_type();
	}
}
