<?php

use App\Models\PortfolioHolding;
use App\Models\Security;
use App\Models\SecurityOrder;
use App\Models\SecurityWatchlist;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Jobs\Vrm\SendMail;
use App\Mail\AdminTransactionMail;
use App\Mail\WalletTransactionMail;
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 Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Component;

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

    public string $search = '';
    public string $activeFilter = 'All';
    public bool $showWatchlist = false;
    public array $orderQuantities = [];
    public array $orderAmounts = [];
    public array $sellQuantities = [];
    public ?int $selectedSecurityId = null;
    public string $tradeStep = 'browse';
    public string $tradeAction = 'buy';
    public string $tradeAmount = '';
    public string $tradeQuantity = '';
    public string $tradeResultStatus = '';
    public string $tradeResultMessage = '';
    public string $tradeResultSymbol = '';
    public float $tradeResultTotalAmount = 0.0;
    public int $tradeResultQuantity = 0;

    public function mount(): void
    {
        $this->selectedSecurityId = Security::query()
            ->where('is_active', true)
            ->orderBy('trading_name')
            ->value('id');
    }

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

    /** System default currency from ATU (single currency across the app). */
    #[Computed]
    public function currencyCode(): string
    {
        return app(DefaultCurrencyService::class)->code();
    }

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

    #[Computed]
    public function securities()
    {
        return Security::query()
            ->where('is_active', true)
            ->with('latestLog')
            ->orderBy('trading_name')
            ->get()
            ->map(fn (Security $security): array => $this->mapSecurityForDisplay($security))
            ->values()
            ->all();
    }

    #[Computed]
    public function watchlistIds(): array
    {
        return auth()->user()->watchlistItems()->pluck('security_id')->all();
    }

    #[Computed]
    public function investedSecurityIds(): array
    {
        return auth()->user()
            ->portfolioHoldings()
            ->where(function ($query): void {
                $query
                    ->where('quantity', '>', 0)
                    ->orWhere('total_invested', '>', 0);
            })
            ->pluck('security_id')
            ->all();
    }

    #[Computed]
    public function holdingMetricsBySecurityId(): array
    {
        return auth()->user()
            ->portfolioHoldings()
            ->with('security')
            ->get()
            ->mapWithKeys(function (PortfolioHolding $holding): array {
                $currentValue = (float) $holding->quantity * (float) $holding->security->latestPrice();
                $gainLossAmount = $currentValue - (float) $holding->total_invested;
                $gainLossPercent = (float) $holding->total_invested > 0
                    ? ($gainLossAmount / (float) $holding->total_invested) * 100
                    : 0.0;

                return [
                    (int) $holding->security_id => [
                        'shares' => (int) $holding->quantity,
                        'available_shares' => max(0, (int) $holding->quantity - (int) ($holding->reserved_quantity ?? 0)),
                        'current_value' => $currentValue,
                        'gain_loss_percent' => $gainLossPercent,
                    ],
                ];
            })
            ->all();
    }

    #[Computed]
    public function filteredSecurities()
    {
        $securities = collect($this->securities)->filter(function (array $security): bool {
            if ($this->search === '') {
                return true;
            }

            $query = strtolower($this->search);

            return str_contains(strtolower((string) ($security['symbol'] ?? '')), $query)
                || str_contains(strtolower((string) ($security['name'] ?? '')), $query);
        });

        // if ($this->activeFilter === 'Gainers') {
        //     $securities = $securities->filter(fn (array $security): bool => ((float) ($security['change'] ?? 0)) > 0);
        // }
        //
        // if ($this->activeFilter === 'Losers') {
        //     $securities = $securities->filter(fn (array $security): bool => ((float) ($security['change'] ?? 0)) < 0);
        // }

        return $securities->values()->all();
    }

    #[Computed]
    public function watchlistSecurities()
    {
        return collect($this->securities)
            ->filter(fn (array $security): bool => in_array((int) ($security['id'] ?? 0), $this->watchlistIds, true))
            ->values()
            ->all();
    }

    #[Computed]
    public function selectedSecurity(): ?Security
    {
        if ($this->selectedSecurityId === null) {
            return null;
        }

        return Security::query()
            ->where('is_active', true)
            ->find($this->selectedSecurityId);
    }

    #[Computed]
    public function selectedHolding(): array
    {
        if ($this->selectedSecurityId === null) {
            return [
                'quantity' => 0,
                'reserved_quantity' => 0,
                'available_quantity' => 0,
                'average_buy_price' => 0,
                'total_invested' => 0,
                'current_value' => 0,
                'gain_loss_amount' => 0,
                'gain_loss_percent' => 0,
            ];
        }

        $holding = PortfolioHolding::query()
            ->where('user_id', auth()->id())
            ->where('security_id', $this->selectedSecurityId)
            ->first();

        if (! $holding || ! $this->selectedSecurity) {
            return [
                'quantity' => 0,
                'reserved_quantity' => 0,
                'available_quantity' => 0,
                'average_buy_price' => 0,
                'total_invested' => 0,
                'current_value' => 0,
                'gain_loss_amount' => 0,
                'gain_loss_percent' => 0,
            ];
        }

        $quantity = (int) $holding->quantity;
        $reservedQuantity = (int) ($holding->reserved_quantity ?? 0);
        $availableQuantity = max(0, $quantity - $reservedQuantity);
        $averageBuyPrice = (float) $holding->average_buy_price;
        $totalInvested = (float) $holding->total_invested;
        $currentValue = (float) $this->selectedSecurity->latestPrice() * $quantity;
        $gainLossAmount = $currentValue - $totalInvested;
        $gainLossPercent = $totalInvested > 0 ? ($gainLossAmount / $totalInvested) * 100 : 0;

        return [
            'quantity' => $quantity,
            'reserved_quantity' => $reservedQuantity,
            'available_quantity' => $availableQuantity,
            'average_buy_price' => $averageBuyPrice,
            'total_invested' => $totalInvested,
            'current_value' => $currentValue,
            'gain_loss_amount' => $gainLossAmount,
            'gain_loss_percent' => $gainLossPercent,
        ];
    }

    public function setFilter(string $filter): void
    {
        $this->activeFilter = $filter;
    }

    public function toggleWatchlist(): void
    {
        $this->showWatchlist = ! $this->showWatchlist;
    }

    public function selectSecurity(int $securityId): void
    {
        $security = Security::query()
            ->where('is_active', true)
            ->find($securityId);

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

            return;
        }

        $this->selectedSecurityId = $securityId;
    }

    public function toggleSecurityWatchlist(int $securityId): void
    {
        $existing = SecurityWatchlist::query()
            ->where('user_id', auth()->id())
            ->where('security_id', $securityId)
            ->first();

        if ($existing) {
            $existing->delete();
            $this->notifyInfo(__('Removed from watchlist.'));

            return;
        }

        SecurityWatchlist::query()->create([
            'user_id' => auth()->id(),
            'security_id' => $securityId,
        ]);

        $this->notifySuccess(__('Added to watchlist.'));
    }

    public function placeOrder(int $securityId): void
    {
        $security = Security::query()->find($securityId);

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

            return;
        }

        $quantity = (int) ($this->orderQuantities[$securityId] ?? 0);

        $this->submitOrder($security, $quantity, 'buy');
    }

    public function placeBuyOrder(int $securityId): bool
    {
        $security = Security::query()->find($securityId);

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

            return false;
        }

        $amount = (float) ($this->orderAmounts[$securityId] ?? 0);

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

            return false;
        }

        $quantity = (int) floor($amount / $this->resolveOrderPrice($security));

        if ($quantity <= 0) {
            $this->notifyError(__('Amount is too low for this security price.'));

            return false;
        }

        return $this->submitOrder($security, $quantity, 'buy');
    }

    public function placeSellOrder(int $securityId): bool
    {
        $security = Security::query()->find($securityId);

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

            return false;
        }

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

        return $this->submitOrder($security, $quantity, 'sell');
    }

    protected function submitOrder(Security $security, int $quantity, string $orderType): bool
    {
        if (! $this->isMarketOpen) {
            $this->notifyError(__('Market is currently closed. Trading is unavailable.'));

            return false;
        }

        if ($quantity < $security->min_shares) {
            $this->notifyError(__('Quantity must be at least :min shares.', ['min' => $security->min_shares]));

            return false;
        }

        if ($security->max_shares_purchase > 0 && $quantity > $security->max_shares_purchase) {
            $this->notifyError(__('Quantity exceeds max shares per purchase.'));

            return false;
        }

        $wallet = $this->wallet;
        $pricePerShare = $this->resolveOrderPrice($security);
        $totalAmount = $pricePerShare * $quantity;

        if ($orderType === 'buy') {
            $existingHoldingQuantity = (int) auth()->user()
                ->portfolioHoldings()
                ->where('security_id', $security->id)
                ->value('quantity');

            if ($security->max_shares_holding > 0 && ($existingHoldingQuantity + $quantity) > $security->max_shares_holding) {
                $this->notifyError(__('Order exceeds your maximum allowed holding for this security.'));

                return false;
            }

            if ($wallet->available_balance < $totalAmount) {
                $this->notifyError(__('Insufficient available balance.'));

                return false;
            }

            DB::transaction(function () use ($security, $quantity, $wallet, $totalAmount, $pricePerShare): void {
                SecurityOrder::query()->create([
                    'user_id' => auth()->id(),
                    'security_id' => $security->id,
                    'wallet_id' => $wallet->id,
                    'order_type' => 'buy',
                    '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' => true,
                ]);

                $wallet->used_amount = (float) $wallet->used_amount + $totalAmount;
                $wallet->save();

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

            $this->orderQuantities[$security->id] = 0;
            $this->orderAmounts[$security->id] = '';
            $adminEmail = config('mail.admin_address');
            if (filled($adminEmail)) {
                SendMail::dispatch((string) $adminEmail, mailable: new AdminTransactionMail(
                        auth()->user()->name,
                        auth()->user()->email,
                        $totalAmount,
                        app(DefaultCurrencyService::class)->code(),
                        'shares_buy',
                        'submitted',
                        'trade'
                    ));
            }
            SendMail::dispatch((string) auth()->user()->email, mailable: new WalletTransactionMail(
                auth()->user()->name,
                'order_debit',
                app(DefaultCurrencyService::class)->code(),
                $totalAmount,
                'pending',
                auth()->user()->phone,
                null,
                now()->format('M j, Y g:i A'),
            ));
            app(SmsAfrikasTalking::class)->sendShortMessage(
                auth()->user()->phone,
                "Buy order submitted for {$security->trading_name}.",
            );
            $this->notifySuccess(__('Buy order submitted and pending admin approval.'));

            return true;
        }

        try {
            DB::transaction(function () use ($security, $quantity, $wallet, $totalAmount, $pricePerShare): void {
                $holding = PortfolioHolding::query()
                    ->where('user_id', auth()->id())
                    ->where('security_id', $security->id)
                    ->lockForUpdate()
                    ->first();

                if (! $holding || (int) $holding->quantity <= 0) {
                    throw new RuntimeException(__('You do not have this security in your portfolio.'));
                }

                $availableQuantity = max(0, (int) $holding->quantity - (int) ($holding->reserved_quantity ?? 0));

                if ($availableQuantity < $quantity) {
                    throw new RuntimeException(__('Sell quantity exceeds your available shares.'));
                }

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

                SecurityOrder::query()->create([
                    'user_id' => auth()->id(),
                    'security_id' => $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 {$security->trading_name}",
                    'meta' => ['security_id' => $security->id, 'quantity' => $quantity, 'order_type' => 'sell'],
                ]);
            });
        } catch (RuntimeException $exception) {
            $this->notifyError($exception->getMessage());

            return false;
        }

        $this->sellQuantities[$security->id] = 0;
        $adminEmail = config('mail.admin_address');
        if (filled($adminEmail)) {
            SendMail::dispatch((string) $adminEmail, mailable: new AdminTransactionMail(
                    auth()->user()->name,
                    auth()->user()->email,
                    $totalAmount,
                    app(DefaultCurrencyService::class)->code(),
                    'shares_sell',
                    'submitted',
                    'trade'
                ));
        }
        SendMail::dispatch((string) auth()->user()->email, mailable: new WalletTransactionMail(
            auth()->user()->name,
            'order_credit',
            app(DefaultCurrencyService::class)->code(),
            $totalAmount,
            'pending',
            auth()->user()->phone,
            null,
            now()->format('M j, Y g:i A'),
        ));
        app(SmsAfrikasTalking::class)->sendShortMessage(
            auth()->user()->phone,
            "Sell order submitted for {$security->trading_name}.",
        );
        $this->notifySuccess(__('Sell order submitted and pending admin approval.'));

        return true;
    }

    public function startBuyFlow(int $securityId): void
    {
        $security = Security::query()
            ->where('is_active', true)
            ->find($securityId);

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

            return;
        }

        $this->selectedSecurityId = $securityId;
        $this->tradeAction = 'buy';
        $this->tradeAmount = number_format($this->resolveOrderPrice($security) * max(1, (int) $security->min_shares), 2, '.', '');
        $this->tradeQuantity = '';
        $this->tradeStep = 'amount';
    }

    public function startSellFlow(int $securityId): void
    {
        $security = Security::query()
            ->where('is_active', true)
            ->find($securityId);

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

            return;
        }

        $metrics = $this->holdingMetricsBySecurityId[$securityId] ?? null;
        $availableShares = (int) ($metrics['available_shares'] ?? 0);

        if ($availableShares <= 0) {
            $this->notifyError(__('No available shares to sell.'));

            return;
        }

        $this->selectedSecurityId = $securityId;
        $this->tradeAction = 'sell';
        $this->tradeQuantity = '1';
        $this->tradeAmount = '';
        $this->tradeStep = 'amount';
    }

    public function goToTradeConfirmation(): void
    {
        if (! $this->selectedSecurity) {
            $this->notifyError(__('Security not found.'));

            return;
        }

        if ($this->tradeAction === 'buy') {
            $amount = (float) $this->tradeAmount;
            if ($amount <= 0) {
                $this->notifyError(__('Enter a valid amount to invest.'));

                return;
            }
        } else {
            $quantity = (int) $this->tradeQuantity;
            $availableShares = (int) (($this->holdingMetricsBySecurityId[$this->selectedSecurityId ?? 0]['available_shares'] ?? 0));
            if ($quantity <= 0 || $quantity > $availableShares) {
                $this->notifyError(__('Sell quantity exceeds your available shares.'));

                return;
            }
        }

        $this->tradeStep = 'confirm';
    }

    public function backToTradeAmount(): void
    {
        $this->tradeStep = 'amount';
    }

    public function submitTradeRequest(): void
    {
        if (! $this->selectedSecurity) {
            $this->notifyError(__('Security not found.'));

            return;
        }

        $securityId = $this->selectedSecurity->id;
        $succeeded = false;

        if ($this->tradeAction === 'buy') {
            $this->orderAmounts[$securityId] = (float) $this->tradeAmount;
            $succeeded = $this->placeBuyOrder($securityId);
            $quantity = (int) floor(((float) $this->tradeAmount) / $this->resolveOrderPrice($this->selectedSecurity));
            $this->tradeResultQuantity = max(0, $quantity);
            $this->tradeResultTotalAmount = (float) $this->tradeAmount;
        } else {
            $quantity = (int) $this->tradeQuantity;
            $this->sellQuantities[$securityId] = $quantity;
            $succeeded = $this->placeSellOrder($securityId);
            $this->tradeResultQuantity = max(0, $quantity);
            $this->tradeResultTotalAmount = $this->resolveOrderPrice($this->selectedSecurity) * max(0, $quantity);
        }

        if (! $succeeded) {
            $this->tradeStep = 'amount';

            return;
        }

        $this->tradeResultStatus = 'pending';
        $this->tradeResultMessage = $this->tradeAction === 'buy'
            ? __('Your buy request has been submitted and is pending admin approval.')
            : __('Your sell request has been submitted and is pending admin approval.');
        $this->tradeResultSymbol = (string) $this->selectedSecurity->trading_name;
        $this->tradeStep = 'result';
    }

    public function finishTradeFlow(): void
    {
        $this->tradeStep = 'browse';
        $this->tradeAction = 'buy';
        $this->tradeAmount = '';
        $this->tradeQuantity = '';
        $this->tradeResultStatus = '';
        $this->tradeResultMessage = '';
        $this->tradeResultSymbol = '';
        $this->tradeResultTotalAmount = 0.0;
        $this->tradeResultQuantity = 0;
    }

    public function estimatedTradeShares(): int
    {
        if ($this->tradeAction !== 'buy' || ! $this->selectedSecurity) {
            return 0;
        }

        $amount = (float) $this->tradeAmount;
        if ($amount <= 0) {
            return 0;
        }

        return max(0, (int) floor($amount / $this->resolveOrderPrice($this->selectedSecurity)));
    }

    private function mapSecurityForDisplay(Security $security): array
    {
        return [
            'id' => $security->id,
            'symbol' => strtoupper((string) $security->trading_name),
            'name' => (string) ($security->name ?: $security->trading_name),
            'price' => $security->latestPrice(),
            'change' => $security->latestChangePercent(),
            'logo' => $security->logo,
            'initials' => $this->buildInitials((string) $security->trading_name, (string) ($security->name ?: $security->trading_name)),
        ];
    }

    private function buildInitials(string $symbol, string $name): string
    {
        $lettersOnly = preg_replace('/[^A-Z]/', '', strtoupper($symbol)) ?? '';
        if ($lettersOnly !== '') {
            return substr($lettersOnly, 0, 2);
        }

        $nameParts = preg_split('/\s+/', trim($name)) ?: [];
        $initials = collect($nameParts)
            ->filter()
            ->take(2)
            ->map(fn (string $part): string => strtoupper(substr($part, 0, 1)))
            ->implode('');

        return $initials !== '' ? $initials : 'ST';
    }

    private function resolveOrderPrice(Security $security): float
    {
        return max(0.0001, (float) $security->latestPrice());
    }

    protected function view($data = [])
    {
        return app('view')->file('/home/ziidiaik/home.ziiditrader.com/storage/framework/views/livewire/views/88213445.blade.php', $data);
    }
}; 