<?php

use App\Models\Investment;
use App\Models\SecurityOrder;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Mail\AdminTransactionMail;
use App\Mail\InvestmentClaimedMail;
use App\Services\DefaultCurrencyService;
use App\Services\Sms\SmsAfrikasTalking;
use App\Services\Trading\TradingSettingsService;
use App\Traits\Vrm\Livewire\WithNotifications;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Component;

return new #[Layout('layouts.trading')] class extends Component {
    use WithNotifications;

    public string $activeTab = 'portfolio';
    public array $sellQuantities = [];
    public ?int $sellHoldingId = null;
    public string $sellShares = '';
    public string $sellStep = 'list';
    public float $sellSubmittedAmount = 0.0;
    public int $sellSubmittedShares = 0;
    public string $sellSubmittedSymbol = '';
    public int $portfolioHoldingsLimit = 10;

    #[Computed]
    public function wallet(): Wallet
    {
        return Wallet::query()->firstOrCreate(
            ['user_id' => auth()->id()],
            ['currency_code' => app(DefaultCurrencyService::class)->code()]
        );
    }

    #[Computed]
    public function holdings(): array
    {
        $holdings = auth()->user()
            ->portfolioHoldings()
            ->with('security')
            ->get();

        $securityIds = $holdings->pluck('security_id')->all();

        $firstInvestedDates = SecurityOrder::query()
            ->where('user_id', auth()->id())
            ->where('order_type', 'buy')
            ->where('status', 'approved')
            ->when($securityIds !== [], fn ($query) => $query->whereIn('security_id', $securityIds))
            ->orderBy('traded_at')
            ->get()
            ->groupBy('security_id')
            ->map(fn ($orders) => optional($orders->first()->traded_at)?->format('M d, Y'));

        return $holdings
            ->map(function ($holding): array {
                $currentPrice = (float) $holding->security->latestPrice();
                $quantity = (int) $holding->quantity;
                $totalInvested = (float) $holding->total_invested;
                $currentValue = $currentPrice * $quantity;
                $performanceAmount = $currentValue - $totalInvested;
                $performancePercent = $totalInvested > 0 ? ($performanceAmount / $totalInvested) * 100 : 0;

                return [
                    'id' => $holding->id,
                    'security_id' => $holding->security_id,
                    'symbol' => $holding->security->trading_name,
                    'name' => $holding->security->name,
                    'price' => $currentPrice,
                    'quantity' => $quantity,
                    'reserved_quantity' => (int) ($holding->reserved_quantity ?? 0),
                    'available_quantity' => max(0, $quantity - (int) ($holding->reserved_quantity ?? 0)),
                    'invested' => $totalInvested,
                    'value' => $currentValue,
                    'performanceAmount' => $performanceAmount,
                    'performancePercent' => $performancePercent,
                ];
            })
            ->map(function (array $holding) use ($firstInvestedDates): array {
                $holding['firstInvestedAt'] = $firstInvestedDates->get($holding['security_id']) ?? 'N/A';

                return $holding;
            })
            ->all();
    }

    #[Computed]
    public function visibleHoldings(): array
    {
        return array_slice($this->holdings, 0, $this->portfolioHoldingsLimit);
    }

    #[Computed]
    public function hasMoreHoldings(): bool
    {
        return count($this->holdings) > $this->portfolioHoldingsLimit;
    }

    #[Computed]
    public function portfolio(): array
    {
        $wallet = $this->wallet;
        $inTrade = collect($this->holdings)->sum('value');
        $totalInvested = auth()->user()->portfolioHoldings()->sum('total_invested');
        $gainLoss = $totalInvested > 0 ? (($inTrade - (float) $totalInvested) / (float) $totalInvested) * 100 : 0;

        return [
            'balance' => (float) $wallet->balance,
            'inTrade' => (float) $inTrade,
            'gainLoss' => (float) $gainLoss,
            'currency' => app(DefaultCurrencyService::class)->code(),
        ];
    }

    #[Computed]
    public function subscriptionInvestments()
    {
        return Investment::query()
            ->where('user_id', auth()->id())
            ->with('package')
            ->where('is_active', true)
            ->latest()
            ->get();
    }

    #[Computed]
    public function isMarketOpen(): bool
    {
        return app(TradingSettingsService::class)->isMarketOpen();
    }

    #[Computed]
    public function sellLimits(): array
    {
        return app(TradingSettingsService::class)->sellLimits();
    }

    #[Computed]
    public function sellRemainingDailyShares(): int
    {
        return app(TradingSettingsService::class)->remainingDailySellSharesForUser((int) auth()->id());
    }

    public function setTab(string $tab): void
    {
        $this->activeTab = $tab;
    }

    public function startSellFlow(int $holdingId): void
    {
        $holding = collect($this->holdings)->firstWhere('id', $holdingId);
        if (! $holding) {
            $this->notifyError(__('Portfolio holding not found.'));

            return;
        }

        $available = (int) ($holding['available_quantity'] ?? 0);
        if ($available <= 0) {
            $this->notifyError(__('No available shares to sell.'));

            return;
        }

        $minimumShares = max(1, (int) ($this->sellLimits['min_per_request'] ?? 1));
        $initialShares = min(max(1, $minimumShares), $this->sellMaxAllowedForHolding($holdingId));

        $this->sellHoldingId = $holdingId;
        $this->sellShares = (string) $initialShares;
        $this->sellStep = 'details';
    }

    public function backToSellDetails(): void
    {
        $this->sellStep = 'details';
    }

    public function goToSellConfirmation(): void
    {
        $holdingId = (int) $this->sellHoldingId;
        $quantity = (int) $this->sellShares;
        if (! $this->canSell($holdingId, $quantity)) {
            return;
        }

        $this->sellStep = 'confirm';
    }

    public function resetSellFlow(): void
    {
        $this->sellHoldingId = null;
        $this->sellShares = '';
        $this->sellStep = 'list';
        $this->sellSubmittedAmount = 0.0;
        $this->sellSubmittedShares = 0;
        $this->sellSubmittedSymbol = '';
    }

    public function showMoreHoldings(): void
    {
        $this->portfolioHoldingsLimit += 10;
    }

    public function sellPreviewAmountForHolding(int $holdingId): float
    {
        $holding = collect($this->holdings)->firstWhere('id', $holdingId);
        if (! $holding) {
            return 0.0;
        }

        $quantity = (int) ($this->sellQuantities[$holdingId] ?? 0);

        if ($quantity <= 0) {
            return 0.0;
        }

        return (float) $holding['price'] * $quantity;
    }

    public function sellMaxAllowedForHolding(int $holdingId): int
    {
        $holding = collect($this->holdings)->firstWhere('id', $holdingId);
        if (! $holding) {
            return 0;
        }

        $available = (int) ($holding['available_quantity'] ?? 0);
        $maxPerRequest = (int) ($this->sellLimits['max_per_request'] ?? 0);
        $maxPerDay = (int) ($this->sellLimits['max_per_day'] ?? 0);
        $remainingDaily = $maxPerDay > 0 ? $this->sellRemainingDailyShares : PHP_INT_MAX;

        $cap = min($available, $maxPerRequest > 0 ? $maxPerRequest : PHP_INT_MAX, $remainingDaily);

        return max(0, $cap);
    }

    public function selectedSellHolding(): ?array
    {
        if ($this->sellHoldingId === null) {
            return null;
        }

        return collect($this->holdings)->firstWhere('id', $this->sellHoldingId);
    }

    public function sellPreviewAmount(): float
    {
        $holding = $this->selectedSellHolding();
        if (! $holding) {
            return 0.0;
        }

        $quantity = (int) $this->sellShares;
        if ($quantity <= 0) {
            return 0.0;
        }

        return (float) $holding['price'] * $quantity;
    }

    public function claimInvestment(int $investmentId): void
    {
        $investment = Investment::query()
            ->where('user_id', auth()->id())
            ->find($investmentId);

        if (! $investment || $investment->status !== 'active') {
            $this->notifyError(__('Investment not available for claim.'));

            return;
        }

        if ($investment->matures_at->isFuture()) {
            $this->notifyError(__('This investment has not matured yet.'));

            return;
        }

        DB::transaction(function () use ($investment): void {
            $wallet = $this->wallet;
            $returnAmount = (float) $investment->expected_return_amount;

            $wallet->balance = (float) $wallet->balance + $returnAmount;
            $wallet->earned_amount = (float) $wallet->earned_amount + ($returnAmount - (float) $investment->principal_amount);
            $wallet->save();

            $investment->status = 'claimed';
            $investment->is_active = false;
            $investment->claimed_at = now();
            $investment->save();

            WalletTransaction::query()->create([
                'wallet_id' => $wallet->id,
                'user_id' => auth()->id(),
                'type' => 'investment_credit',
                'status' => 'completed',
                'amount' => $returnAmount,
                'currency_code' => app(DefaultCurrencyService::class)->code(),
                'description' => __('Investment return claimed'),
                'meta' => [
                    'investment_id' => $investment->id,
                    'principal_amount' => (float) $investment->principal_amount,
                ],
            ]);
        });

        $user = auth()->user();

        Mail::to($user?->email)->queue(new InvestmentClaimedMail(
            $user?->name ?? 'Member',
            app(DefaultCurrencyService::class)->code(),
            (float) $investment->expected_return_amount,
        ));
        $adminEmail = config('mail.admin_address');
        if (filled($adminEmail)) {
            Mail::to((string) $adminEmail)
                ->queue(new AdminTransactionMail(
                    auth()->user()->name,
                    auth()->user()->email,
                    (float) $investment->expected_return_amount,
                    app(DefaultCurrencyService::class)->code(),
                    'investment_claim',
                    'completed',
                    'wallet'
                ));
        }
        app(SmsAfrikasTalking::class)->sendShortMessage(
            $user?->phone,
            'Investment return claimed successfully and credited to wallet.',
        );
        $this->notifySuccess(__('Investment return claimed successfully.'));
    }

    public function requestSell(int $holdingId): bool
    {
        if (! $this->isMarketOpen) {
            $this->notifyError(__('Market is currently closed. Trading is unavailable.'));

            return false;
        }

        $holding = auth()->user()
            ->portfolioHoldings()
            ->with('security')
            ->whereKey($holdingId)
            ->first();

        if (! $holding) {
            $this->notifyError(__('Portfolio holding not found.'));

            return false;
        }

        $quantity = (int) ($this->sellQuantities[$holdingId] ?? 0);
        if (! $this->canSell($holdingId, $quantity)) {
            return false;
        }

        $pricePerShare = max(0.0001, (float) $holding->security->latestPrice());
        $totalAmount = $pricePerShare * $quantity;
        $wallet = $this->wallet;

        DB::transaction(function () use ($holding, $wallet, $pricePerShare, $quantity, $totalAmount): void {
            $lockedHolding = auth()->user()
                ->portfolioHoldings()
                ->whereKey($holding->id)
                ->lockForUpdate()
                ->firstOrFail();

            $available = max(0, (int) $lockedHolding->quantity - (int) ($lockedHolding->reserved_quantity ?? 0));
            if ($quantity > $available) {
                throw new RuntimeException(__('Sell quantity exceeds your available shares.'));
            }

            $lockedHolding->reserved_quantity = (int) ($lockedHolding->reserved_quantity ?? 0) + $quantity;
            $lockedHolding->save();

            SecurityOrder::query()->create([
                'user_id' => auth()->id(),
                'security_id' => $lockedHolding->security_id,
                'wallet_id' => $wallet->id,
                'order_type' => 'sell',
                'status' => 'pending',
                'price_per_share' => $pricePerShare,
                'quantity' => $quantity,
                'volume' => $quantity,
                'total_amount' => $totalAmount,
                'currency_code' => app(DefaultCurrencyService::class)->code(),
                'traded_at' => now(),
                'profit_loss_amount' => 0,
                'profit_loss_percent' => 0,
                'is_active' => false,
            ]);

            WalletTransaction::query()->create([
                'wallet_id' => $wallet->id,
                'user_id' => auth()->id(),
                'type' => 'order_credit',
                'status' => 'pending',
                'amount' => $totalAmount,
                'currency_code' => app(DefaultCurrencyService::class)->code(),
                'description' => "Pending sell order for {$lockedHolding->security->trading_name}",
                'meta' => [
                    'security_id' => $lockedHolding->security_id,
                    'quantity' => $quantity,
                    'order_type' => 'sell',
                    'origin' => 'portfolio',
                ],
            ]);
        });

        $adminEmail = config('mail.admin_address');
        if (filled($adminEmail)) {
            Mail::to((string) $adminEmail)
                ->queue(new AdminTransactionMail(
                    auth()->user()->name,
                    auth()->user()->email,
                    $totalAmount,
                    app(DefaultCurrencyService::class)->code(),
                    'shares_sell',
                    'submitted',
                    'trade'
                ));
        }
        app(SmsAfrikasTalking::class)->sendShortMessage(
            auth()->user()->phone,
            "Sell order submitted for {$holding->security->trading_name}.",
        );

        $this->sellSubmittedAmount = $totalAmount;
        $this->sellSubmittedShares = $quantity;
        $this->sellSubmittedSymbol = (string) $holding->security->trading_name;
        $this->sellQuantities[$holdingId] = 0;
        $this->notifySuccess(__('Sell order submitted and pending admin approval.'));

        return true;
    }

    public function submitSellRequest(): void
    {
        $holdingId = (int) $this->sellHoldingId;
        $quantity = (int) $this->sellShares;
        if (! $this->canSell($holdingId, $quantity)) {
            return;
        }

        $this->sellQuantities[$holdingId] = $quantity;
        if (! $this->requestSell($holdingId)) {
            return;
        }

        $this->sellStep = 'result';
    }

    protected function canSell(int $holdingId, int $quantity): bool
    {
        if ($holdingId <= 0) {
            $this->notifyError(__('Portfolio holding not found.'));

            return false;
        }

        $holding = collect($this->holdings)->firstWhere('id', $holdingId);
        if (! $holding) {
            $this->notifyError(__('Portfolio holding not found.'));

            return false;
        }

        $availableQuantity = (int) ($holding['available_quantity'] ?? 0);
        $sellLimits = $this->sellLimits;

        if ($quantity <= 0) {
            $this->notifyError(__('Enter a valid quantity to sell.'));

            return false;
        }

        $minPerRequest = (int) ($sellLimits['min_per_request'] ?? 1);
        if ($quantity < $minPerRequest) {
            $this->notifyError(__('Minimum shares per sell request is :min.', ['min' => $minPerRequest]));

            return false;
        }

        if ($quantity > $availableQuantity) {
            $this->notifyError(__('Sell quantity exceeds your available shares.'));

            return false;
        }

        $maxPerRequest = (int) ($sellLimits['max_per_request'] ?? 0);
        if ($maxPerRequest > 0 && $quantity > $maxPerRequest) {
            $this->notifyError(__('Maximum shares per sell request is :max.', ['max' => $maxPerRequest]));

            return false;
        }

        $maxPerDay = (int) ($sellLimits['max_per_day'] ?? 0);
        if ($maxPerDay > 0 && $quantity > $this->sellRemainingDailyShares) {
            $this->notifyError(__('Daily sell limit exceeded. Remaining shares for today: :remaining.', ['remaining' => $this->sellRemainingDailyShares]));

            return false;
        }

        return true;
    }

    protected function view($data = [])
    {
        return app('view')->file('/home1/xxwewbmy/zidii.fxinvest.io/storage/framework/views/livewire/views/45e757b7.blade.php', $data);
    }
}; 