<?php
namespace App\Nexus\Engine;

use App\Models\Bid;
use App\Models\Market;
use App\Models\Sell;
use App\Models\User;
use App\Nexus\Models\NexusEconomySnapshot;
use App\Nexus\Models\NexusUserMetric;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;

/**
 * NEXUS ENGINE - EconomyGuardian
 *
 * Handles all dynamic pricing calculations.
 * Keeps the house profitable while maintaining market liquidity.
 *
 * Deployed config keys used:
 *   buyback_zone_percent  (e.g. 0.2 = 0.2%, after Phase 10 fix)
 *   max_sell_margin       (default 10 if not seeded)
 *   win_rate_ceiling      (default 50 if not seeded)
 *   monthly_profit_cap_percent (default 10 if not seeded)
 *
 * @package App\Nexus\Engine
 */
class EconomyGuardian
{
    /**
     * Calculate the dynamic market ask price for a product.
     *
     * base_margin = market.sell_margin (seed data from markets table)
     * Adjusted by supply factor, economy factor, and session noise.
     *
     * @param Market $market       The market this product belongs to
     * @param float  $initialPrice The ownership value (what user paid)
     * @param array  $batchContext  Session-wide batch data
     * @param array  $sessionProfile The rotated criteria profile
     * @return float The calculated market ask price in dollars
     */
    public function calculateMarketAsk(
        Market $market,
        float $initialPrice,
        array $batchContext,
        array $sessionProfile
    ): float {
        $baseMargin = (float) ($market->sell_margin ?? 1.0);

        // Supply factor: more sellers in this market = lower margin
        $marketId      = $market->id;
        $categoryCount = $batchContext['sell_count_by_market'][$marketId] ?? 1;
        $avgCount      = ! empty($batchContext['sell_count_by_market'])
            ? array_sum($batchContext['sell_count_by_market']) / count($batchContext['sell_count_by_market'])
            : 1;
        $supplyFactor = ($avgCount > 0)
            ? max(0.5, min(1.5, $avgCount / max(1, $categoryCount)))
            : 1.0;

        // Economy factor from latest snapshot
        $economyFactor = $this->getLatestLiquidityFactor();

        // Session noise (bounded, seeded)
        $noiseSeed = $sessionProfile['noise_seed'] ?? 0;
        mt_srand($noiseSeed);
        $rawNoise = (mt_rand(0, 10000) / 10000) * 2 - 1;
        mt_srand();
        $sessionNoise = ($sessionProfile['noise_factor'] ?? 0.1) * $rawNoise;

        // Effective margin
        $effectiveMargin = ($baseMargin * $supplyFactor * $economyFactor) + $sessionNoise;
        $maxMargin       = NexusConfig::getFloat('max_sell_margin', 10.0);
        $effectiveMargin = max(0.1, min($maxMargin, $effectiveMargin));

        $marketAsk = $initialPrice + ($effectiveMargin / 100 * $initialPrice);
        return round($marketAsk, 2);
    }

    /**
     * Calculate the buyback margin range.
     *
     * Deployed config has a single buyback_zone_percent value.
     * We derive min = 25% of the zone, max = the full zone.
     * Example: buyback_zone_percent = 0.2 -> min 0.05%, max 0.20%
     *
     * @param array $batchContext
     * @return array ['min' => float, 'max' => float] as percentages
     */
    public function calculateBuybackMargin(array $batchContext): array
    {
        $zonePercent = NexusConfig::getFloat('buyback_zone_percent', 0.2);

        // Derive min as 25% of the zone value, max as the full value
        $min = round($zonePercent * 0.25, 4);
        $max = $zonePercent;

        // If market is starved (few sells), widen slightly
        $sellCount = $batchContext['total_sell_count'] ?? 0;
        if ($sellCount < 5) {
            $max = min($max * 1.5, 1.0);
        }

        return ['min' => $min, 'max' => $max];
    }

    /**
     * Calculate session win rate from sell/bid cost ratio.
     *
     * ratio < 1.0  -> base 80% (conservative market)
     * ratio 1.0-2.0 -> 50% (moderate)
     * ratio > 2.0  -> 30% (greedy market)
     *
     * Session variance from profile applied on top.
     * Clamped to [20, win_rate_ceiling].
     *
     * @param array $batchContext
     * @param array $sessionProfile
     * @return float Win rate as percentage
     */
    public function calculateWinRate(array $batchContext, array $sessionProfile): float
    {
        $totalSellValue = $batchContext['total_sell_value'] ?? 0;
        $totalBidCost   = max(1, $batchContext['total_bid_cost'] ?? 1);

        $ratio = $totalSellValue / $totalBidCost;

        if ($ratio < 1.0) {
            $baseRate = 80.0;
        } elseif ($ratio <= 2.0) {
            $baseRate = 80.0 - (($ratio - 1.0) * 30.0);
        } else {
            $baseRate = 30.0;
        }

        $variance    = $sessionProfile['win_rate_variance'] ?? 0;
        $appliedRate = $baseRate + $variance;

        $ceiling     = NexusConfig::getFloat('win_rate_ceiling', 50.0);
        $appliedRate = max(20.0, min($ceiling, $appliedRate));

        return round($appliedRate, 2);
    }

    /**
     * Get current economy health indicators for dashboard and snapshots.
     */
    public function getEconomyHealth(): array
    {
        return Cache::remember('nexus.economy.health', 30, function () {
            $today = Carbon::today();

            $totalUsersActive = User::where('status', 1)->count();
            $totalBidsToday   = Bid::where('type', 'manual_bid')->whereDate('bid_time', $today)->count();
            $totalSellsToday  = Sell::where('type', 'manual_sell')->whereDate('sell_time', $today)->count();
            $totalVolumeToday = Sell::where('type', 'manual_sell')->where('status', 1)
                ->whereDate('updated_at', $today)->sum(\DB::raw('amount * quantity'));

            $avgProfitMargin = Sell::where('type', 'manual_sell')->where('status', 1)
                ->whereDate('updated_at', $today)->where('initial_price', '>', 0)
                ->selectRaw('AVG((amount - initial_price) / initial_price) as avg_margin')
                ->value('avg_margin') ?? 0;

            $evaluatedToday = Sell::where('type', 'manual_sell')->whereIn('status', [1, 2])
                ->whereDate('updated_at', $today)->count();
            $wonToday = Sell::where('type', 'manual_sell')->where('status', 1)
                ->whereDate('updated_at', $today)->count();
            $winRateAvg = $evaluatedToday > 0 ? ($wonToday / $evaluatedToday) * 100 : 0;

            $usersAtCap = NexusUserMetric::where('monthly_capital', '>', 0)
                ->whereRaw('monthly_profit >= (monthly_capital * ? / 100)',
                    [NexusConfig::getFloat('monthly_profit_cap_percent', 10)])
                ->count();

            $liquidityIndex = $this->computeLiquidityIndex();

            if ($liquidityIndex >= 0.25 && $winRateAvg >= 10 && $winRateAvg <= 60) {
                $health = 'healthy';
            } elseif ($liquidityIndex < 0.15 || $winRateAvg < 5) {
                $health = 'stagnant';
            } else {
                $health = 'overheated';
            }

            return [
                'total_users_active' => $totalUsersActive,
                'total_bids_today'   => $totalBidsToday,
                'total_sells_today'  => $totalSellsToday,
                'total_volume'       => round((float) $totalVolumeToday, 2),
                'avg_profit_margin'  => round((float) $avgProfitMargin, 4),
                'win_rate_avg'       => round($winRateAvg, 2),
                'users_at_cap'       => $usersAtCap,
                'liquidity_index'    => $liquidityIndex,
                'economy_health'     => $health,
                'snapshot_date'      => $today->toDateString(),
            ];
        });
    }

    /**
     * Liquidity index (0.0 to 1.0).
     */
    public function computeLiquidityIndex(): float
    {
        $now = Carbon::now();

        $bids24h       = Bid::where('type', 'manual_bid')->where('bid_time', '>=', $now->copy()->subDay())->count();
        $bids7d        = Bid::where('type', 'manual_bid')->where('bid_time', '>=', $now->copy()->subDays(7))->count();
        $avgBidsPerDay = max(1, $bids7d / 7);
        $bidActivity   = min(1.0, $bids24h / $avgBidsPerDay);

        $sellsTotal = Sell::where('type', 'manual_sell')->whereIn('status', [1, 2])
            ->where('updated_at', '>=', $now->copy()->subDays(7))->count();
        $sellsWon = Sell::where('type', 'manual_sell')->where('status', 1)
            ->where('updated_at', '>=', $now->copy()->subDays(7))->count();
        $sellCompletionRate = $sellsTotal > 0 ? $sellsWon / $sellsTotal : 0.5;

        $totalActive = max(1, User::where('status', 1)->count());
        $tradedToday = Bid::where('type', 'manual_bid')->whereDate('bid_time', Carbon::today())
            ->distinct('user_id')->count('user_id');
        $userActivity = min(1.0, $tradedToday / max(1, $totalActive * 0.1));

        $index = ($bidActivity * 0.4) + ($sellCompletionRate * 0.35) + ($userActivity * 0.25);
        return round(max(0.0, min(1.0, $index)), 2);
    }

    private function getLatestLiquidityFactor(): float
    {
        $snapshot = NexusEconomySnapshot::orderBy('captured_at', 'desc')->first();
        if (! $snapshot || ! $snapshot->platform_liquidity) {
            return 1.0;
        }

        // platform_liquidity stored as percentage (0-100), convert to 0-1 range
        $liquidityIndex = $snapshot->platform_liquidity / 100;
        $factor         = 0.7 + ($liquidityIndex * 0.6);
        return round(max(0.7, min(1.3, $factor)), 2);
    }
}