<?php

declare(strict_types=1);

namespace Instinctor\Services;

use Instinctor\Core\Config;
use Instinctor\Core\DB;


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

/**
 * Currency Service
 * Handles multi-currency support with exchange rates
 */
class CurrencyService
{
    private static ?array $currencies = null;
    private static ?string $currentCurrency = null;
    private static array $currencyData = [
        'USD' => ['name' => 'US Dollar', 'symbol' => '$', 'decimals' => 2],
        'EUR' => ['name' => 'Euro', 'symbol' => '€', 'decimals' => 2],
        'GBP' => ['name' => 'British Pound', 'symbol' => '£', 'decimals' => 2],
        'CAD' => ['name' => 'Canadian Dollar', 'symbol' => 'C$', 'decimals' => 2],
        'AUD' => ['name' => 'Australian Dollar', 'symbol' => 'A$', 'decimals' => 2],
        'JPY' => ['name' => 'Japanese Yen', 'symbol' => '¥', 'decimals' => 0],
        'CNY' => ['name' => 'Chinese Yuan', 'symbol' => '¥', 'decimals' => 2],
        'INR' => ['name' => 'Indian Rupee', 'symbol' => '₹', 'decimals' => 2],
        'MXN' => ['name' => 'Mexican Peso', 'symbol' => 'MX$', 'decimals' => 2],
        'BRL' => ['name' => 'Brazilian Real', 'symbol' => 'R$', 'decimals' => 2],
        'CHF' => ['name' => 'Swiss Franc', 'symbol' => 'CHF', 'decimals' => 2],
        'SEK' => ['name' => 'Swedish Krona', 'symbol' => 'kr', 'decimals' => 2],
        'NOK' => ['name' => 'Norwegian Krone', 'symbol' => 'kr', 'decimals' => 2],
        'DKK' => ['name' => 'Danish Krone', 'symbol' => 'kr', 'decimals' => 2],
        'PLN' => ['name' => 'Polish Zloty', 'symbol' => 'zł', 'decimals' => 2],
        'KRW' => ['name' => 'South Korean Won', 'symbol' => '₩', 'decimals' => 0],
        'SGD' => ['name' => 'Singapore Dollar', 'symbol' => 'S$', 'decimals' => 2],
        'HKD' => ['name' => 'Hong Kong Dollar', 'symbol' => 'HK$', 'decimals' => 2],
        'NZD' => ['name' => 'New Zealand Dollar', 'symbol' => 'NZ$', 'decimals' => 2],
        'ZAR' => ['name' => 'South African Rand', 'symbol' => 'R', 'decimals' => 2],
        'AED' => ['name' => 'UAE Dirham', 'symbol' => 'د.إ', 'decimals' => 2],
        'ILS' => ['name' => 'Israeli Shekel', 'symbol' => '₪', 'decimals' => 2],
        'THB' => ['name' => 'Thai Baht', 'symbol' => '฿', 'decimals' => 2],
        'PHP' => ['name' => 'Philippine Peso', 'symbol' => '₱', 'decimals' => 2],
        'TWD' => ['name' => 'Taiwan Dollar', 'symbol' => 'NT$', 'decimals' => 0],
        'TRY' => ['name' => 'Turkish Lira', 'symbol' => '₺', 'decimals' => 2],
        'CZK' => ['name' => 'Czech Koruna', 'symbol' => 'Kč', 'decimals' => 2],
        'HUF' => ['name' => 'Hungarian Forint', 'symbol' => 'Ft', 'decimals' => 0],
    ];

    /**
     * Get all enabled currencies
     */
    public static function getEnabledCurrencies(): array
    {
        
        if (self::$currencies !== null) {
            return self::$currencies;
        }

        $table = DB::prefix() . 'instinctor_currencies';
        
        // Check if table exists
        $tableExists = DB::get_var("SHOW TABLES LIKE '$table'") === $table;
        if (!$tableExists) {
            self::$currencies = [];
            return self::$currencies;
        }

        self::$currencies = DB::get_results(
            "SELECT * FROM $table WHERE enabled = 1 ORDER BY is_default DESC, sort_order ASC"
        ) ?: [];

        return self::$currencies;
    }

    /**
     * Get all currencies (including disabled)
     */
    public static function getAllCurrencies(): array
    {
        $table = DB::prefix() . 'instinctor_currencies';

        // Check if table exists
        $tableExists = DB::get_var("SHOW TABLES LIKE '$table'") === $table;
        if (!$tableExists) {
            return [];
        }

        return DB::get_results(
            "SELECT * FROM $table ORDER BY is_default DESC, sort_order ASC"
        ) ?: [];
    }

    /**
     * Get default currency
     */
    public static function getDefaultCurrency(): ?object
    {
        $table = DB::prefix() . 'instinctor_currencies';

        // Check if table exists
        $tableExists = DB::get_var("SHOW TABLES LIKE '$table'") === $table;
        if (!$tableExists) {
            return null;
        }

        $currency = DB::get_row(
            "SELECT * FROM $table WHERE is_default = 1 LIMIT 1"
        );

        if (!$currency) {
            // Fall back to first enabled currency
            $currency = DB::get_row(
                "SELECT * FROM $table WHERE enabled = 1 ORDER BY sort_order ASC LIMIT 1"
            );
        }

        return $currency;
    }

    /**
     * Get currency by code
     */
    public static function getCurrency(string $code): ?object
    {
        $table = DB::prefix() . 'instinctor_currencies';

        return DB::get_row(DB::prepare(
            "SELECT * FROM $table WHERE code = %s",
            strtoupper($code)
        ));
    }

    /**
     * Get current active currency (from session/cookie)
     */
    public static function getCurrentCurrency(): object
    {
        if (self::$currentCurrency !== null) {
            $currency = self::getCurrency(self::$currentCurrency);
            if ($currency) {
                return $currency;
            }
        }

        // Check cookie
        if (isset($_COOKIE['instinctor_currency'])) {
            $raw = preg_replace('/[^A-Z]/', '', strtoupper(substr(sanitize_text_field(wp_unslash($_COOKIE['instinctor_currency'])), 0, 3)));
            $currency = $raw ? self::getCurrency($raw) : null;
            if ($currency && $currency->enabled) {
                self::$currentCurrency = $currency->code;
                return $currency;
            }
        }

        // Auto-detect from browser
        if (Config::get('instinctor_auto_detect_currency')) {
            $detected = self::detectCurrencyFromLocale();
            if ($detected) {
                $currency = self::getCurrency($detected);
                if ($currency && $currency->enabled) {
                    self::$currentCurrency = $currency->code;
                    return $currency;
                }
            }
        }

        // Fall back to default
        $default = self::getDefaultCurrency();
        if ($default) {
            self::$currentCurrency = $default->code;
            return $default;
        }

        // Ultimate fallback - return store base currency
        return (object) [
            'code' => Config::get('instinctor_currency', 'USD'),
            'symbol' => '$',
            'exchange_rate' => 1,
            'decimals' => 2,
            'symbol_position' => Config::get('instinctor_currency_position', 'left'),
            'thousand_separator' => Config::get('instinctor_thousand_separator', ','),
            'decimal_separator' => Config::get('instinctor_decimal_separator', '.'),
        ];
    }

    /**
     * Set current currency
     */
    public static function setCurrentCurrency(string $code): bool
    {
        $currency = self::getCurrency($code);
        if (!$currency || !$currency->enabled) {
            return false;
        }

        self::$currentCurrency = $code;
        setcookie('instinctor_currency', $code, time() + (86400 * 30), '/');
        
        return true;
    }

    /**
     * Convert price from base currency to target currency
     */
    public static function convertPrice(float $price, ?string $targetCode = null): float
    {
        if ($targetCode === null) {
            $target = self::getCurrentCurrency();
        } else {
            $target = self::getCurrency($targetCode);
        }

        if (!$target || $target->exchange_rate == 1) {
            return $price;
        }

        return round($price * floatval($target->exchange_rate), intval($target->decimals));
    }

    /**
     * Convert product price considering per-product currency pricing
     * 
     * @param object $product Product object with meta field
     * @param string $priceField 'price' or 'sale_price'
     * @param string|null $targetCode Target currency code (null = current currency)
     * @return float Converted price
     */
    public static function convertProductPrice(object $product, string $priceField = 'price', ?string $targetCode = null): float
    {
        if ($targetCode === null) {
            $target = self::getCurrentCurrency();
        } else {
            $target = self::getCurrency($targetCode);
        }

        $default = self::getDefaultCurrency();
        
        // If target is default currency or no target, return base price
        if (!$target || ($default && $target->code === $default->code)) {
            return floatval($product->$priceField ?? 0);
        }

        // Check for per-product pricing
        $meta = !empty($product->meta) ? json_decode($product->meta, true) : [];
        $currencyPrices = $meta['currency_prices'] ?? [];
        
        // Determine which key to look for
        $priceKey = $target->code;
        if ($priceField === 'sale_price') {
            $priceKey = $target->code . '_sale';
        }
        
        // If per-product price exists for this currency, use it
        if (isset($currencyPrices[$priceKey]) && $currencyPrices[$priceKey] > 0) {
            return floatval($currencyPrices[$priceKey]);
        }
        
        // Fall back to exchange rate conversion
        $basePrice = floatval($product->$priceField ?? 0);
        if ($basePrice <= 0) {
            return 0;
        }
        
        return round($basePrice * floatval($target->exchange_rate), intval($target->decimals));
    }

    /**
     * Get product price in current currency (formatted)
     */
    public static function getProductPrice(object $product, bool $includeSale = true): string
    {
        $currentCurrency = self::getCurrentCurrency();
        
        // Get regular price
        $regularPrice = self::convertProductPrice($product, 'price');
        
        // Get sale price if applicable
        $salePrice = 0;
        if ($includeSale && !empty($product->sale_price) && floatval($product->sale_price) > 0) {
            $salePrice = self::convertProductPrice($product, 'sale_price');
        }
        
        // Format and return
        if ($salePrice > 0 && $salePrice < $regularPrice) {
            return '<del>' . self::formatPrice($regularPrice) . '</del> <ins>' . self::formatPrice($salePrice) . '</ins>';
        }
        
        return self::formatPrice($regularPrice);
    }

    /**
     * Get raw product prices in current currency
     */
    public static function getProductPrices(object $product): array
    {
        return [
            'regular' => self::convertProductPrice($product, 'price'),
            'sale' => !empty($product->sale_price) ? self::convertProductPrice($product, 'sale_price') : null,
            'currency' => self::getCurrentCurrency()->code,
            'symbol' => self::getSymbol(),
        ];
    }

    /**
     * Format price with currency
     */
    public static function formatPrice($price, ?string $currencyCode = null): string
    {
        $price = (float) $price; // DB decimal columns arrive as strings; coerce so callers needn't cast.
        if ($currencyCode === null) {
            $currency = self::getCurrentCurrency();
        } else {
            $currency = self::getCurrency($currencyCode);
        }

        if (!$currency) {
            $currency = (object) [
                'symbol' => '$',
                'decimals' => 2,
                'symbol_position' => 'left',
                'thousand_separator' => ',',
                'decimal_separator' => '.',
            ];
        }

        $formatted = number_format(
            $price,
            intval($currency->decimals),
            $currency->decimal_separator ?? '.',
            $currency->thousand_separator ?? ','
        );

        $position = $currency->symbol_position ?? 'left';
        $symbol = $currency->symbol ?? '$';

        switch ($position) {
            case 'left':
                return $symbol . $formatted;
            case 'right':
                return $formatted . $symbol;
            case 'left_space':
                return $symbol . ' ' . $formatted;
            case 'right_space':
                return $formatted . ' ' . $symbol;
            default:
                return $symbol . $formatted;
        }
    }

    /**
     * Get currency symbol for current currency
     */
    public static function getSymbol(?string $currencyCode = null): string
    {
        if ($currencyCode === null) {
            $currency = self::getCurrentCurrency();
        } else {
            $currency = self::getCurrency($currencyCode);
        }

        return $currency->symbol ?? '$';
    }

    /**
     * Add or update a currency
     */
    public static function saveCurrency(array $data): int
    {
        $table = DB::prefix() . 'instinctor_currencies';

        $currency = [
            'code' => strtoupper(sanitize_text_field($data['code'] ?? '')),
            'name' => sanitize_text_field($data['name'] ?? ''),
            'symbol' => sanitize_text_field($data['symbol'] ?? ''),
            'exchange_rate' => floatval($data['exchange_rate'] ?? 1),
            'decimals' => intval($data['decimals'] ?? 2),
            'symbol_position' => sanitize_text_field($data['symbol_position'] ?? 'left'),
            'thousand_separator' => sanitize_text_field($data['thousand_separator'] ?? ','),
            'decimal_separator' => sanitize_text_field($data['decimal_separator'] ?? '.'),
            'enabled' => !empty($data['enabled']) ? 1 : 0,
            'is_default' => !empty($data['is_default']) ? 1 : 0,
            'auto_update' => !empty($data['auto_update']) ? 1 : 0,
            'sort_order' => intval($data['sort_order'] ?? 0),
        ];

        // If setting as default, unset other defaults
        if ($currency['is_default']) {
            DB::update($table, ['is_default' => 0], ['is_default' => 1]);
        }

        $existing = self::getCurrency($currency['code']);

        if ($existing) {
            DB::update($table, $currency, ['id' => $existing->id]);
            return (int) $existing->id;
        } else {
            DB::insert($table, $currency);
            return (int) DB::insert_id();
        }
    }

    /**
     * Delete a currency
     */
    public static function deleteCurrency(int $id): bool
    {
        $table = DB::prefix() . 'instinctor_currencies';

        // Don't delete the default currency
        $currency = DB::get_row(DB::prepare("SELECT * FROM $table WHERE id = %d", $id));
        if ($currency && $currency->is_default) {
            return false;
        }

        return DB::delete($table, ['id' => $id]) !== false;
    }

    /**
     * Get available currency codes (for adding new)
     */
    public static function getAvailableCurrencyCodes(): array
    {
        return self::$currencyData;
    }

    /**
     * Get currency info by code (from static data)
     */
    public static function getCurrencyInfo(string $code): ?array
    {
        $code = strtoupper($code);
        return self::$currencyData[$code] ?? null;
    }

    /**
     * Detect currency from browser locale
     */
    private static function detectCurrencyFromLocale(): ?string
    {
        $locale = isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])
            ? substr(preg_replace('/[^a-zA-Z\-]/', '', sanitize_text_field(wp_unslash((string) $_SERVER['HTTP_ACCEPT_LANGUAGE']))), 0, 5)
            : '';

        $localeToCurrency = [
            'en-US' => 'USD',
            'en-GB' => 'GBP',
            'en-AU' => 'AUD',
            'en-CA' => 'CAD',
            'en-NZ' => 'NZD',
            'de' => 'EUR',
            'fr' => 'EUR',
            'es' => 'EUR',
            'it' => 'EUR',
            'pt-BR' => 'BRL',
            'ja' => 'JPY',
            'zh' => 'CNY',
            'ko' => 'KRW',
            'pl' => 'PLN',
            'sv' => 'SEK',
            'no' => 'NOK',
            'da' => 'DKK',
        ];

        // Check full locale first
        if (isset($localeToCurrency[$locale])) {
            return $localeToCurrency[$locale];
        }

        // Check just language
        $lang = substr($locale, 0, 2);
        if (isset($localeToCurrency[$lang])) {
            return $localeToCurrency[$lang];
        }

        return null;
    }

    /**
     * Update exchange rates from API
     */
    public static function updateExchangeRates(): bool
    {
        $apiKey = Config::get('instinctor_exchange_rate_api');
        if (empty($apiKey)) {
            return false;
        }

        $default = self::getDefaultCurrency();
        if (!$default) {
            return false;
        }

        // Using exchangerate-api.com
        $response = wp_remote_get(
            "https://v6.exchangerate-api.com/v6/{$apiKey}/latest/{$default->code}"
        );

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

        // WP.org B2 — force array shape; a 429 rate-limit returns a bare string.
        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (!is_array($body) || ($body['result'] ?? '') !== 'success') {
            return false;
        }

        $rates = $body['conversion_rates'] ?? [];
        $currencies = self::getAllCurrencies();

        $table = DB::prefix() . 'instinctor_currencies';

        foreach ($currencies as $currency) {
            if ($currency->code === $default->code) {
                continue; // Skip default
            }

            if (!$currency->auto_update) {
                continue; // Skip manually managed rates
            }

            if (isset($rates[$currency->code])) {
                DB::update(
                    $table,
                    ['exchange_rate' => $rates[$currency->code]],
                    ['id' => $currency->id]
                );
            }
        }

        // Clear cache
        self::$currencies = null;

        return true;
    }

    /**
     * Initialize default currencies
     */
    public static function initializeDefaults(): void
    {
        $baseCurrency = Config::get('instinctor_currency', 'USD');
        $existing = self::getAllCurrencies();

        if (!empty($existing)) {
            return;
        }

        // Add base currency as default
        $info = self::getCurrencyInfo($baseCurrency) ?? ['name' => $baseCurrency, 'symbol' => '$', 'decimals' => 2];
        
        self::saveCurrency([
            'code' => $baseCurrency,
            'name' => $info['name'],
            'symbol' => $info['symbol'],
            'exchange_rate' => 1,
            'decimals' => $info['decimals'],
            'symbol_position' => Config::get('instinctor_currency_position', 'left'),
            'thousand_separator' => Config::get('instinctor_thousand_separator', ','),
            'decimal_separator' => Config::get('instinctor_decimal_separator', '.'),
            'enabled' => 1,
            'is_default' => 1,
        ]);
    }

    /**
     * Get currency selector HTML for frontend
     */
    public static function getCurrencySelectorHtml(): string
    {
        $currencies = self::getEnabledCurrencies();
        if (count($currencies) < 2) {
            return '';
        }

        $current = self::getCurrentCurrency();
        $html = '<div class="instinctor-currency-selector">';
        $html .= '<select id="instinctor-currency-select">';

        foreach ($currencies as $currency) {
            $selected = ($currency->code === $current->code) ? ' selected' : '';
            $html .= sprintf(
                '<option value="%s"%s>%s (%s)</option>',
                esc_attr($currency->code),
                $selected,
                esc_html($currency->code),
                esc_html($currency->symbol)
            );
        }

        $html .= '</select></div>';

        return $html;
    }
}
