<?php
/**
 * Credit Manager - Main facade for the credit system
 *
 * Single entry point for all credit operations.
 * Handles balance checking, credit usage, and synchronization.
 *
 * @package AIPT\Core\CreditSystem
 */

namespace AIPT\Core\CreditSystem;

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

/**
 * Credit Check Result
 */
class CreditCheckResult
{
    public bool $allowed;
    public float $cost;
    public float $remaining;

    public function __construct(bool $allowed, float $cost, float $remaining)
    {
        $this->allowed = $allowed;
        $this->cost = $cost;
        $this->remaining = $remaining;
    }
}

/**
 * Credit Manager
 */
class CreditManager
{
    private static ?self $instance = null;
    private CreditApiClient $api;
    private CreditCache $cache;
    private array $localUsage = [];

    /**
     * Get singleton instance
     */
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Private constructor
     */
    private function __construct()
    {
        $this->api = new CreditApiClient();
        $this->cache = new CreditCache();
        $this->loadLocalUsage();
    }

    /**
     * Check if action is allowed based on available credits
     *
     * @param string $action Action type (description, title, tags, etc.)
     * @param int $quantity Number of actions
     * @return CreditCheckResult
     */
    public function check(string $action, int $quantity = 1): CreditCheckResult
    {
        if (!$this->isRegistered()) {
            return new CreditCheckResult(false, 0, 0);
        }

        $balance = $this->getBalance();

        if (!empty($balance['is_unlimited'])) {
            return new CreditCheckResult(true, 0, PHP_INT_MAX);
        }

        $costs = $this->getCosts();
        $cost = ($costs[$action] ?? 1) * $quantity;
        $available = ($balance['available'] ?? 0) - $this->getPendingUsage();

        return new CreditCheckResult(
            $available >= $cost,
            $cost,
            max(0, $available)
        );
    }

    /**
     * Use credits for an action
     *
     * @param string $action Action type
     * @param int $quantity Number of actions
     * @return bool True if successful
     */
    public function use(string $action, int $quantity = 1): bool
    {
        $check = $this->check($action, $quantity);
        if (!$check->allowed) {
            return false;
        }

        $this->recordLocalUsage($action, $check->cost);

        $this->cache->decrementAvailable($check->cost);

        do_action('aipt_credit_used', $action, $check->cost);

        return true;
    }

    /**
     * Get current balance (cache-first)
     *
     * @param bool $force Force refresh from API
     * @return array Balance data
     */
    public function getBalance(bool $force = false): array
    {
        if (!$force) {
            $cached = $this->cache->getBalance();
            if ($cached !== null) {
                return $cached;
            }
        }

        $usageToSync = $this->exportLocalUsage();
        $hadUsageToSync = !empty($usageToSync);

        $response = $this->api->sync($usageToSync);
        if (!empty($response['success'])) {
            $balance = $response['current_balance'] ?? $this->getDefaultBalance();
            $this->cache->setBalance($balance);

            if (!empty($response['plan_name'])) {
                \update_option('aipt_credit_plan_name', sanitize_text_field($response['plan_name']), false);
            }

            $this->handlePlanMismatch($response);

            if ($hadUsageToSync) {

                $usageProcessed = !empty($response['usage_processed']) ||
                                  (isset($response['already_processed']) && $response['already_processed'] === true);

                if ($usageProcessed) {
                    $this->clearLocalUsage();
                } else {

                }
            }

            return $balance;
        }

        return $this->cache->getBalance() ?? $this->getDefaultBalance();
    }

    /**
     * Get credit costs per action (cache-first)
     *
     * @param bool $force Force refresh from API
     * @return array Costs by action type
     */
    public function getCosts(bool $force = false): array
    {
        if (!$force) {
            $cached = $this->cache->getCosts();
            if ($cached !== null) {
                return $cached;
            }
        }

        $response = $this->api->sync([]);
        if (!empty($response['success']) && isset($response['credit_costs'])) {
            $costs = $this->parseCosts($response['credit_costs']);
            $this->cache->setCosts($costs);
            return $costs;
        }

        return $this->cache->getCosts() ?? $this->getDefaultCosts();
    }

    /**
     * Force synchronization with remote API
     *
     * @return array Current balance after sync
     */
    public function sync(): array
    {
        return $this->getBalance(true);
    }

    /**
     * Register site with remote API
     *
     * @param array $siteData Site registration data
     * @return array Registration result
     */
    public function register(array $siteData): array
    {
        return $this->api->register($siteData);
    }

    /**
     * Run troubleshoot action
     *
     * @param string $action Troubleshoot action
     * @param array $data Additional data
     * @return array Result
     */
    public function troubleshoot(string $action, array $data = []): array
    {
        return $this->api->troubleshoot($action, $data);
    }

    /**
     * Check if site is registered
     *
     * @return bool
     */
	public function isRegistered(): bool
	{
		return CreditCredentials::isRegistered();
	}

    /**
     * Clear all caches
     */
    public function clearCache(): void
    {
        $this->cache->clear();
    }

    /**
     * Handle plan mismatch between client and server
     *
     * If API reports plan_mismatch, force sync from Freemius servers then retry update
     */
    private function handlePlanMismatch(array $response): void
    {
        if (empty($response['plan_mismatch'])) {
            return;
        }

        $backoffKey = 'aipt_plan_mismatch_backoff';
        $lastAttempt = (int) get_transient($backoffKey);
        $now = time();
        
        if ($lastAttempt > 0 && ($now - $lastAttempt) < 300) {
            return;
        }

        set_transient($backoffKey, $now, 600);

		if (class_exists(\AIPT\Core\FsManager::class)) {
			$fsManager = \AIPT\Core\FsManager::getInstance();
			

			$freshPlan = $fsManager->forceSyncFromFreemius();
			

			$serverPlan = $response['plan_name'] ?? 'unknown';
			
			
			try {
				$update_response = $this->api->updatePlan($freshPlan);
				if (empty($update_response['success'])) {
					set_transient($backoffKey, $now, 600);
				} else {

					$chosenPlan = $update_response['plan_name'] ?? $freshPlan;
					update_option('aipt_credit_plan_name', sanitize_text_field($chosenPlan), false);
				}
			} catch (\Throwable $e) {
				set_transient($backoffKey, $now, 600);
			}
		}
    }

    /**
     * Load local usage from database
     */
    private function loadLocalUsage(): void
    {
        $this->localUsage = get_option('aipt_credit_local_usage', [
            'total' => 0,
            'actions' => [],
            'started' => null
        ]);
    }

    /**
     * Record usage locally for later sync
     *
     * @param string $action Action type
     * @param float $cost Credit cost
     */
    private function recordLocalUsage(string $action, float $cost): void
    {

        if (empty($this->localUsage['started'])) {
            $this->localUsage['started'] = time();
        }

        $this->localUsage['total'] = ($this->localUsage['total'] ?? 0) + $cost;
        $this->localUsage['actions'][$action] = ($this->localUsage['actions'][$action] ?? 0) + $cost;
        $this->localUsage['last_activity'] = time();
        update_option('aipt_credit_local_usage', $this->localUsage, false);
    }

    /**
     * Get pending (unsynced) usage
     *
     * @return float
     */
    private function getPendingUsage(): float
    {
        return $this->localUsage['total'] ?? 0;
    }

    /**
     * Export local usage for sync
     *
     * @return array Usage summary or empty array
     */
    private function exportLocalUsage(): array
    {
        $total = $this->localUsage['total'] ?? 0;
        if ($total <= 0) {
            return [];
        }

        $startTime = $this->localUsage['started'] ?? 0;

        return [
            'total_local_used' => $total,
            'actions_breakdown' => $this->localUsage['actions'] ?? [],
            'summary_start_time' => $startTime,
            'summary_last_activity' => $this->localUsage['last_activity'] ?? null,
            'export_id' => uniqid('usage_', true),
            'signature' => $this->computeSignature($total, $startTime)
        ];
    }

    /**
     * Clear local usage after successful sync
     */
    private function clearLocalUsage(): void
    {
        $this->localUsage = [
            'total' => 0,
            'actions' => [],
            'started' => null
        ];
        update_option('aipt_credit_local_usage', $this->localUsage, false);
    }

    /**
     * Compute signature for usage data verification
     *
     * @param float $total Total usage amount
     * @param int $startTime Start timestamp (0 if not set)
     * @return string HMAC signature
     */
    private function computeSignature(float $total, int $startTime): string
    {
        $secret = get_option('aipt_credit_secret_key', '');
        $data = $total . ':' . $startTime;
        return hash_hmac('sha256', $data, $secret);
    }

    /**
     * Parse costs array from API response
     *
     * @param array $costs Raw costs array
     * @return array Parsed costs by action
     */
    private function parseCosts(array $costs): array
    {
        $result = [];
        foreach ($costs as $item) {
            if (isset($item['action_type'], $item['cost'])) {
                $result[$item['action_type']] = (float) $item['cost'];
            }
        }
        return $result;
    }

    /**
     * Get default balance structure
     *
     * @return array
     */
    private function getDefaultBalance(): array
    {
        return [
            'total' => 0,
            'used' => 0,
            'available' => 0,
            'is_unlimited' => false
        ];
    }

    /**
     * Get default costs
     *
     * @return array
     */
    private function getDefaultCosts(): array
    {
        return [
            'description' => 1,
            'title' => 1,
            'tags' => 1
        ];
    }
}
