"""
FreeExile MegaShop Orchestrator Service.
Coordinates product catalog, currency exchange / PPP pricing, platform compliance routing,
atomic receipt / crypto on-chain settlement, and inventory / coin delivery via Two-Phase Commit.
"""

import time
import uuid
import random
from typing import Dict, List, Optional, Set, Any
from dataclasses import dataclass, field

from .shop_catalog import (
    MegaShopCatalog,
    ProductCategory,
    ShopProduct,
    ChestDropProbability,
)
from .currency_exchange import (
    CurrencyExchangeEngine,
    RegionalPricingTier,
)
from .payment_gateway_router import (
    PaymentGatewayRouter,
    PlatformType,
    PaymentGateway,
    PlatformPolicyViolationError,
)
from .receipt_verifier import (
    Web3CryptoVerifier,
    CryptoChain,
    CryptoToken,
    CryptoOrder,
    Web3TransactionVerificationError,
)
from trade.two_phase_commit import InstantBuyoutEngine


@dataclass(slots=True)
class PlayerProfile:
    account_id: str
    coin_balance: int = 0
    owned_skins: Set[str] = field(default_factory=set)
    inventory: List[Dict[str, Any]] = field(default_factory=list)
    product_purchase_counts: Dict[str, int] = field(default_factory=dict)
    active_passes: Dict[str, float] = field(default_factory=dict)


@dataclass(slots=True, frozen=True)
class PurchaseOrderResult:
    order_id: str
    success: bool
    error_message: str = ""
    fiat_amount: float = 0.0
    currency: str = "USD"
    expected_coins: int = 0
    web3_details: Optional[CryptoOrder] = None


@dataclass(slots=True, frozen=True)
class SettlementResult:
    order_id: str
    success: bool
    error_message: str = ""
    coins_credited: int = 0
    product_delivered: str = ""


@dataclass(slots=True, frozen=True)
class DroppedLoot:
    item_id: str
    item_name_key: str
    rarity: int
    drop_chance_percent: float


@dataclass(slots=True, frozen=True)
class ChestOpenResult:
    success: bool
    error_message: str = ""
    chest_product_id: str = ""
    dropped_item: Optional[DroppedLoot] = None


class MegaShopService:
    """Central Gateway Service for all FreeExile Monetization, In-Game Store, and Payment Processing."""

    def __init__(self) -> None:
        self.catalog = MegaShopCatalog()
        self.fx_engine = CurrencyExchangeEngine()
        self.gateway_router = PaymentGatewayRouter()
        self.crypto_verifier = Web3CryptoVerifier()
        self.buyout_engine = InstantBuyoutEngine()

        self.players: Dict[str, PlayerProfile] = {}
        self.pending_orders: Dict[str, Dict[str, Any]] = {}

    def register_player(self, account_id: str) -> PlayerProfile:
        if account_id not in self.players:
            self.players[account_id] = PlayerProfile(account_id=account_id)
            self.buyout_engine.register_account(account_id)
        return self.players[account_id]

    def get_player(self, account_id: str) -> PlayerProfile:
        if account_id not in self.players:
            return self.register_player(account_id)
        return self.players[account_id]

    def get_player_coin_balance(self, account_id: str) -> int:
        return self.get_player(account_id).coin_balance

    def get_player_owned_skins(self, account_id: str) -> Set[str]:
        return self.get_player(account_id).owned_skins

    def get_player_inventory(self, account_id: str) -> List[Dict[str, Any]]:
        return self.get_player(account_id).inventory

    def credit_player_coins(self, account_id: str, amount: int, reason: str = "direct_credit") -> int:
        player = self.get_player(account_id)
        player.coin_balance += amount
        # Sync with 2PC account stash
        stash = self.buyout_engine.accounts.get(account_id)
        if stash:
            stash.currencies["ThienMenh"] = player.coin_balance
        return player.coin_balance

    # =========================================================================
    # FIAT & CRYPTO PAYMENT ORDER CREATION
    # =========================================================================
    def create_purchase_order(
        self,
        account_id: str,
        product_id: str,
        platform: PlatformType,
        gateway: PaymentGateway,
        country_code: str = "US",
        crypto_chain: CryptoChain = CryptoChain.CHAIN_UNKNOWN,
        crypto_token: CryptoToken = CryptoToken.TOKEN_UNKNOWN,
        buyer_wallet: str = ""
    ) -> PurchaseOrderResult:
        product = self.catalog.get_product(product_id)
        if not product:
            return PurchaseOrderResult("", False, f"Product {product_id} not found")

        # Check account purchase limits
        player = self.get_player(account_id)
        if product.purchase_limit_per_account > 0:
            count = player.product_purchase_counts.get(product_id, 0)
            if count >= product.purchase_limit_per_account:
                return PurchaseOrderResult("", False, f"Purchase limit reached for {product_id}")

        # Check Platform Policy Compliance
        try:
            order_id = self.gateway_router.create_payment_order(
                account_id=account_id,
                product_id=product_id,
                platform=platform,
                requested_gateway=gateway,
                country_code=country_code
            )
        except PlatformPolicyViolationError as e:
            return PurchaseOrderResult("", False, str(e))

        is_crypto = (gateway == PaymentGateway.GATEWAY_CRYPTO_WEB3)
        base_usd = product.base_fiat_usd if product.base_fiat_usd > 0 else (product.coin_price / 100.0)

        if product.category == ProductCategory.COINS and product.coin_price > 0:
            base_coins = product.coin_price
            if is_crypto or platform in (PlatformType.PLATFORM_WEB_PC, PlatformType.PLATFORM_ANDROID_SIDELOAD):
                expected_coins = base_coins + int(round(base_coins * 0.10))
            else:
                expected_coins = base_coins
        else:
            expected_coins = self.fx_engine.calculate_coins_reward(
                base_usd=base_usd,
                platform=platform.value,
                is_web3_crypto=is_crypto
            )

        localized_price = self.fx_engine.calculate_localized_fiat_price(base_usd, country_code)
        currency_code = self.fx_engine.get_currency_for_country(country_code)

        web3_order: Optional[CryptoOrder] = None
        if is_crypto:
            if crypto_chain == CryptoChain.CHAIN_UNKNOWN or crypto_token == CryptoToken.TOKEN_UNKNOWN:
                return PurchaseOrderResult("", False, "Crypto chain and token must be specified for Web3 payment")
            web3_order = self.crypto_verifier.create_crypto_order(
                account_id=account_id,
                product_id=product_id,
                fiat_usd_amount=base_usd,
                chain=crypto_chain,
                token=crypto_token,
                buyer_wallet=buyer_wallet
            )
            order_id = web3_order.order_id

        # Register pending order
        self.pending_orders[order_id] = {
            "account_id": account_id,
            "product_id": product_id,
            "platform": platform,
            "gateway": gateway,
            "expected_coins": expected_coins,
            "is_crypto": is_crypto,
            "timestamp": time.time(),
        }

        return PurchaseOrderResult(
            order_id=order_id,
            success=True,
            fiat_amount=localized_price,
            currency=currency_code,
            expected_coins=expected_coins,
            web3_details=web3_order
        )

    # =========================================================================
    # SETTLEMENT (APPLE IAP / GOOGLE PLAY / STRIPE)
    # =========================================================================
    def settle_fiat_order(self, order_id: str, receipt_payload: str) -> SettlementResult:
        order_meta = self.pending_orders.get(order_id)
        if not order_meta:
            return SettlementResult(order_id, False, f"Unknown pending order {order_id}")

        gateway = order_meta["gateway"]
        is_valid = False

        if gateway == PaymentGateway.GATEWAY_APPLE_IAP:
            is_valid = self.crypto_verifier.verify_apple_storekit_receipt(receipt_payload, order_id)
        elif gateway == PaymentGateway.GATEWAY_GOOGLE_PLAY:
            is_valid = self.crypto_verifier.verify_google_play_receipt(receipt_payload, order_id)
        elif gateway in (PaymentGateway.GATEWAY_STRIPE, PaymentGateway.GATEWAY_PAYPAL, PaymentGateway.GATEWAY_LOCAL_VIETNAM):
            is_valid = bool(receipt_payload and not "invalid" in receipt_payload.lower())

        if not is_valid:
            return SettlementResult(order_id, False, "Receipt verification rejected by processor")

        # Deliver goods
        account_id = order_meta["account_id"]
        product_id = order_meta["product_id"]
        coins_to_credit = order_meta["expected_coins"]

        player = self.get_player(account_id)
        player.product_purchase_counts[product_id] = player.product_purchase_counts.get(product_id, 0) + 1
        self.credit_player_coins(account_id, coins_to_credit, reason=f"fiat_order_{order_id}")

        del self.pending_orders[order_id]
        return SettlementResult(
            order_id=order_id,
            success=True,
            coins_credited=coins_to_credit,
            product_delivered=product_id
        )

    # =========================================================================
    # SETTLEMENT (WEB3 CRYPTO ON-CHAIN)
    # =========================================================================
    def settle_crypto_order(
        self,
        order_id: str,
        tx_hash: str,
        chain: CryptoChain
    ) -> SettlementResult:
        order_meta = self.pending_orders.get(order_id)
        if not order_meta:
            return SettlementResult(order_id, False, f"Unknown pending order {order_id}")

        try:
            verified, _ = self.crypto_verifier.verify_and_settle_order(order_id, tx_hash, chain)
        except Web3TransactionVerificationError as e:
            return SettlementResult(order_id, False, str(e))

        if not verified:
            return SettlementResult(order_id, False, "On-chain transaction verification failed")

        account_id = order_meta["account_id"]
        product_id = order_meta["product_id"]
        coins_to_credit = order_meta["expected_coins"]

        player = self.get_player(account_id)
        player.product_purchase_counts[product_id] = player.product_purchase_counts.get(product_id, 0) + 1
        self.credit_player_coins(account_id, coins_to_credit, reason=f"crypto_tx_{tx_hash}")

        del self.pending_orders[order_id]
        return SettlementResult(
            order_id=order_id,
            success=True,
            coins_credited=coins_to_credit,
            product_delivered=product_id
        )

    # =========================================================================
    # IN-GAME COIN PURCHASES (SKINS, ITEMS, PASSES)
    # =========================================================================
    def purchase_product_with_coins(
        self,
        account_id: str,
        product_id: str
    ) -> SettlementResult:
        product = self.catalog.get_product(product_id)
        if not product:
            return SettlementResult("", False, f"Product {product_id} not found")

        player = self.get_player(account_id)
        price = product.calculate_discounted_price()

        if player.coin_balance < price:
            return SettlementResult(
                "",
                False,
                f"Insufficient Gold Cores: have {player.coin_balance}, need {price}"
            )

        # Check purchase limits
        if product.purchase_limit_per_account > 0:
            count = player.product_purchase_counts.get(product_id, 0)
            if count >= product.purchase_limit_per_account:
                return SettlementResult("", False, f"Account purchase limit reached for {product_id}")

        # Deduct coins
        player.coin_balance -= price
        player.product_purchase_counts[product_id] = player.product_purchase_counts.get(product_id, 0) + 1

        # Deliver by Category
        if product.category == ProductCategory.SKINS:
            player.owned_skins.add(product_id)
        elif product.category == ProductCategory.ITEMS:
            player.inventory.append({
                "item_id": product_id,
                "obtained_at": time.time(),
                "type": "utility"
            })
        elif product.category == ProductCategory.PASSES:
            player.active_passes[product_id] = time.time() + (30 * 86400)

        return SettlementResult(
            order_id=f"coin_buy_{uuid.uuid4().hex[:8]}",
            success=True,
            product_delivered=product_id
        )

    # =========================================================================
    # MYSTERY CHEST GACHA OPENING (100% PROBABILITY DISCLOSURE)
    # =========================================================================
    def purchase_and_open_chest(
        self,
        account_id: str,
        chest_product_id: str
    ) -> ChestOpenResult:
        product = self.catalog.get_product(chest_product_id)
        if not product or product.category != ProductCategory.CHESTS:
            return ChestOpenResult(False, f"Invalid mystery chest {chest_product_id}")

        player = self.get_player(account_id)
        price = product.calculate_discounted_price()

        if player.coin_balance < price:
            return ChestOpenResult(False, f"Insufficient coins to open chest: need {price}")

        # Deduct cost
        player.coin_balance -= price

        # Weighted Random Drop Selection based on transparent drop_chance_percent
        drop_rates = product.chest_drop_rates
        if not drop_rates:
            return ChestOpenResult(False, "Chest contains no drop table configuration")

        rand_val = random.uniform(0.0, 100.0)
        cumulative = 0.0
        selected_drop: Optional[ChestDropProbability] = None

        for drop in drop_rates:
            cumulative += drop.drop_chance_percent
            if rand_val <= cumulative:
                selected_drop = drop
                break

        if not selected_drop:
            selected_drop = drop_rates[0] # Fallback to base common

        # Deliver loot to inventory
        loot = DroppedLoot(
            item_id=selected_drop.item_id,
            item_name_key=selected_drop.item_name_key,
            rarity=selected_drop.rarity,
            drop_chance_percent=selected_drop.drop_chance_percent
        )

        player.inventory.append({
            "item_id": loot.item_id,
            "item_name_key": loot.item_name_key,
            "rarity": loot.rarity,
            "obtained_at": time.time(),
            "source": chest_product_id
        })

        return ChestOpenResult(
            success=True,
            chest_product_id=chest_product_id,
            dropped_item=loot
        )
