import requests
from flask import Blueprint, abort, render_template, request, jsonify, current_app, g, redirect
from datetime import datetime, timedelta
from functools import lru_cache
import time
import threading
from helpers import get_page_faqs, normalize_page_id

currency_bp = Blueprint('currency', __name__)


@currency_bp.before_request
def _normalize_url_case():
    path = request.path
    if path.startswith('/currency/') and path != path.lower():
        lowered = path.lower()
        qs = request.query_string
        if qs:
            return redirect(lowered + '?' + qs.decode('utf-8'), 301)
        return redirect(lowered, 301)


EXCHANGERATE_API = 'https://open.er-api.com/v6/latest'
CURRENCY_API_BASE = 'https://latest.currency-api.pages.dev/v1/currencies'
FRANKFURTER_API = 'https://api.frankfurter.app'
COINGECKO_API = 'https://api.coingecko.com/api/v3'
COINPAPRIKA_API = 'https://api.coinpaprika.com/v1'
FLAG_CDN = "https://flagcdn.com/w320/{}.png"
CRYPTO_ICON_CDN = "https://assets.coingecko.com/coins/images/{}/small/{}"

CRYPTO_CURRENCIES = {
    'btc': {'name': 'Bitcoin', 'symbol': '\u20bf', 'coingecko_id': 'bitcoin', 'paprika_id': 'btc-bitcoin', 'icon': 'https://assets.coingecko.com/coins/images/1/small/bitcoin.png'},
    'eth': {'name': 'Ethereum', 'symbol': '\u039e', 'coingecko_id': 'ethereum', 'paprika_id': 'eth-ethereum', 'icon': 'https://assets.coingecko.com/coins/images/279/small/ethereum.png'},
    'xrp': {'name': 'Ripple', 'symbol': 'XRP', 'coingecko_id': 'ripple', 'paprika_id': 'xrp-xrp', 'icon': 'https://assets.coingecko.com/coins/images/44/small/xrp-symbol-white-128.png'},
    'ltc': {'name': 'Litecoin', 'symbol': '\u0141', 'coingecko_id': 'litecoin', 'paprika_id': 'ltc-litecoin', 'icon': 'https://assets.coingecko.com/coins/images/2/small/litecoin.png'},
    'doge': {'name': 'Dogecoin', 'symbol': '\u00d0', 'coingecko_id': 'dogecoin', 'paprika_id': 'doge-dogecoin', 'icon': 'https://assets.coingecko.com/coins/images/5/small/dogecoin.png'},
    'sol': {'name': 'Solana', 'symbol': 'SOL', 'coingecko_id': 'solana', 'paprika_id': 'sol-solana', 'icon': 'https://assets.coingecko.com/coins/images/4128/small/solana.png'},
    'ada': {'name': 'Cardano', 'symbol': 'ADA', 'coingecko_id': 'cardano', 'paprika_id': 'ada-cardano', 'icon': 'https://assets.coingecko.com/coins/images/975/small/cardano.png'},
    'usdt': {'name': 'Tether', 'symbol': '\u20ae', 'coingecko_id': 'tether', 'paprika_id': 'usdt-tether', 'icon': 'https://assets.coingecko.com/coins/images/325/small/Tether.png'},
    'bnb': {'name': 'BNB', 'symbol': 'BNB', 'coingecko_id': 'binancecoin', 'paprika_id': 'bnb-binance-coin', 'icon': 'https://assets.coingecko.com/coins/images/825/small/bnb-icon2_2x.png'},
    'usdc': {'name': 'USD Coin', 'symbol': 'USDC', 'coingecko_id': 'usd-coin', 'paprika_id': 'usdc-usd-coin', 'icon': 'https://assets.coingecko.com/coins/images/6319/small/usdc.png'},
    'dot': {'name': 'Polkadot', 'symbol': 'DOT', 'coingecko_id': 'polkadot', 'paprika_id': 'dot-polkadot', 'icon': 'https://assets.coingecko.com/coins/images/12171/small/polkadot.png'},
    'matic': {'name': 'Polygon', 'symbol': 'MATIC', 'coingecko_id': 'matic-network', 'paprika_id': 'matic-polygon', 'icon': 'https://assets.coingecko.com/coins/images/4713/small/polygon.png'},
    'avax': {'name': 'Avalanche', 'symbol': 'AVAX', 'coingecko_id': 'avalanche-2', 'paprika_id': 'avax-avalanche', 'icon': 'https://assets.coingecko.com/coins/images/12559/small/Avalanche_Circle_RedWhite_Trans.png'},
    'link': {'name': 'Chainlink', 'symbol': 'LINK', 'coingecko_id': 'chainlink', 'paprika_id': 'link-chainlink', 'icon': 'https://assets.coingecko.com/coins/images/877/small/chainlink-new-logo.png'},
    'atom': {'name': 'Cosmos', 'symbol': 'ATOM', 'coingecko_id': 'cosmos', 'paprika_id': 'atom-cosmos', 'icon': 'https://assets.coingecko.com/coins/images/1481/small/cosmos_hub.png'},
}

CURRENCY_NAMES = {
    'AED': 'UAE Dirham', 'AFN': 'Afghan Afghani', 'ALL': 'Albanian Lek', 'AMD': 'Armenian Dram',
    'ANG': 'Netherlands Antillean Guilder', 'AOA': 'Angolan Kwanza', 'ARS': 'Argentine Peso',
    'AUD': 'Australian Dollar', 'AWG': 'Aruban Florin', 'AZN': 'Azerbaijani Manat',
    'BAM': 'Bosnia-Herzegovina Mark', 'BBD': 'Barbadian Dollar', 'BDT': 'Bangladeshi Taka',
    'BGN': 'Bulgarian Lev', 'BHD': 'Bahraini Dinar', 'BIF': 'Burundian Franc',
    'BMD': 'Bermudan Dollar', 'BND': 'Brunei Dollar', 'BOB': 'Bolivian Boliviano',
    'BRL': 'Brazilian Real', 'BSD': 'Bahamian Dollar', 'BTN': 'Bhutanese Ngultrum',
    'BWP': 'Botswanan Pula', 'BYN': 'Belarusian Ruble', 'BZD': 'Belize Dollar',
    'CAD': 'Canadian Dollar', 'CDF': 'Congolese Franc', 'CHF': 'Swiss Franc',
    'CLP': 'Chilean Peso', 'CNY': 'Chinese Yuan', 'COP': 'Colombian Peso',
    'CRC': 'Costa Rican Col\u00f3n', 'CUP': 'Cuban Peso', 'CVE': 'Cape Verdean Escudo',
    'CZK': 'Czech Koruna', 'DJF': 'Djiboutian Franc', 'DKK': 'Danish Krone',
    'DOP': 'Dominican Peso', 'DZD': 'Algerian Dinar', 'EGP': 'Egyptian Pound',
    'ERN': 'Eritrean Nakfa', 'ETB': 'Ethiopian Birr', 'EUR': 'Euro',
    'FJD': 'Fijian Dollar', 'FKP': 'Falkland Islands Pound', 'FOK': 'Faroese Kr\u00f3na',
    'GBP': 'British Pound', 'GEL': 'Georgian Lari', 'GGP': 'Guernsey Pound',
    'GHS': 'Ghanaian Cedi', 'GIP': 'Gibraltar Pound', 'GMD': 'Gambian Dalasi',
    'GNF': 'Guinean Franc', 'GTQ': 'Guatemalan Quetzal', 'GYD': 'Guyanaese Dollar',
    'HKD': 'Hong Kong Dollar', 'HNL': 'Honduran Lempira', 'HRK': 'Croatian Kuna',
    'HTG': 'Haitian Gourde', 'HUF': 'Hungarian Forint', 'IDR': 'Indonesian Rupiah',
    'ILS': 'Israeli New Shekel', 'IMP': 'Isle of Man Pound', 'INR': 'Indian Rupee',
    'IQD': 'Iraqi Dinar', 'IRR': 'Iranian Rial', 'ISK': 'Icelandic Kr\u00f3na',
    'JEP': 'Jersey Pound', 'JMD': 'Jamaican Dollar', 'JOD': 'Jordanian Dinar',
    'JPY': 'Japanese Yen', 'KES': 'Kenyan Shilling', 'KGS': 'Kyrgystani Som',
    'KHR': 'Cambodian Riel', 'KID': 'Kiribati Dollar', 'KMF': 'Comorian Franc',
    'KRW': 'South Korean Won', 'KWD': 'Kuwaiti Dinar', 'KYD': 'Cayman Islands Dollar',
    'KZT': 'Kazakhstani Tenge', 'LAK': 'Laotian Kip', 'LBP': 'Lebanese Pound',
    'LKR': 'Sri Lankan Rupee', 'LRD': 'Liberian Dollar', 'LSL': 'Lesotho Loti',
    'LYD': 'Libyan Dinar', 'MAD': 'Moroccan Dirham', 'MDL': 'Moldovan Leu',
    'MGA': 'Malagasy Ariary', 'MKD': 'Macedonian Denar', 'MMK': 'Myanmar Kyat',
    'MNT': 'Mongolian Tugrik', 'MOP': 'Macanese Pataca', 'MRU': 'Mauritanian Ouguiya',
    'MUR': 'Mauritian Rupee', 'MVR': 'Maldivian Rufiyaa', 'MWK': 'Malawian Kwacha',
    'MXN': 'Mexican Peso', 'MYR': 'Malaysian Ringgit', 'MZN': 'Mozambican Metical',
    'NAD': 'Namibian Dollar', 'NGN': 'Nigerian Naira', 'NIO': 'Nicaraguan C\u00f3rdoba',
    'NOK': 'Norwegian Krone', 'NPR': 'Nepalese Rupee', 'NZD': 'New Zealand Dollar',
    'OMR': 'Omani Rial', 'PAB': 'Panamanian Balboa', 'PEN': 'Peruvian Sol',
    'PGK': 'Papua New Guinean Kina', 'PHP': 'Philippine Peso', 'PKR': 'Pakistani Rupee',
    'PLN': 'Polish Zloty', 'PYG': 'Paraguayan Guarani', 'QAR': 'Qatari Riyal',
    'RON': 'Romanian Leu', 'RSD': 'Serbian Dinar', 'RUB': 'Russian Ruble',
    'RWF': 'Rwandan Franc', 'SAR': 'Saudi Riyal', 'SBD': 'Solomon Islands Dollar',
    'SCR': 'Seychellois Rupee', 'SDG': 'Sudanese Pound', 'SEK': 'Swedish Krona',
    'SGD': 'Singapore Dollar', 'SHP': 'Saint Helena Pound', 'SLE': 'Sierra Leonean Leone',
    'SOS': 'Somali Shilling', 'SRD': 'Surinamese Dollar', 'SSP': 'South Sudanese Pound',
    'STN': 'S\u00e3o Tom\u00e9 & Pr\u00edncipe Dobra', 'SYP': 'Syrian Pound', 'SZL': 'Swazi Lilangeni',
    'THB': 'Thai Baht', 'TJS': 'Tajikistani Somoni', 'TMT': 'Turkmenistani Manat',
    'TND': 'Tunisian Dinar', 'TOP': 'Tongan Pa\u02bbanga', 'TRY': 'Turkish Lira',
    'TTD': 'Trinidad & Tobago Dollar', 'TVD': 'Tuvaluan Dollar', 'TWD': 'New Taiwan Dollar',
    'TZS': 'Tanzanian Shilling', 'UAH': 'Ukrainian Hryvnia', 'UGX': 'Ugandan Shilling',
    'USD': 'US Dollar', 'UYU': 'Uruguayan Peso', 'UZS': 'Uzbekistani Som',
    'VES': 'Venezuelan Bol\u00edvar', 'VND': 'Vietnamese Dong', 'VUV': 'Vanuatu Vatu',
    'WST': 'Samoan Tala', 'XAF': 'Central African CFA Franc', 'XCD': 'East Caribbean Dollar',
    'XDR': 'Special Drawing Rights', 'XOF': 'West African CFA Franc', 'XPF': 'CFP Franc',
    'YER': 'Yemeni Rial', 'ZAR': 'South African Rand', 'ZMW': 'Zambian Kwacha',
    'ZWL': 'Zimbabwean Dollar',
}

CURRENCY_SYMBOLS = {
    'USD': '$', 'EUR': '\u20ac', 'GBP': '\u00a3', 'JPY': '\u00a5', 'CNY': '\u00a5',
    'INR': '\u20b9', 'CAD': 'C$', 'AUD': 'A$', 'CHF': 'Fr', 'HKD': 'HK$',
    'SGD': 'S$', 'SEK': 'kr', 'KRW': '\u20a9', 'NOK': 'kr', 'NZD': 'NZ$',
    'MXN': '$', 'BRL': 'R$', 'RUB': '\u20bd', 'ZAR': 'R', 'TRY': '\u20ba',
    'PLN': 'z\u0142', 'THB': '\u0e3f', 'IDR': 'Rp', 'HUF': 'Ft', 'CZK': 'K\u010d',
    'ILS': '\u20aa', 'PHP': '\u20b1', 'MYR': 'RM', 'RON': 'lei', 'BGN': '\u043b\u0432',
    'DKK': 'kr', 'TWD': 'NT$', 'AED': 'AED', 'SAR': 'SAR', 'EGP': 'E\u00a3',
    'PKR': '\u20a8', 'NGN': '\u20a6', 'BDT': '\u09f3', 'VND': '\u20ab', 'COP': '$',
    'ARS': '$', 'CLP': '$', 'PEN': 'S/', 'UAH': '\u20b4', 'KZT': '\u20b8',
    'QAR': 'QR', 'KWD': 'KD', 'BHD': 'BD', 'OMR': 'OMR', 'JOD': 'JD',
}

FLAG_MAP = {
    'USD': 'us', 'EUR': 'eu', 'GBP': 'gb', 'JPY': 'jp', 'CNY': 'cn', 'INR': 'in',
    'CAD': 'ca', 'AUD': 'au', 'CHF': 'ch', 'HKD': 'hk', 'SGD': 'sg', 'SEK': 'se',
    'KRW': 'kr', 'NOK': 'no', 'NZD': 'nz', 'MXN': 'mx', 'BRL': 'br', 'RUB': 'ru',
    'ZAR': 'za', 'TRY': 'tr', 'PLN': 'pl', 'THB': 'th', 'IDR': 'id', 'HUF': 'hu',
    'CZK': 'cz', 'ILS': 'il', 'PHP': 'ph', 'MYR': 'my', 'RON': 'ro', 'BGN': 'bg',
    'DKK': 'dk', 'TWD': 'tw', 'AED': 'ae', 'SAR': 'sa', 'EGP': 'eg', 'PKR': 'pk',
    'NGN': 'ng', 'BDT': 'bd', 'VND': 'vn', 'COP': 'co', 'ARS': 'ar', 'CLP': 'cl',
    'PEN': 'pe', 'UAH': 'ua', 'KZT': 'kz', 'QAR': 'qa', 'KWD': 'kw', 'BHD': 'bh',
    'OMR': 'om', 'JOD': 'jo', 'ISK': 'is', 'HRK': 'hr', 'RSD': 'rs', 'GEL': 'ge',
    'MAD': 'ma', 'TND': 'tn', 'LKR': 'lk', 'NPR': 'np', 'KES': 'ke', 'GHS': 'gh',
    'TZS': 'tz', 'UGX': 'ug', 'MMK': 'mm', 'KHR': 'kh', 'LAK': 'la', 'JMD': 'jm',
    'TTD': 'tt', 'DOP': 'do', 'GTQ': 'gt', 'HNL': 'hn', 'CRC': 'cr', 'BOB': 'bo',
    'PYG': 'py', 'UYU': 'uy', 'BZD': 'bz', 'FJD': 'fj', 'AMD': 'am', 'KGS': 'kg',
    'LBP': 'lb', 'IQD': 'iq', 'SYP': 'sy', 'LYD': 'ly', 'DZD': 'dz', 'ETB': 'et',
    'MKD': 'mk', 'MDL': 'md', 'NAD': 'na', 'BWP': 'bw', 'BYN': 'by', 'AZN': 'az',
    'BAM': 'ba', 'BND': 'bn', 'AFN': 'af', 'ANG': 'an', 'ALL': 'al', 'AOA': 'ao',
    'AWG': 'aw', 'BBD': 'bb', 'BIF': 'bi', 'BMD': 'bm', 'BSD': 'bs', 'BTN': 'bt',
    'CDF': 'cd', 'CUP': 'cu', 'CVE': 'cv', 'DJF': 'dj', 'ERN': 'er', 'FKP': 'fk',
    'GGP': 'gg', 'GIP': 'gi', 'GMD': 'gm', 'GNF': 'gn', 'GYD': 'gy', 'HTG': 'ht',
    'IMP': 'im', 'IRR': 'ir', 'JEP': 'je', 'KID': 'ki', 'KMF': 'km', 'KYD': 'ky',
    'LRD': 'lr', 'LSL': 'ls', 'MGA': 'mg', 'MNT': 'mn', 'MOP': 'mo', 'MRU': 'mr',
    'MUR': 'mu', 'MVR': 'mv', 'MWK': 'mw', 'MZN': 'mz', 'NIO': 'ni', 'PAB': 'pa',
    'PGK': 'pg', 'RWF': 'rw', 'SBD': 'sb', 'SCR': 'sc', 'SDG': 'sd', 'SHP': 'sh',
    'SLE': 'sl', 'SOS': 'so', 'SRD': 'sr', 'SSP': 'ss', 'STN': 'st', 'SZL': 'sz',
    'TJS': 'tj', 'TMT': 'tm', 'TOP': 'to', 'TVD': 'tv', 'UZS': 'uz', 'VES': 've',
    'VUV': 'vu', 'WST': 'ws', 'XAF': 'cm', 'XCD': 'ag', 'XOF': 'sn', 'XPF': 'pf',
    'YER': 'ye', 'ZMW': 'zm', 'ZWL': 'zw', 'FOK': 'fo',
}

_cache = {}
_cache_lock = threading.Lock()

def _cached_get(key, ttl_seconds, fetch_fn):
    now = time.time()
    with _cache_lock:
        if key in _cache:
            val, ts = _cache[key]
            if now - ts < ttl_seconds:
                return val
    try:
        val = fetch_fn()
        with _cache_lock:
            _cache[key] = (val, now)
        return val
    except Exception as e:
        with _cache_lock:
            if key in _cache:
                return _cache[key][0]
        raise e


def is_crypto(code):
    return code.lower() in CRYPTO_CURRENCIES


def get_currency_icon(code):
    code_upper = code.upper()
    code_lower = code.lower()
    if code_lower in CRYPTO_CURRENCIES:
        return CRYPTO_CURRENCIES[code_lower].get('icon', '')
    country = FLAG_MAP.get(code_upper, code_lower[:2])
    return FLAG_CDN.format(country)


def get_currency_symbol(code):
    code_upper = code.upper()
    if code_upper in CURRENCY_SYMBOLS:
        return CURRENCY_SYMBOLS[code_upper]
    code_lower = code.lower()
    if code_lower in CRYPTO_CURRENCIES:
        return CRYPTO_CURRENCIES[code_lower]['symbol']
    return ''


def get_currency_name(code):
    code_upper = code.upper()
    if code_upper in CURRENCY_NAMES:
        return CURRENCY_NAMES[code_upper]
    code_lower = code.lower()
    if code_lower in CRYPTO_CURRENCIES:
        return CRYPTO_CURRENCIES[code_lower]['name']
    return code_upper


def _build_currency_info(code):
    code_lower = code.lower()
    code_upper = code.upper()
    return {
        'name': get_currency_name(code),
        'type': 'crypto' if is_crypto(code) else 'fiat',
        'symbol': get_currency_symbol(code),
        'icon_url': get_currency_icon(code),
    }


def get_currencies_data():
    def fetch():
        currencies = {}
        try:
            resp = requests.get(f"{EXCHANGERATE_API}/USD", timeout=8)
            if resp.status_code == 200:
                data = resp.json()
                for code in data.get('rates', {}).keys():
                    if len(code) == 3:
                        currencies[code.lower()] = _build_currency_info(code)
        except Exception:
            try:
                resp = requests.get(f"{CURRENCY_API_BASE}/usd.json", timeout=5)
                if resp.status_code == 200:
                    data = resp.json()
                    for code in data.get('usd', {}).keys():
                        if len(code) == 3:
                            currencies[code.lower()] = _build_currency_info(code)
            except Exception:
                pass

        if not currencies:
            for code in CURRENCY_NAMES:
                currencies[code.lower()] = _build_currency_info(code)

        for code, info in CRYPTO_CURRENCIES.items():
            currencies[code] = _build_currency_info(code)

        return dict(sorted(currencies.items()))

    return _cached_get('currencies_data', 3600, fetch)


def get_exchange_rates(base):
    base_lower = base.lower()
    base_upper = base.upper()

    if is_crypto(base_lower):
        return _get_crypto_rates(base_lower)

    def fetch():
        try:
            resp = requests.get(f"{EXCHANGERATE_API}/{base_upper}", timeout=8)
            if resp.status_code == 200:
                data = resp.json()
                rates = {}
                for code, rate in data.get('rates', {}).items():
                    rates[code.lower()] = rate
                date_str = data.get('time_last_update_utc', '')
                if date_str:
                    try:
                        dt = datetime.strptime(date_str, '%a, %d %b %Y %H:%M:%S %z')
                        date_str = dt.strftime('%Y-%m-%d')
                    except Exception:
                        date_str = datetime.now().strftime('%Y-%m-%d')
                _add_crypto_rates_to_fiat(rates, base_upper)
                return rates, date_str
        except Exception:
            pass
        try:
            resp = requests.get(f"{CURRENCY_API_BASE}/{base_lower}.json", timeout=5)
            if resp.status_code == 200:
                data = resp.json()
                rates = data.get(base_lower, {})
                _add_crypto_rates_to_fiat(rates, base_upper)
                return rates, data.get('date', datetime.now().strftime('%Y-%m-%d'))
        except Exception:
            pass
        return {}, ''

    return _cached_get(f'rates_{base_lower}', 600, fetch)


def _add_crypto_rates_to_fiat(rates, base_upper):
    try:
        ids = ','.join(c['coingecko_id'] for c in CRYPTO_CURRENCIES.values())
        vs = base_upper.lower()
        resp = requests.get(
            f"{COINGECKO_API}/simple/price?ids={ids}&vs_currencies={vs}",
            timeout=8
        )
        if resp.status_code == 200:
            data = resp.json()
            for code, info in CRYPTO_CURRENCIES.items():
                cg_id = info['coingecko_id']
                if cg_id in data and vs in data[cg_id]:
                    price_in_base = data[cg_id][vs]
                    if price_in_base and price_in_base > 0:
                        rates[code] = 1.0 / price_in_base
    except Exception:
        pass


def _get_crypto_rates(crypto_code):
    def fetch():
        info = CRYPTO_CURRENCIES.get(crypto_code)
        if not info:
            return {}, ''
        cg_id = info['coingecko_id']
        try:
            fiat_codes = ['usd', 'eur', 'gbp', 'jpy', 'cny', 'inr', 'cad', 'aud',
                          'chf', 'hkd', 'sgd', 'sek', 'krw', 'nok', 'nzd', 'mxn',
                          'brl', 'rub', 'zar', 'try', 'pln', 'thb', 'idr', 'huf',
                          'czk', 'ils', 'php', 'myr', 'ron', 'bgn', 'dkk', 'twd',
                          'aed', 'sar', 'pkr', 'ngn', 'bdt', 'vnd', 'ars', 'clp']
            vs_str = ','.join(fiat_codes)
            resp = requests.get(
                f"{COINGECKO_API}/simple/price?ids={cg_id}&vs_currencies={vs_str}",
                timeout=8
            )
            rates = {}
            date_str = datetime.now().strftime('%Y-%m-%d')
            if resp.status_code == 200:
                data = resp.json()
                prices = data.get(cg_id, {})
                for fiat, price in prices.items():
                    if price and price > 0:
                        rates[fiat.lower()] = price
                for other_code, other_info in CRYPTO_CURRENCIES.items():
                    if other_code != crypto_code:
                        other_cg = other_info['coingecko_id']
                        try:
                            r2 = requests.get(
                                f"{COINGECKO_API}/simple/price?ids={cg_id},{other_cg}&vs_currencies=usd",
                                timeout=5
                            )
                            if r2.status_code == 200:
                                d2 = r2.json()
                                p1 = d2.get(cg_id, {}).get('usd', 0)
                                p2 = d2.get(other_cg, {}).get('usd', 0)
                                if p1 and p2:
                                    rates[other_code] = p1 / p2
                        except Exception:
                            pass
            return rates, date_str
        except Exception:
            return {}, ''

    return _cached_get(f'crypto_rates_{crypto_code}', 300, fetch)


def get_crypto_stats(code):
    code_lower = code.lower()
    if code_lower not in CRYPTO_CURRENCIES:
        return None

    def fetch():
        info = CRYPTO_CURRENCIES[code_lower]
        stats = {
            'code': code_lower.upper(),
            'name': info['name'],
            'price': None, 'market_cap': None, 'volume_24h': None,
            'change_24h': None, 'change_7d': None, 'change_30d': None,
            'ath': None, 'ath_date': None, 'circulating_supply': None, 'rank': None,
        }
        try:
            cg_id = info['coingecko_id']
            resp = requests.get(
                f"{COINGECKO_API}/simple/price?ids={cg_id}&vs_currencies=usd"
                f"&include_24hr_change=true&include_market_cap=true&include_24hr_vol=true",
                timeout=8
            )
            if resp.status_code == 200:
                data = resp.json().get(cg_id, {})
                stats['price'] = data.get('usd')
                stats['market_cap'] = data.get('usd_market_cap')
                stats['volume_24h'] = data.get('usd_24h_vol')
                stats['change_24h'] = data.get('usd_24h_change')
        except Exception:
            pass
        try:
            pp_id = info['paprika_id']
            resp = requests.get(f"{COINPAPRIKA_API}/tickers/{pp_id}?quotes=USD", timeout=8)
            if resp.status_code == 200:
                data = resp.json()
                q = data.get('quotes', {}).get('USD', {})
                stats['change_7d'] = q.get('percent_change_7d')
                stats['change_30d'] = q.get('percent_change_30d')
                stats['ath'] = q.get('ath_price')
                stats['ath_date'] = q.get('ath_date', '')[:10] if q.get('ath_date') else None
                stats['circulating_supply'] = data.get('circulating_supply')
                stats['rank'] = data.get('rank')
                if stats['price'] is None:
                    stats['price'] = q.get('price')
                if stats['market_cap'] is None:
                    stats['market_cap'] = q.get('market_cap')
                if stats['volume_24h'] is None:
                    stats['volume_24h'] = q.get('volume_24h')
                if stats['change_24h'] is None:
                    stats['change_24h'] = q.get('percent_change_24h')
        except Exception:
            pass
        return stats

    return _cached_get(f'crypto_stats_{code_lower}', 300, fetch)


def get_rate_changes(base_upper):
    def fetch():
        changes = {}
        try:
            yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
            resp_y = requests.get(f"{FRANKFURTER_API}/{yesterday}?from={base_upper}", timeout=8)
            resp_t = requests.get(f"{EXCHANGERATE_API}/{base_upper}", timeout=8)
            rates_y = {}
            rates_t = {}
            if resp_y.status_code == 200:
                rates_y = resp_y.json().get('rates', {})
            if resp_t.status_code == 200:
                rates_t = resp_t.json().get('rates', {})
            for code in rates_t:
                y_val = rates_y.get(code)
                if y_val and y_val != 0:
                    change_pct = ((rates_t[code] - y_val) / y_val) * 100
                    changes[code.lower()] = round(change_pct, 4)
        except Exception:
            pass
        return changes

    return _cached_get(f'rate_changes_{base_upper}', 14400, fetch)


@currency_bp.route("/<lang_code>/currencies")
@currency_bp.route('/currencies')
def currencies():
    all_currencies = get_currencies_data()

    q = request.args.get("q", "").lower()
    filter_type = request.args.get("type", "all").lower()
    filtered = {}

    for code, info in all_currencies.items():
        name = info.get("name", "").lower()
        currency_type = info.get("type", "fiat")
        matches_search = (q in code.lower() or q in name or not q)
        matches_type = (filter_type == "all" or currency_type == filter_type)
        if matches_search and matches_type:
            filtered[code] = info

    filtered = dict(sorted(filtered.items()))

    popular_rates = {}
    try:
        rates, _ = get_exchange_rates('usd')
        popular_pairs = {'eur': rates.get('eur'), 'gbp': rates.get('gbp'),
                         'jpy': rates.get('jpy'), 'cny': rates.get('cny')}
        popular_rates = {k: v for k, v in popular_pairs.items() if v}
        for cc in ['btc', 'eth', 'sol']:
            if cc in rates:
                popular_rates[cc] = rates[cc]
    except Exception:
        pass

    crypto_stats_map = {}
    for cc in ['btc', 'eth', 'sol', 'xrp', 'doge']:
        try:
            s = get_crypto_stats(cc)
            if s:
                crypto_stats_map[cc] = s
        except Exception:
            pass

    lang = getattr(g, 'lang_code', 'en')
    page_id = normalize_page_id(static_page='currencies')
    page_faqs = get_page_faqs(page_id, lang)
    return render_template('currencies.html',
                           currencies=filtered,
                           q=q,
                           filter_type=filter_type,
                           popular_rates=popular_rates,
                           crypto_stats=crypto_stats_map,
                           page_id=page_id,
                           page_faqs=page_faqs)


@currency_bp.route("/<lang_code>/currency/<base>")
@currency_bp.route('/currency/<base>')
def currency_all(base):
    base = base.lower()
    currencies_data = get_currencies_data()

    if base not in currencies_data:
        return render_template('404.html'), 404

    try:
        rates, date = get_exchange_rates(base)
    except Exception:
        rates, date = {}, datetime.now().strftime('%Y-%m-%d')

    if not rates and is_crypto(base):
        rates = {}
        date = datetime.now().strftime('%Y-%m-%d')
        try:
            info = CRYPTO_CURRENCIES.get(base)
            if info:
                cg_id = info['coingecko_id']
                resp = requests.get(
                    f"{COINGECKO_API}/simple/price?ids={cg_id}&vs_currencies=usd",
                    timeout=8
                )
                if resp.status_code == 200:
                    price = resp.json().get(cg_id, {}).get('usd')
                    if price:
                        rates['usd'] = price
                        try:
                            fiat_rates, _ = get_exchange_rates('usd')
                            for code in ['eur', 'gbp', 'jpy', 'cny', 'inr', 'cad', 'aud', 'chf']:
                                if code in fiat_rates:
                                    rates[code] = price * fiat_rates[code]
                        except Exception:
                            pass
        except Exception:
            pass

    if not rates and not is_crypto(base):
        return render_template('404.html'), 404

    api_unavailable = not rates and is_crypto(base)

    enriched_rates = {}
    for code, rate in rates.items():
        if code == base:
            continue
        info = currencies_data.get(code)
        if info:
            entry = info.copy()
            entry['rate'] = rate
            enriched_rates[code] = entry
        elif len(code) == 3:
            entry = _build_currency_info(code)
            entry['rate'] = rate
            enriched_rates[code] = entry

    enriched_rates = dict(sorted(enriched_rates.items()))

    base_info = currencies_data.get(base, _build_currency_info(base))

    stats = {
        'total': len(enriched_rates),
        'fiat_count': sum(1 for v in enriched_rates.values() if v.get('type') == 'fiat'),
        'crypto_count': sum(1 for v in enriched_rates.values() if v.get('type') == 'crypto'),
    }
    fiat_only = {k: v['rate'] for k, v in enriched_rates.items() if v.get('type') == 'fiat' and v.get('rate')}
    if fiat_only:
        strongest = min(fiat_only, key=fiat_only.get)
        weakest = max(fiat_only, key=fiat_only.get)
        stats['strongest'] = {'code': strongest.upper(), 'rate': fiat_only[strongest],
                              'name': enriched_rates[strongest].get('name', strongest.upper())}
        stats['weakest'] = {'code': weakest.upper(), 'rate': fiat_only[weakest],
                            'name': enriched_rates[weakest].get('name', weakest.upper())}

    lang = getattr(g, 'lang_code', 'en')
    # Use per-base page ID (currency:<base>) so admins can set per-currency content
    page_id = normalize_page_id(currency=base)
    page_faqs = get_page_faqs(page_id, lang)
    return render_template("currency-rates.html",
                           base=base.upper(),
                           base_info=base_info,
                           currencies=enriched_rates,
                           all_currencies=currencies_data,
                           date=date,
                           stats=stats,
                           api_unavailable=api_unavailable,
                           page_id=page_id,
                           page_faqs=page_faqs), 503 if api_unavailable else 200


@currency_bp.route("/<lang_code>/currency/<base>-to-<target>", methods=['GET'])
@currency_bp.route("/<lang_code>/currency/<base>-to-<target>/", methods=['GET'])
@currency_bp.route('/currency/<base>-to-<target>', methods=['GET'])
@currency_bp.route('/currency/<base>-to-<target>/', methods=['GET'])
def currency_converter(base, target):
    try:
        base = base.lower()
        target = target.lower() if target else 'usd'

        if base == target:
            return redirect(f'/currency/{base}')

        amount = float(request.args.get('amount', 1))

        # Compute page_id and page_faqs once — shared by all render_template paths
        lang = getattr(g, 'lang_code', 'en')
        # Use per-pair page ID (currency:<base>:<target>) so admins can set per-pair content
        page_id = normalize_page_id(currency=base, to_currency=target)
        page_faqs = get_page_faqs(page_id, lang)

        currencies_data = get_currencies_data()

        if base not in currencies_data:
            return render_template('404.html'), 404
        if target not in currencies_data:
            if 'usd' in currencies_data:
                target = 'usd'
            else:
                return render_template('404.html'), 404

        try:
            rates, date = get_exchange_rates(base)
        except Exception:
            rates, date = {}, datetime.now().strftime('%Y-%m-%d')

        if not rates and is_crypto(base):
            rates = {}
            date = datetime.now().strftime('%Y-%m-%d')
            try:
                info = CRYPTO_CURRENCIES.get(base)
                if info:
                    cg_id = info['coingecko_id']
                    resp = requests.get(
                        f"{COINGECKO_API}/simple/price?ids={cg_id}&vs_currencies=usd",
                        timeout=8
                    )
                    if resp.status_code == 200:
                        price = resp.json().get(cg_id, {}).get('usd')
                        if price:
                            rates['usd'] = price
                            try:
                                fiat_rates_data, _ = get_exchange_rates('usd')
                                for fc in fiat_rates_data:
                                    rates[fc] = price * fiat_rates_data[fc]
                            except Exception:
                                pass
            except Exception:
                pass

        if not rates and not (is_crypto(base) or is_crypto(target)):
            return render_template('404.html'), 404

        api_unavailable = not rates and (is_crypto(base) or is_crypto(target))

        if api_unavailable:
            base_info = currencies_data.get(base, _build_currency_info(base))
            target_info = currencies_data.get(target, _build_currency_info(target))
            is_crypto_pair = is_crypto(base) or is_crypto(target)
            return render_template("currency-single.html",
                                   base=base.upper(),
                                   target=target.upper(),
                                   base_info=base_info,
                                   target_info=target_info,
                                   rate=0,
                                   amount=amount,
                                   result=0,
                                   date=datetime.now().strftime('%Y-%m-%d'),
                                   currencies=currencies_data,
                                   quick_conversions=[],
                                   is_crypto_pair=is_crypto_pair,
                                   api_unavailable=True,
                                   page_id=page_id,
                                   page_faqs=page_faqs), 503

        if target not in rates:
            try:
                inverse_rates, _ = get_exchange_rates(target)
            except Exception:
                inverse_rates = {}
            if base in inverse_rates and inverse_rates[base] != 0:
                rate = 1 / inverse_rates[base]
            else:
                base_info = currencies_data.get(base, _build_currency_info(base))
                target_info = currencies_data.get(target, _build_currency_info(target))
                is_crypto_pair = is_crypto(base) or is_crypto(target)
                if is_crypto_pair:
                    return render_template("currency-single.html",
                                           base=base.upper(),
                                           target=target.upper(),
                                           base_info=base_info,
                                           target_info=target_info,
                                           rate=0,
                                           amount=amount,
                                           result=0,
                                           date=datetime.now().strftime('%Y-%m-%d'),
                                           currencies=currencies_data,
                                           quick_conversions=[],
                                           is_crypto_pair=is_crypto_pair,
                                           api_unavailable=True,
                                           page_id=page_id,
                                           page_faqs=page_faqs), 503
                return render_template('404.html'), 404
        else:
            rate = rates[target]

        result_val = amount * rate

        base_info = currencies_data.get(base, _build_currency_info(base))
        target_info = currencies_data.get(target, _build_currency_info(target))

        quick_conversions = []
        popular = ['usd', 'eur', 'gbp', 'jpy', 'cny', 'btc', 'eth', 'chf', 'aud', 'cad']
        for c in popular:
            if c in rates and c != target and c != base:
                quick_conversions.append({
                    'code': c.upper(),
                    'name': get_currency_name(c),
                    'rate': rates[c],
                    'icon': get_currency_icon(c),
                    'type': 'crypto' if is_crypto(c) else 'fiat',
                })
        quick_conversions = quick_conversions[:6]

        is_crypto_pair = is_crypto(base) or is_crypto(target)

        return render_template("currency-single.html",
                               base=base.upper(),
                               target=target.upper(),
                               base_info=base_info,
                               target_info=target_info,
                               rate=rate,
                               amount=amount,
                               result=result_val,
                               date=date,
                               currencies=currencies_data,
                               quick_conversions=quick_conversions,
                               is_crypto_pair=is_crypto_pair,
                               api_unavailable=False,
                               page_id=page_id,
                               page_faqs=page_faqs)

    except ValueError:
        return render_template('404.html'), 404
    except Exception as e:
        current_app.logger.error(f"Error in currency converter: {str(e)}")
        return render_template('404.html'), 404


@currency_bp.route("/api/historical-rates/<base>/<target>")
def historical_rates(base, target):
    try:
        base_upper = base.upper()
        target_upper = target.upper()
        base_lower = base.lower()
        target_lower = target.lower()
        days = request.args.get('days', 30, type=int)
        days = min(days, 365)

        if is_crypto(base_lower) or is_crypto(target_lower):
            return _crypto_historical(base_lower, target_lower, days)

        end_date = datetime.now()
        start_date = end_date - timedelta(days=days)
        start_str = start_date.strftime('%Y-%m-%d')
        end_str = end_date.strftime('%Y-%m-%d')

        url = f"{FRANKFURTER_API}/{start_str}..{end_str}?from={base_upper}&to={target_upper}"
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        data = response.json()

        dates = []
        rates_list = []
        if 'rates' in data:
            sorted_dates = sorted(data['rates'].keys())
            for d in sorted_dates:
                rate_data = data['rates'][d]
                if target_upper in rate_data:
                    dates.append(d)
                    rates_list.append(rate_data[target_upper])

        if not rates_list:
            return jsonify({'error': 'No historical data available'}), 404

        first_rate = rates_list[0]
        last_rate = rates_list[-1]
        trend = "up" if last_rate > first_rate else "down" if last_rate < first_rate else "neutral"
        change = last_rate - first_rate
        change_percent = (change / first_rate) * 100 if first_rate != 0 else 0

        high_24h = None
        low_24h = None
        if len(rates_list) >= 2:
            recent = rates_list[-min(5, len(rates_list)):]
            high_24h = round(max(recent), 6)
            low_24h = round(min(recent), 6)

        return jsonify({
            'dates': dates,
            'rates': rates_list,
            'base': base_upper,
            'target': target_upper,
            'trend': trend,
            'change': round(change, 6),
            'change_percent': round(change_percent, 2),
            'current_rate': last_rate,
            'min_rate': round(min(rates_list), 6),
            'max_rate': round(max(rates_list), 6),
            'avg_rate': round(sum(rates_list) / len(rates_list), 6),
            'start_date': dates[0],
            'end_date': dates[-1],
            'volume_data': None,
            'market_cap': None,
            'volume_24h': None,
            'high_24h': high_24h,
            'low_24h': low_24h,
        })

    except Exception as e:
        current_app.logger.error(f"Error in historical_rates: {str(e)}")
        return jsonify({'error': 'Failed to fetch historical data'}), 500


def _crypto_historical(base_lower, target_lower, days):
    try:
        both_crypto = is_crypto(base_lower) and is_crypto(target_lower)

        if both_crypto:
            base_cg = CRYPTO_CURRENCIES[base_lower]['coingecko_id']
            target_cg = CRYPTO_CURRENCIES[target_lower]['coingecko_id']
            resp_base = requests.get(
                f"{COINGECKO_API}/coins/{base_cg}/market_chart?vs_currency=usd&days={days}",
                timeout=12
            )
            resp_target = requests.get(
                f"{COINGECKO_API}/coins/{target_cg}/market_chart?vs_currency=usd&days={days}",
                timeout=12
            )
            resp_base.raise_for_status()
            resp_target.raise_for_status()
            base_prices = resp_base.json().get('prices', [])
            target_prices = resp_target.json().get('prices', [])
            base_volumes = resp_base.json().get('total_volumes', [])

            if not base_prices or not target_prices:
                return jsonify({'error': 'No historical data available'}), 404

            target_price_map = {}
            for tp in target_prices:
                ts_key = int(tp[0] / 3600000) * 3600000
                target_price_map[ts_key] = tp[1]

            raw_prices = []
            raw_volumes = []
            for bp in base_prices:
                ts_key = int(bp[0] / 3600000) * 3600000
                tp_val = target_price_map.get(ts_key)
                if tp_val and tp_val > 0:
                    raw_prices.append((bp[0], bp[1] / tp_val))
            for bv in base_volumes:
                raw_volumes.append(bv)

            prices = raw_prices
            volumes = raw_volumes
            invert = False
        elif is_crypto(base_lower) and not is_crypto(target_lower):
            cg_id = CRYPTO_CURRENCIES[base_lower]['coingecko_id']
            vs = target_lower
            resp = requests.get(
                f"{COINGECKO_API}/coins/{cg_id}/market_chart?vs_currency={vs}&days={days}",
                timeout=12
            )
            resp.raise_for_status()
            data = resp.json()
            prices = [(p[0], p[1]) for p in data.get('prices', [])]
            volumes = data.get('total_volumes', [])
            invert = False
        elif is_crypto(target_lower) and not is_crypto(base_lower):
            cg_id = CRYPTO_CURRENCIES[target_lower]['coingecko_id']
            vs = base_lower
            resp = requests.get(
                f"{COINGECKO_API}/coins/{cg_id}/market_chart?vs_currency={vs}&days={days}",
                timeout=12
            )
            resp.raise_for_status()
            data = resp.json()
            prices = [(p[0], p[1]) for p in data.get('prices', [])]
            volumes = data.get('total_volumes', [])
            invert = True
        else:
            return jsonify({'error': 'Invalid pair'}), 400

        if not prices:
            return jsonify({'error': 'No historical data available'}), 404

        step = max(1, len(prices) // 90)
        sampled_prices = prices[::step]
        sampled_volumes = volumes[::step] if volumes else []

        dates = []
        rates_list = []
        volume_data = []

        for ts, price in sampled_prices:
            dt = datetime.fromtimestamp(ts / 1000)
            dates.append(dt.strftime('%Y-%m-%d'))
            if invert:
                rates_list.append(1.0 / price if price else 0)
            else:
                rates_list.append(price)

        for v in sampled_volumes:
            volume_data.append(v[1] if isinstance(v, (list, tuple)) else v)

        first_rate = rates_list[0]
        last_rate = rates_list[-1]
        trend = "up" if last_rate > first_rate else "down" if last_rate < first_rate else "neutral"
        change = last_rate - first_rate
        change_percent = (change / first_rate) * 100 if first_rate != 0 else 0

        crypto_code = base_lower if is_crypto(base_lower) else target_lower
        stats_data = get_crypto_stats(crypto_code)

        high_24h = None
        low_24h = None
        if len(rates_list) >= 2:
            recent = rates_list[-min(24, len(rates_list)):]
            high_24h = round(max(recent), 6)
            low_24h = round(min(recent), 6)

        return jsonify({
            'dates': dates,
            'rates': rates_list,
            'base': base_lower.upper(),
            'target': target_lower.upper(),
            'trend': trend,
            'change': round(change, 6),
            'change_percent': round(change_percent, 2),
            'current_rate': last_rate,
            'min_rate': round(min(rates_list), 6),
            'max_rate': round(max(rates_list), 6),
            'avg_rate': round(sum(rates_list) / len(rates_list), 6),
            'start_date': dates[0],
            'end_date': dates[-1],
            'volume_data': volume_data if volume_data else None,
            'market_cap': stats_data.get('market_cap') if stats_data else None,
            'volume_24h': stats_data.get('volume_24h') if stats_data else None,
            'high_24h': high_24h,
            'low_24h': low_24h,
        })

    except Exception as e:
        current_app.logger.error(f"Error in crypto historical: {str(e)}")
        return jsonify({'error': 'Failed to fetch historical data'}), 500


def get_24h_change(base, target):
    base_upper = base.upper()
    target_upper = target.upper()

    def _compute_change(rate_y, rate_t):
        if rate_y and rate_t and rate_y != 0:
            change_pct = ((rate_t - rate_y) / rate_y) * 100
            return {
                'change': round(rate_t - rate_y, 6),
                'change_percent': round(change_pct, 4),
                'rate_yesterday': rate_y,
                'rate_today': rate_t,
            }
        return None

    def fetch():
        try:
            yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
            today = datetime.now().strftime('%Y-%m-%d')
            resp_y = requests.get(
                f"{FRANKFURTER_API}/{yesterday}?from={base_upper}&to={target_upper}", timeout=8
            )
            resp_t = requests.get(
                f"{FRANKFURTER_API}/{today}?from={base_upper}&to={target_upper}", timeout=8
            )
            if resp_y.status_code == 200 and resp_t.status_code == 200:
                rate_y = resp_y.json().get('rates', {}).get(target_upper)
                rate_t = resp_t.json().get('rates', {}).get(target_upper)
                result = _compute_change(rate_y, rate_t)
                if result:
                    return result
        except Exception:
            pass

        try:
            resp = requests.get(f"{EXCHANGERATE_API}/{base_upper}", timeout=8)
            if resp.status_code == 200:
                data = resp.json()
                rate_t = data.get('rates', {}).get(target_upper)
                if rate_t:
                    try:
                        yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
                        resp_y = requests.get(
                            f"{FRANKFURTER_API}/{yesterday}?from={base_upper}&to={target_upper}", timeout=8
                        )
                        if resp_y.status_code == 200:
                            rate_y = resp_y.json().get('rates', {}).get(target_upper)
                            result = _compute_change(rate_y, rate_t)
                            if result:
                                return result
                    except Exception:
                        pass
                    return {
                        'change_percent': 0,
                        'rate_today': rate_t,
                    }
        except Exception:
            pass
        return None

    return _cached_get(f'24h_change_{base_upper}_{target_upper}', 21600, fetch)


@currency_bp.route("/api/crypto-stats/<code>")
def crypto_stats_api(code):
    code_lower = code.lower()
    stats = get_crypto_stats(code_lower)
    if not stats:
        return jsonify({'error': f'Unknown crypto: {code}'}), 404
    return jsonify(stats)


@currency_bp.route("/api/rate-change/<base>/<target>")
def rate_change_pair(base, target):
    if is_crypto(base.lower()) or is_crypto(target.lower()):
        return jsonify({'error': 'Use /api/crypto-stats for crypto pairs'}), 400
    result = get_24h_change(base, target)
    if not result:
        return jsonify({'error': 'Could not compute 24h change'}), 404
    return jsonify(result)


@currency_bp.route("/api/rate-change/<base>/all")
def rate_change_all(base):
    base_upper = base.upper()
    if is_crypto(base.lower()):
        return jsonify({})
    changes = get_rate_changes(base_upper)
    return jsonify(changes)


def register_currency_routes(app):
    app.register_blueprint(currency_bp)
