<?php
/**
 * Credit Manager — Handles credit balance via Supabase API
 *
 * @package PackshotPhotoAI
 */

namespace PackshotPhotoAI\Core;

defined('ABSPATH') || exit;

/**
 * Manages AI credit system — source of truth is Supabase
 */
class CreditManager {

    /**
     * Supabase project URL
     */
    private const SUPABASE_URL = 'https://duubkqjowabuevqudtbk.supabase.co/functions/v1';

    /**
     * Cache time for balance (seconds)
     */
    private const CACHE_TTL = 60;

    /**
     * Transient key for cached balance
     */
    private const CACHE_KEY = 'packshotphoto_credits_cache';

    /**
     * Get current credit balance (from cache or API)
     *
     * @return int
     */
    public static function get_balance(): int {
        if ('custom_api' === get_option('packshotphoto_connection_type', 'our_service')) {
            return 999999;
        }

        self::verify_domain_integrity();

        $cached = get_transient(self::CACHE_KEY);
        if (false !== $cached) {
            return (int) $cached['balance'];
        }

        // Refresh from API if we have a key
        $existing_key = Encryption::decrypt(get_option('packshotphoto_api_key', ''));
        if (!empty($existing_key)) {
            $status = self::fetch_status_from_api();
            if ($status) {
                return (int) $status['credits_balance'];
            }
        } else {
            // Auto-provision key if empty (e.g., first generation attempt or page load)
            $sync = self::force_sync();
            if ($sync['success']) {
                return (int) ($sync['balance'] ?? 0);
            }
        }

        // Fallback to local option if API unreachable
        return (int) get_option('packshotphoto_credits_balance', 0);
    }

    /**
     * Check if user has at least one credit
     *
     * @return bool
     */
    public static function has_credits(): bool {
        if ('custom_api' === get_option('packshotphoto_connection_type', 'our_service')) {
            return true;
        }
        return self::get_balance() > 0;
    }

    /**
     * Check if 50% OFF welcome promo (first 12 hours from install) is currently active.
     * Active for fresh installations (<= 10 trial credits) who have never made a purchase.
     *
     * @return array{active: bool, remaining_seconds: int, expire_timestamp: int, discount_pct: int}
     */
    public static function get_welcome_promo_status(): array {
        // 1. If site has already purchased credits, promo is permanently disabled
        if (get_option('packshotphoto_has_purchased')) {
            return [
                'active'            => false,
                'remaining_seconds' => 0,
                'expire_timestamp'  => 0,
                'discount_pct'      => 0,
            ];
        }

        // 2. If site balance is > 10 (means they have purchased/accumulated credits previously)
        $current_balance = (int) get_option('packshotphoto_credits_balance', 0);
        if ($current_balance > 10) {
            return [
                'active'            => false,
                'remaining_seconds' => 0,
                'expire_timestamp'  => 0,
                'discount_pct'      => 0,
            ];
        }

        // 3. Track install time (starts on first run)
        $installed_time = (int) get_option('packshotphoto_installed_time', 0);
        if (!$installed_time || $installed_time === 1) {
            $installed_time = time();
            update_option('packshotphoto_installed_time', $installed_time);
        }

        $duration = 12 * HOUR_IN_SECONDS; // 12 hours from installation
        $expire_timestamp = $installed_time + $duration;
        $now = time();
        $remaining = max(0, $expire_timestamp - $now);
        $is_active = ($remaining > 0);

        return [
            'active'            => $is_active,
            'remaining_seconds' => $remaining,
            'expire_timestamp'  => $expire_timestamp,
            'discount_pct'      => $is_active ? 50 : 0,
        ];
    }

    /**
     * Is welcome promo active boolean helper
     */
    public static function is_welcome_promo_active(): bool {
        $promo = self::get_welcome_promo_status();
        return $promo['active'];
    }

    /**
     * Deduct credit — via Supabase API (atomic, server-side)
     *
     * @param int    $amount
     * @param string $description
     * @return bool
     */
    public static function deduct_credit(int $amount = 1, string $description = 'AI generation'): bool {
        if ('custom_api' === get_option('packshotphoto_connection_type', 'our_service')) {
            return true;
        }

        try {
            // Note: The Supabase edge function 'generate-seo' already deducts the credit from
            // the database server-side, so we only need to decrement our local cache and option.
            // This prevents double-deduction and eliminates an extra blocking HTTP call.
            $balance = (int) get_option('packshotphoto_credits_balance', 0);
            $new_balance = max(0, $balance - $amount);

            update_option('packshotphoto_credits_balance', $new_balance);
            self::set_balance_cache($new_balance);

            Logger::debug('CreditManager: local balance synced after generation', [
                'deducted'    => $amount,
                'new_balance' => $new_balance
            ]);

            return true;

        } catch (\Exception $e) {
            Logger::error('CreditManager: Exception during local balance sync', ['error' => $e->getMessage()]);
            return false;
        }
    }

    /**
     * Force-refresh balance from Supabase API
     *
     * @return array|null
     */
    public static function fetch_status_from_api(): ?array {
        self::verify_domain_integrity();

        $api_key = Encryption::decrypt(get_option('packshotphoto_api_key', ''));
        if (empty($api_key)) {
            return null;
        }

        $response = wp_remote_post(self::SUPABASE_URL . '/license-validate', [
            'timeout'   => 8,
            'headers'   => ['Content-Type' => 'application/json'],
            'sslverify' => false,
            'body'      => wp_json_encode([
                'api_key'  => $api_key,
                'site_url' => home_url(),
            ]),
        ]);

        if (is_wp_error($response)) {
            Logger::warning('CreditManager: Cannot reach API', ['error' => $response->get_error_message()]);
            return null;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (is_array($body) && isset($body['valid'])) {
            if ($body['valid']) {
                self::set_balance_cache((int) $body['credits_balance']);
                update_option('packshotphoto_credits_balance', (int) $body['credits_balance']);
                update_option('packshotphoto_activated_url', home_url());
                return $body;
            } else {
                Logger::warning('CreditManager: API key explicitly declared invalid by server. Wiping local credentials.', ['body' => $body]);
                delete_option('packshotphoto_api_key');
                delete_option('packshotphoto_credits_balance');
                delete_option('packshotphoto_activated_url');
                delete_transient(self::CACHE_KEY);
            }
        }

        return null;
    }

    /**
     * Get full status including transaction history
     *
     * @return array
     */
    public static function get_full_status(): array {
        self::verify_domain_integrity();

        // Auto-adopt key if none is saved yet (e.g., first page load after purchase)
        $existing_key = Encryption::decrypt(get_option('packshotphoto_api_key', ''));
        if (empty($existing_key)) {
            Logger::info('CreditManager: No API key — running force_sync to auto-adopt');
            $sync = self::force_sync();
            if ($sync['success']) {
                return self::fetch_status_from_api() ?? [
                    'valid'           => true,
                    'credits_balance' => $sync['balance'] ?? 0,
                    'plan'            => 'credits',
                    'transactions'    => $sync['transactions'] ?? [],
                ];
            }
        }

        $status = self::fetch_status_from_api();

        if ($status) {
            return $status;
        }

        return [
            'valid'           => false,
            'credits_balance' => (int) get_option('packshotphoto_credits_balance', 0),
            'plan'            => 'unknown',
            'transactions'    => [],
            'offline'         => true,
        ];
    }

    public static function generate_site_hash(): string {
        $url = strtolower(trim(home_url(), '/'));
        return hash('sha256', $url . 'packshotphoto_site_id_v2');
    }

    /**
     * Force synchronization with Supabase.
     *
     * Strategy:
     * 1. Clear transient cache only (NOT the option — preserve last known good value).
     * 2. If an API key is already saved in WP, validate it directly — works regardless of URL/hash.
     * 3. If no API key, fall back to polling by site hash (for first-time key adoption).
     *
     * @return array
     */
    public static function force_sync(): array {
        // Clear transient so next get_balance() hits the API
        delete_transient(self::CACHE_KEY);

        $existing_key = Encryption::decrypt(get_option('packshotphoto_api_key', ''));
        if (!empty($existing_key)) {
            $new_status = self::fetch_status_from_api();
            if (is_array($new_status) && isset($new_status['credits_balance'])) {
                $balance = (int) $new_status['credits_balance'];
                update_option('packshotphoto_credits_balance', $balance);
                Logger::info('CreditManager: force_sync via existing key', ['balance' => $balance]);
                return [
                    'success' => true,
                    'message' => __('Balance refreshed!', 'packshotphoto-ai'),
                    'balance' => $balance,
                    'transactions' => $new_status['transactions'] ?? [],
                ];
            }
            // API call failed — return last known good value
            Logger::warning('CreditManager: force_sync API call failed, returning cached value');
            return [
                'success' => true,
                'message' => __('Using cached balance (API temporarily unavailable).', 'packshotphoto-ai'),
                'balance' => (int) get_option('packshotphoto_credits_balance', 0),
            ];
        }

        // Strategy 2 — no key yet: try to adopt by site hash
        $hash   = self::generate_site_hash();
        $status = self::poll_checkout_status($hash);

        // If no purchased/existing key is found, automatically register a free 10-credits key on the server
        if (empty($status['api_key'])) {
            Logger::info('CreditManager: No API key found in poll — calling register_free_key to auto-register');
            $free_res = self::register_free_key($hash);
            if (!empty($free_res['api_key'])) {
                $status = [
                    'api_key' => $free_res['api_key'],
                    'balance' => $free_res['balance'] ?? 10
                ];
            }
        }

        if (!empty($status['api_key'])) {
            $api_key = sanitize_text_field($status['api_key']);
            update_option('packshotphoto_api_key', Encryption::encrypt($api_key));
            $new_status = self::fetch_status_from_api();
            $balance    = (int) ($new_status['credits_balance'] ?? $status['balance'] ?? 0);
            update_option('packshotphoto_credits_balance', $balance);
            Logger::info('CreditManager: force_sync adopted key via hash', ['balance' => $balance]);
            return [
                'success' => true,
                'message' => __('Credits synchronized successfully!', 'packshotphoto-ai'),
                'balance' => $balance,
                'transactions' => $new_status['transactions'] ?? [],
            ];
        }

        // Strategy 3 — fallback: poll by email (hash may mismatch due to AUTH_KEY difference)
        $emails_to_try = array_unique(filter_var_array([
            get_option('admin_email', ''),
            wp_get_current_user()->user_email ?? ''
        ], FILTER_SANITIZE_EMAIL));

        foreach ($emails_to_try as $email) {
            if (empty($email)) continue;
            
            $status_by_email = self::poll_checkout_by_email($email);
            if (!empty($status_by_email['api_key'])) {
                $api_key = sanitize_text_field($status_by_email['api_key']);
                update_option('packshotphoto_api_key', Encryption::encrypt($api_key));
                $new_status = self::fetch_status_from_api();
                $balance    = (int) ($new_status['credits_balance'] ?? $status_by_email['credits_balance'] ?? 0);
                update_option('packshotphoto_credits_balance', $balance);
                Logger::info('CreditManager: force_sync adopted key via email', ['email' => $email, 'balance' => $balance]);
                return [
                    'success' => true,
                    'message' => __('Credits synchronized successfully!', 'packshotphoto-ai'),
                    'balance' => $balance,
                    'transactions' => $new_status['transactions'] ?? [],
                ];
            }
        }

        Logger::warning('CreditManager: force_sync failed — no key found', ['hash' => $hash, 'emails' => $emails_to_try]);
        return [
            'success' => false,
            'message' => __('Could not synchronize credits. Please try again or contact support.', 'packshotphoto-ai'),
            'balance'  => (int) get_option('packshotphoto_credits_balance', 0),
        ];
    }



    /**
     * Poll Supabase for completed checkout (called after redirect)
     *
     * @param string $site_url_hash
     * @return array|null
     */
    public static function poll_checkout_status(string $site_url_hash = ''): ?array {
        if (empty($site_url_hash)) {
            $site_url_hash = self::generate_site_hash();
        }
        $url = self::SUPABASE_URL . '/checkout-session?action=poll&hash=' . urlencode($site_url_hash);
        $response = wp_remote_get($url, [
            'timeout'   => 5,
            'sslverify' => false,
        ]);

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

        $body = json_decode(wp_remote_retrieve_body($response), true);
        return $body ?: null;
    }

    /**
     * Register a new free key on the Supabase server
     *
     * @param string $site_url_hash
     * @param string $email
     * @return array|null
     */
    public static function register_free_key(string $site_url_hash = '', string $email = ''): ?array {
        if (empty($site_url_hash)) {
            $site_url_hash = self::generate_site_hash();
        }
        if (empty($email)) {
            $email = get_option('admin_email', '');
        }

        $url = self::SUPABASE_URL . '/checkout-session?action=register-free&hash=' . urlencode($site_url_hash) . '&email=' . urlencode($email);
        $response = wp_remote_get($url, [
            'timeout'   => 10,
            'sslverify' => false,
        ]);

        if (is_wp_error($response)) {
            Logger::error('CreditManager: register_free_key request failed', ['error' => $response->get_error_message()]);
            return null;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (!empty($body['status']) && $body['status'] === 'success') {
            return $body;
        }
        return null;
    }

    /**
     * Poll Supabase for an API key by admin email address.
     * This is a fallback for when the site hash doesn't match
     * (e.g., AUTH_KEY differs between environments).
     *
     * @param string $email
     * @return array|null
     */
    public static function poll_checkout_by_email(string $email): ?array {
        if (empty($email)) {
            return null;
        }

        $url      = self::SUPABASE_URL . '/checkout-session?action=poll&email=' . urlencode($email);
        $response = wp_remote_get($url, [
            'timeout'   => 8,
            'sslverify' => false,
        ]);

        if (is_wp_error($response)) {
            Logger::warning('CreditManager: poll_checkout_by_email failed', ['error' => $response->get_error_message()]);
            return null;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        Logger::debug('CreditManager: poll_checkout_by_email response', ['email' => $email]);
        return $body ?: null;
    }

    /**
     * Set balance in transient cache
     *
     * @param int $balance
     */
    private static function set_balance_cache(int $balance): void {
        set_transient(self::CACHE_KEY, ['balance' => $balance, 'ts' => time()], self::CACHE_TTL);
    }



    /**
     * Add credits (only called from WebhookHandler — now deprecated in favor of Supabase)
     *
     * @deprecated Credits are now managed server-side by Supabase
     */
    public static function add_credits(int $amount, string $reason = ''): int {
        Logger::warning('CreditManager::add_credits called locally — credits should be managed by Supabase', ['reason' => $reason]);
        $balance = (int) get_option('packshotphoto_credits_balance', 0);
        $new = $balance + $amount;
        update_option('packshotphoto_credits_balance', $new);
        return $new;
    }

    /**
     * Ping Supabase to keep the project active (called weekly via WP Cron)
     */
    public static function keep_alive_ping(): void {
        Logger::info('CreditManager: Running weekly keep-alive ping to Supabase');
        $api_key = Encryption::decrypt(get_option('packshotphoto_api_key', ''));
        if (empty($api_key)) {
            self::force_sync();
            return;
        }

        $validate_url = self::SUPABASE_URL . '/license-validate';
        wp_remote_post($validate_url, [
            'timeout'   => 8,
            'headers'   => ['Content-Type' => 'application/json'],
            'sslverify' => false,
            'body'      => wp_json_encode([
                'api_key'  => $api_key,
                'site_url' => home_url(),
            ]),
        ]);
    }

    /**
     * Verify that the current domain matches the activated domain.
     * If there's a mismatch (e.g., database was cloned), wipe local credentials.
     */
    public static function verify_domain_integrity(): void {
        $activated_url = get_option('packshotphoto_activated_url', '');
        $existing_key  = Encryption::decrypt(get_option('packshotphoto_api_key', ''));

        if (empty($existing_key)) {
            return;
        }

        // If no activated URL is saved yet but we have a key, bind it to current URL
        if (empty($activated_url)) {
            update_option('packshotphoto_activated_url', home_url());
            return;
        }

        $parsed_activated = wp_parse_url($activated_url, PHP_URL_HOST);
        $activated_host = is_string($parsed_activated) ? strtolower($parsed_activated) : '';
        $activated_host = preg_replace('/^www\./', '', $activated_host);

        $parsed_current = wp_parse_url(home_url(), PHP_URL_HOST);
        $current_host   = is_string($parsed_current) ? strtolower($parsed_current) : '';
        $current_host   = preg_replace('/^www\./', '', $current_host);

        // Permit local development environments to bypass the domain mismatch check
        $is_local = in_array($current_host, ['localhost', '127.0.0.1'], true) || 
                    substr($current_host, -6) === '.local' || 
                    substr($current_host, -5) === '.test';

        if ($activated_host !== $current_host && !$is_local) {
            $is_activated_local = in_array($activated_host, ['localhost', '127.0.0.1'], true) || 
                                  substr($activated_host, -6) === '.local' || 
                                  substr($activated_host, -5) === '.test' ||
                                  empty($activated_host);

            if ($is_activated_local) {
                update_option('packshotphoto_activated_url', home_url());
                return;
            }

            Logger::warning('CreditManager: Domain mismatch detected (cloned site). Wiping credentials.', [
                'activated' => $activated_host,
                'current'   => $current_host
            ]);
            delete_option('packshotphoto_api_key');
            delete_option('packshotphoto_credits_balance');
            delete_option('packshotphoto_activated_url');
            delete_transient(self::CACHE_KEY);
        }
    }
}
