<?php

namespace App\Trading\Rules;

use App\Enums\MarketType;
use App\Enums\WalletType;
use App\Models\WalletBalance;
use App\Models\WalletRule;
use Illuminate\Support\Facades\Cache;

class WalletRuleEngine
{
    /**
     * Cache duration for rules (in seconds).
     */
    protected int $cacheDuration = 3600; // 1 hour
    
    /**
     * Cache version for invalidation on deployment.
     */
    protected string $cacheVersion = 'v1';

    /**
     * Get all rules for a wallet type.
     */
    public function getRulesForWallet(WalletType $walletType): array
    {
        $cacheKey = "wallet_rules:{$this->cacheVersion}:{$walletType->value}";

        return Cache::remember($cacheKey, $this->cacheDuration, function () use ($walletType) {
            return WalletRule::forWalletType($walletType)
                ->active()
                ->byPriority()
                ->get()
                ->keyBy('rule_key')
                ->toArray();
        });
    }

    /**
     * Get a specific rule value for a wallet type.
     */
    public function getRule(WalletType $walletType, string $ruleKey, $default = null)
    {
        $rules = $this->getRulesForWallet($walletType);

        if (!isset($rules[$ruleKey])) {
            return $default;
        }

        $rule = WalletRule::make($rules[$ruleKey]);
        return $rule->parsed_value ?? $default;
    }

    /**
     * Check if wallet can trade crypto markets.
     */
    public function canTradeCrypto(WalletBalance $wallet): bool
    {
        return $this->getRule($wallet->wallet_type, 'can_trade_crypto', false);
    }

    /**
     * Check if wallet can trade OTC markets.
     */
    public function canTradeOTC(WalletBalance $wallet): bool
    {
        return $this->getRule($wallet->wallet_type, 'can_trade_otc', true);
    }

    /**
     * Check if wallet can withdraw.
     */
    public function canWithdraw(WalletBalance $wallet): bool
    {
        return $this->getRule($wallet->wallet_type, 'can_withdraw', false);
    }

    /**
     * Check if wallet uses fair trading.
     */
    public function usesFairTrading(WalletBalance $wallet): bool
    {
        return $this->getRule($wallet->wallet_type, 'fair_trading', false);
    }

    /**
     * Check if wallet is beginner friendly.
     */
    public function isBeginnerFriendly(WalletBalance $wallet): bool
    {
        return $this->getRule($wallet->wallet_type, 'beginner_friendly', false);
    }

    /**
     * Get win probability boost for wallet.
     */
    public function getWinProbabilityBoost(WalletBalance $wallet): float
    {
        return (float) $this->getRule($wallet->wallet_type, 'win_probability_boost', 0.0);
    }

    /**
     * Get maximum trade amount for wallet.
     */
    public function getMaxTradeAmount(WalletBalance $wallet): string
    {
        return (string) $this->getRule($wallet->wallet_type, 'max_trade_amount', '10000');
    }

    /**
     * Get initial balance for wallet (demo only).
     */
    public function getInitialBalance(WalletType $walletType): string
    {
        return (string) $this->getRule($walletType, 'initial_balance', '10000');
    }

    /**
     * Check if wallet can trade on specific market.
     */
    public function canTradeMarket(WalletBalance $wallet, MarketType $marketType): bool
    {
        return match ($marketType) {
            MarketType::CRYPTO => $this->canTradeCrypto($wallet),
            MarketType::OTC => $this->canTradeOTC($wallet),
        };
    }

    /**
     * Validate trade amount against wallet rules.
     */
    public function validateTradeAmount(WalletBalance $wallet, string $amount): array
    {
        $maxAmount = $this->getMaxTradeAmount($wallet);

        if (bccomp($amount, $maxAmount, 8) > 0) {
            return [
                'valid' => false,
                'error' => "Trade amount exceeds maximum of {$maxAmount}",
            ];
        }

        if (bccomp($amount, '0', 8) <= 0) {
            return [
                'valid' => false,
                'error' => 'Trade amount must be greater than zero',
            ];
        }

        if (!$wallet->hasSufficientBalance($amount)) {
            return [
                'valid' => false,
                'error' => 'Insufficient balance',
            ];
        }

        return ['valid' => true];
    }

    /**
     * Get all permissions for a wallet.
     */
    public function getWalletPermissions(WalletBalance $wallet): array
    {
        return [
            'can_trade_crypto' => $this->canTradeCrypto($wallet),
            'can_trade_otc' => $this->canTradeOTC($wallet),
            'can_withdraw' => $this->canWithdraw($wallet),
            'uses_fair_trading' => $this->usesFairTrading($wallet),
            'is_beginner_friendly' => $this->isBeginnerFriendly($wallet),
            'win_probability_boost' => $this->getWinProbabilityBoost($wallet),
            'max_trade_amount' => $this->getMaxTradeAmount($wallet),
        ];
    }

    /**
     * Clear rules cache.
     */
    public function clearCache(?WalletType $walletType = null): void
    {
        if ($walletType) {
            Cache::forget("wallet_rules:{$this->cacheVersion}:{$walletType->value}");
        } else {
            foreach (WalletType::cases() as $type) {
                Cache::forget("wallet_rules:{$this->cacheVersion}:{$type->value}");
            }
        }
    }

    /**
     * Update a rule value.
     */
    public function updateRule(
        WalletType $walletType,
        string $ruleKey,
        $value,
        ?string $description = null
    ): bool {
        $rule = WalletRule::forWalletType($walletType)
            ->byKey($ruleKey)
            ->first();

        $ruleValue = is_array($value) ? json_encode($value) : (string) $value;

        if ($rule) {
            $rule->rule_value = $ruleValue;
            if ($description) {
                $rule->description = $description;
            }
            $result = $rule->save();
        } else {
            $result = WalletRule::create([
                'wallet_type' => $walletType,
                'rule_name' => ucwords(str_replace('_', ' ', $ruleKey)),
                'rule_key' => $ruleKey,
                'rule_value' => $ruleValue,
                'description' => $description,
                'is_active' => true,
                'priority' => 100,
            ]) !== null;
        }

        if ($result) {
            $this->clearCache($walletType);
        }

        return $result;
    }

    /**
     * Enable or disable a rule.
     */
    public function toggleRule(WalletType $walletType, string $ruleKey, bool $isActive): bool
    {
        $rule = WalletRule::forWalletType($walletType)
            ->byKey($ruleKey)
            ->first();

        if (!$rule) {
            return false;
        }

        $rule->is_active = $isActive;
        $result = $rule->save();

        if ($result) {
            $this->clearCache($walletType);
        }

        return $result;
    }
}

// Made with Bob