<?php

namespace App\Console;

use App\Models\User;
use App\Models\Admin;
use App\Models\Bid;
use App\Models\Sell;
use App\Models\Investment;
use App\Models\Ownership;
use App\Models\Product;
use App\Models\Market;
use Illuminate\Support\Str;
use Carbon\Carbon;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class Kernel extends ConsoleKernel
{
    private $agentCache = null;

    private function getAgent()
    {
        if (is_null($this->agentCache)) {
            $this->agentCache = Admin::where('role_id', 4)->first();
        }
        return $this->agentCache;
    }

    protected function schedule(Schedule $schedule)
    {
        // QUEUE
        $schedule->command('queue:work --stop-when-empty --tries=3 --timeout=55')
             ->everyMinute()
             ->withoutOverlapping();
             
        $schedule->call(function () {
            $this->runManualBiddingProcess();
        })
            ->everyMinute()
            ->name('run-manual-bids')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->runManualSellingProcess();
        })
            ->everyMinute()
            ->name('run-manual-sells')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->runAiBiddingProcess();
            $this->runAiSellingProcess();
        })
            ->everyMinute()
            ->name('run-ai-bids-and-sells')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->processAiBids();
        })
            ->everyMinute()
            ->name('process-ai-bids')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->processAiSells();
        })
            ->everyMinute()
            ->name('process-ai-sells')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->finishCompletedAiTrades();
        })
            ->everyMinute()
            ->name('finish-completed-ai-trades')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->cleanupOldAiBids();
        })
            ->daily()
            ->at('02:00') // Run at 2 AM when traffic is low
            ->name('cleanup-old-ai-bids')
            ->withoutOverlapping();

        $schedule->call(function () {
            $this->cleanupOldAiSells();
        })
            ->daily()
            ->at('02:30') // Run at 2:30 AM
            ->name('cleanup-old-ai-sells')
            ->withoutOverlapping();
    }

    private function runManualBiddingProcess(): void
    {
        $manualBids = Bid::with(['market', 'product'])
            ->where('type', 'manual_bid')
            ->where('status', 0)
            ->get();

        foreach ($manualBids as $manualBid) {
            $user = User::find($manualBid->user_id);
            if (!$user) {
                $manualBid->delete();
                continue;
            } elseif ($user->status == 0) {
                continue;
            }

            $bidTime = Carbon::parse($manualBid->bid_time);
            if (Carbon::now()->gte($bidTime->copy()->addHours(24))) {
                $this->processManualBid($manualBid, $user);
            }
        }
    }

    private function processManualBid($bid, $user)
    {
        $margin = $bid->product->value - ($bid->market->bid_margin / 100) * $bid->product->value;

        if ($bid->amount >= $margin) {
            $bid->update(['status' => 1]);

            Ownership::create([
                'slug' => Str::random(9),
                'user_id' => $user->id,
                'product_id' => $bid->product->id,
                'value' => $bid->amount,
                'quantity' => $bid->quantity,
                'type' => 'manual',
            ]);

            $this->updateProductAndOwnerBalance($bid);
        } else {
            $total = $bid->amount * $bid->quantity;
            $user->update(['balance' => $user->balance + $total]);
            $bid->update(['status' => 2]);
        }
    }

    private function updateProductAndOwnerBalance($bid)
    {
        $quantityToUpdate = 0;
        $quantityObtained = 0;

        if (!is_null($bid->product->quantity) && $bid->product->quantity >= $bid->quantity) {
            $quantityToUpdate = $bid->product->quantity - $bid->quantity;
            $quantityObtained = $bid->quantity;
        } else {
            $quantityToUpdate = 0;
            $quantityObtained = $bid->product->quantity;
        }

        $bidProduct = tap($bid->product)->update([
            'quantity' => $quantityToUpdate,
        ]);

        $profit = (5 / 100) * $bidProduct->value;
        $totalAmount = ($bidProduct->value + $profit) * $quantityObtained;
        $owner = $bid->product->user;

        if ($owner) {
            $owner->update(['balance' => $owner->balance + $totalAmount]);
        }
    }

    private function runManualSellingProcess()
    {
        $sells = Sell::with(['market', 'product', 'ownership'])->where([
            'type' => 'manual_sell',
            'status' => 0
        ])->get();

        foreach ($sells as $sell) {
            $user = User::find($sell->user_id);
            if (!$user) {
                $sell->delete();
                continue;
            } elseif ($user->status == 0) {
                continue;
            }

            $timestamp = Carbon::parse($sell->sell_time);
            if (Carbon::now()->gte($timestamp->copy()->addHours(1))) {
                $this->processManualSell($sell, $user);
            }
        }
    }

    private function processManualSell($sell, User $user)
    {
        $margin = $sell->initial_price + (($sell->market->sell_margin / 100) * $sell->initial_price);

        if ($sell->amount <= $margin) {
            $sell->update(['status' => 1]);
            $sell->ownership->delete();
            $total = $sell->amount * $sell->quantity;
            $profit = $sell->amount - $sell->initial_price;

            $user->update(['balance' => $user->balance + $total]);
            $this->fundAgentAndReferrers($profit, $user);
        } else {
            $sell->update(['status' => 2]);
        }
    }

    private function runAiBiddingProcess()
    {
        $aiBots = Investment::where('status', 2)->take(100)->get();
        $products = Product::inRandomOrder()->get();

        foreach ($aiBots as $bot) {
            $user = User::find($bot->user_id);

            // Check if user exist or user is banned
            if (!$user || $user->status == 0) continue;

            // Check if bot is paused
            if ($bot->is_paused == 1) continue;

            $endDate = Carbon::parse($bot->end_date);
            if (Carbon::now()->lessThan($endDate)) {
                if ($bot->funded == 1)
                    $this->placeAiBids($bot, $products);
            } else {
                $bot->update(['status' => 3]);
                $this->adjustBotBalanceAfterCompletion($bot, false);
            }
        }
    }

    private function placeAiBids($bot, &$products)
    {
        foreach ($products as $product) {
            // if bot balance is exhausted, stop iteration
            if ($bot->balance < 1) break;

            if (!$product || !$product->value) continue;

            // get product amount
            $amount = (rand(1, 5) / 100) * $product->value + $product->value;
            // get product quantity
            $prdQty = rand(1, rand(1, 3));

            $totalCost = $amount * $prdQty;
            if ($bot->balance >= $totalCost && $totalCost > 0) {
                Bid::create([
                    'user_id' => $bot->user_id,
                    'store_name' => $bot->user->username,
                    'product_id' => $product->id,
                    'market_id' => $product->market->id,
                    'investment_id' => $bot->id,
                    'amount' => $amount,
                    // 'quantity' => 1,
                    'quantity' => $prdQty,
                    'status' => 0,
                    'type' => 'ai_bid',
                    'bid_time' => now(),
                ]);

                $bot->decrement('balance', $totalCost);
                // $bot->update(['balance' => $bot->balance - ($amount * $prdQty)]);
            }
        }
    }

    private function adjustBotBalanceAfterCompletion($bot, $lastCheck = true)
    {
        // Skip if this bot has already been settled once
        if ($bot->is_settled == 1) return;

        $newBal = null;

        if (!$lastCheck) {
            $newBal = $bot->f_pfit > 0
                ? $bot->amount + $bot->f_pfit - $bot->moved
                : $bot->amount + $bot->pfit - $bot->moved;
        } else {
            $expDate = Carbon::parse($bot->end_date);
            if ($expDate->isToday() && $bot->moved < 1) {
                $newBal = $bot->f_pfit > 0
                    ? $bot->amount + $bot->f_pfit - $bot->moved
                    : $bot->amount + $bot->pfit - $bot->moved;
            }
        }

        if (!is_null($newBal)) {
            $bot->update([
                'balance' => $newBal,
                'is_settled' => 1, // ✅ mark as completed
            ]);

            // ✅ Delete all pending trades for this bot
            $this->cleanupPendingTrades($bot);
        }
    }


    private function processAiBids()
    {
        $bids = Bid::with(['market', 'product'])->where([
            'type' => 'ai_bid',
            'status' => 0
        ])->take(100)->get();

        foreach ($bids as $bid) {
            $user = User::find($bid->user_id);
            if (!$user) {
                $bid->delete();
                continue;
            }

            $bidTimePlus24Hours = Carbon::parse($bid->bid_time)->copy()->addHours(24);
            if (Carbon::now()->gte($bidTimePlus24Hours)) {
                $this->processAiBid($bid, $user);
            }
        }
    }

    private function processAiBid($bid, User $user)
    {
        $bot = Investment::find($bid->investment_id);
        if (is_null($bot)) return;

        // ✅ Skip if bot is already settled (shouldn't exist but just in case)
        if ($bot->is_settled == 1) return;

        $bid->update(['status' => 1]);
        Ownership::create([
            'slug' => Str::random(9),
            'user_id' => $user->id,
            'product_id' => $bid->product->id,
            'investment_id' => $bid->investment_id,
            'value' => $bid->amount,
            'quantity' => $bid->quantity,
            'type' => 'ai',
        ]);
    }

    private function runAiSellingProcess()
    {
        $aiBots = Investment::where('status', 2)->get(); // Get AI bots with status 'active'
        if (count($aiBots) < 1) return;

        foreach ($aiBots as $bot) {
            $user = User::find($bot->user_id);
            if (is_null($user)) continue;  // Skip if user doesn't exist

            // Check if the user is banned
            if ($user->status == 0) continue;

            // Check if bot is paused
            if ($bot->is_paused == 1) continue;

            // Retrieve the ownerships related to the AI bot
            $ownerships = Ownership::with('product')->where([
                'user_id' => $user->id,
                'investment_id' => $bot->id,
            ])->get();

            if (count($ownerships) < 1) continue; // Skip if no ownerships are found

            $this->establishOwnership($user, $bot, $ownerships); // Establish the ownerships if not already done

            // Get the end date of the bot
            $endDate = Carbon::parse($bot->end_date);
            $today = Carbon::now();

            // If the bot's end date has passed and no ownerships are available, complete the bot trade
            if ($today->gte($endDate) && count($ownerships) < 1) {
                $bot->update([
                    'status' => 3, // Mark the bot as completed
                ]);

                $this->adjustBotBalanceAfterCompletion($bot, false);
            }
        }
    }

    private function processAiSell($sell, $user)
    {
        $sellTime = Carbon::parse($sell->sell_time);
        if (Carbon::now()->gte($sellTime->copy()->addHours(1))) {
            DB::transaction(function () use ($sell, $user) {
                // Lock the bot record to prevent concurrent updates
                $bot = Investment::where('id', $sell->investment_id)->lockForUpdate()->first();
                if (is_null($bot) || $bot->is_settled == 1) return;

                $sell->update(['status' => 1]);

                $total = $sell->amount * $sell->quantity;
                $profit = ($sell->amount - $sell->initial_price) * $sell->quantity;

                $bot->update([
                    'pause_time' => now(),
                    'balance' => $bot->balance + $total,
                    'pfit' => $bot->pfit + $profit
                ]);

                if (!is_null($sell->ownership))
                    $sell->ownership->delete();

                // Fund agent and referrers
                $this->fundAgentAndReferrers($profit, $user);
            });
        }
    }

    private function processAiSells()
    {
        $aiSells = Sell::with(['market', 'product', 'user', 'investment'])->where([
            'type' => 'ai_sell',
            'status' => 0
        ])->get();

        foreach ($aiSells as $sell) {
            // Use the already loaded user
            if (is_null($sell->user)) {
                $sell->delete();
                continue;
            }

            $this->processAiSell($sell, $sell->user);
        }
    }

    private function fundAgentAndReferrers($profit, User $user)
    {
        // Fund agent
        $agentProfit = ($profit / 2) * 0.15;
        $agent = $this->getAgent();

        if ($agent) {
            // Use increment instead of manual update
            Admin::where('id', $agent->id)->increment('balance', $agentProfit);
        }

        // Fund referrers with bulk update
        $this->fundReferrersBulk($user, $profit);
    }

    private function fundReferrersBulk($user, $amount)
    {
        $updates = [];

        $referrers = [
            $user->referrer,
            $user->referrer?->referrer,
            $user->referrer?->referrer?->referrer,
        ];

        $rebatePercentages = [
            $user->referrer?->first_rebate,
            $user->referrer?->referrer?->second_rebate,
            $user->referrer?->referrer?->referrer?->third_rebate,
        ];

        foreach ($referrers as $index => $referrer) {
            if ($referrer instanceof User && isset($rebatePercentages[$index])) {
                $fundAmount = $rebatePercentages[$index] / 100 * $amount;
                $updates[$referrer->id] = $fundAmount;
            }
        }

        // Bulk update in single query
        foreach ($updates as $userId => $fundAmount) {
            User::where('id', $userId)->increment('rebates', $fundAmount);
        }
    }


    private function finishCompletedAiTrades()
    {
        $completedTrades = Investment::where('status', 3)->where('is_settled', 0)->get();
        foreach ($completedTrades as $trade) {
            $user = User::find($trade->user_id);
            if ($user) {
                // For ownerships
                $ownerships = Ownership::with('product')->where([
                    'user_id' => $user->id,
                    'investment_id' => $trade->id,
                ])->get();

                if (count($ownerships) > 0) {
                    $this->establishOwnership($user, $trade, $ownerships);
                }

                // For bids
                $bids = Bid::with(['product', 'market'])->where([
                    'investment_id' => $trade->id,
                    'user_id' => $user->id,
                    'status' => 0,
                ])->get();

                if (count($bids) > 0) {
                    foreach ($bids as $bid) {
                        $this->processAiBid($bid, $user);
                    }
                }

                // For sells
                $sells = Sell::with(['market', 'product', 'ownership'])->where([
                    'user_id' => $user->id,
                    'investment_id' => $trade->id,
                    'status' => 0,
                ])->get();

                if (count($sells) > 0) {
                    foreach ($sells as $sell) {
                        $this->processAiSell($sell, $user);
                    }
                }

                $this->adjustBotBalanceAfterCompletion($trade);
            }
        }
    }


    private function establishOwnership($user, $bot, $ownerships)
    {
        foreach ($ownerships as $ownership) {
            if ($ownership->sells()->where('status', 0)->doesntExist()) {
                $market = Market::where('type', 'sell')->inRandomOrder()->first();
                if (is_null($market)) continue;

                $limit = ($this->random_float($bot->trade_min, $bot->trade_max) / 100) * $ownership->value;
                $amount = $limit + $ownership->value;
                Sell::create([
                    'user_id' => $user->id,
                    'ownership_id' => $ownership->id,
                    'product_id' => $ownership->product->id,
                    'market_id' => $market->id,
                    'investment_id' => $bot->id,
                    'initial_price' => $ownership->value,
                    'amount' => $amount,
                    'quantity' => $ownership->quantity,
                    'status' => 0,
                    'type' => 'ai_sell',
                    'sell_time' => now(),
                ]);
            }
        }
    }

    private function random_float($min, $max)
    {
        return $min + mt_rand() / mt_getrandmax() * ($max - $min);
    }

    private function cleanupPendingTrades($bot)
    {
        // Delete pending bids
        Bid::where([
            'investment_id' => $bot->id,
            'status' => 0,
        ])->delete();

        // Delete pending sells
        Sell::where([
            'investment_id' => $bot->id,
            'status' => 0,
        ])->delete();

        // Delete any remaining ownerships
        Ownership::where([
            'investment_id' => $bot->id,
        ])->delete();
    }

    /**
     * Delete old AI bids for completed bots (older than 2 months), keeping only last 100
     */
    private function cleanupOldAiBids()
    {
        try {
            DB::transaction(function () {
                // Get bots completed more than 2 months ago
                $twoMonthsAgo = Carbon::now()->subMonths(2);

                $completedBotIds = Investment::where('status', 3)
                    ->where('is_settled', 1)
                    ->where('updated_at', '<=', $twoMonthsAgo)
                    ->pluck('id');

                if ($completedBotIds->isEmpty()) {
                    Log::info('No old completed bots found for bid cleanup');
                    return;
                }

                $deletedCount = 0;

                foreach ($completedBotIds as $botId) {
                    // Get the IDs of the last 100 bids to keep
                    $bidsToKeep = Bid::where('investment_id', $botId)
                        ->where('type', 'ai_bid')
                        ->orderBy('id', 'desc')
                        ->take(100)
                        ->pluck('id');

                    // Delete all bids except the last 100
                    $deleted = Bid::where('investment_id', $botId)
                        ->where('type', 'ai_bid')
                        ->when($bidsToKeep->isNotEmpty(), function ($query) use ($bidsToKeep) {
                            $query->whereNotIn('id', $bidsToKeep);
                        })
                        ->delete();

                    $deletedCount += $deleted;
                }

                Log::info("Cleanup: Deleted {$deletedCount} old AI bids for {$completedBotIds->count()} bots");
            });
        } catch (\Exception $e) {
            Log::error('Error in cleanupOldAiBids: ' . $e->getMessage());
        }
    }

    /**
     * Delete old AI sells for completed bots (older than 2 months), keeping only last 100
     */
    private function cleanupOldAiSells()
    {
        try {
            DB::transaction(function () {
                // Get bots completed more than 2 months ago
                $twoMonthsAgo = Carbon::now()->subMonths(2);

                $completedBotIds = Investment::where('status', 3)
                    ->where('is_settled', 1)
                    ->where('updated_at', '<=', $twoMonthsAgo)
                    ->pluck('id');

                if ($completedBotIds->isEmpty()) {
                    Log::info('No old completed bots found for sell cleanup');
                    return;
                }

                $deletedCount = 0;

                foreach ($completedBotIds as $botId) {
                    // Get the IDs of the last 100 sells to keep
                    $sellsToKeep = Sell::where('investment_id', $botId)
                        ->where('type', 'ai_sell')
                        ->orderBy('id', 'desc')
                        ->take(100)
                        ->pluck('id');

                    // Delete all sells except the last 100
                    $deleted = Sell::where('investment_id', $botId)
                        ->where('type', 'ai_sell')
                        ->when($sellsToKeep->isNotEmpty(), function ($query) use ($sellsToKeep) {
                            $query->whereNotIn('id', $sellsToKeep);
                        })
                        ->delete();

                    $deletedCount += $deleted;
                }

                Log::info("Cleanup: Deleted {$deletedCount} old AI sells for {$completedBotIds->count()} bots");
            });
        } catch (\Exception $e) {
            Log::error('Error in cleanupOldAiSells: ' . $e->getMessage());
        }
    }
}
