<?php

namespace App\Services\FlatTrade;

use App\Models\BrokerAccount;
use App\Models\Instrument;
use App\Models\Order;
use App\Models\Position;
use App\Models\User;
use Illuminate\Support\Facades\Log;

class FlatTradeAccountSyncService
{
    public function __construct(
        private FlatTradeClient $client,
        private FlatTradeAuthService $auth,
        private BrokerAccountResolver $resolver,
    ) {}

    public function syncAllConnectedUsers(): int
    {
        $count = 0;

        User::query()
            ->whereHas('brokerAccount', fn ($q) => $q->where('is_connected', true))
            ->with('brokerAccount')
            ->chunkById(50, function ($users) use (&$count) {
                foreach ($users as $user) {
                    if ($this->syncUser($user)) {
                        $count++;
                    }
                }
            });

        return $count;
    }

    public function syncUser(User $user): bool
    {
        $account = $this->resolver->tradingAccount($user);
        if (! $account || ! $this->auth->isTokenValid($account)) {
            return false;
        }

        try {
            $this->auth->bindClient($this->client, $account);

            $this->syncLimits($account);
            $this->syncPositions($user, $account);
            $this->syncOrders($user, $account);

            $account->update(['last_synced_at' => now()]);

            return true;
        } catch (\Throwable $e) {
            Log::error('FlatTrade user sync failed', [
                'user_id' => $user->id,
                'error' => $e->getMessage(),
            ]);

            return false;
        }
    }

    private function syncLimits(BrokerAccount $account): void
    {
        $limits = $this->client->getLimits();
        if (($limits['stat'] ?? '') === 'Not_Ok') {
            return;
        }

        $account->update([
            'available_balance' => (float) ($limits['cash'] ?? 0),
            'margin_used' => (float) ($limits['marginused'] ?? 0),
            'collateral' => isset($limits['collateral']) ? (float) $limits['collateral'] : null,
            'unrealized_pnl_total' => (float) ($limits['urmtom'] ?? 0),
            'sync_snapshot' => $limits,
        ]);
    }

    private function syncPositions(User $user, BrokerAccount $account): void
    {
        $remotePositions = $this->client->getPositions();
        if (! is_array($remotePositions)) {
            return;
        }

        $activeSymbols = [];
        $totalRealized = 0.0;
        $totalUnrealized = 0.0;

        foreach ($remotePositions as $remote) {
            $netQty = (int) ($remote['netqty'] ?? 0);
            $tsym = (string) ($remote['tsym'] ?? '');

            if ($netQty === 0 || $tsym === '') {
                continue;
            }

            $activeSymbols[] = $tsym;
            $instrument = Instrument::where('tradingsymbol', $tsym)->first();
            $unrealized = (float) ($remote['urmtom'] ?? 0);
            $realized = (float) ($remote['rpnl'] ?? 0);
            $totalUnrealized += $unrealized;
            $totalRealized += $realized;

            Position::updateOrCreate(
                [
                    'user_id' => $user->id,
                    'tradingsymbol' => $tsym,
                    'is_open' => true,
                ],
                [
                    'instrument_id' => $instrument?->id,
                    'exchange' => $remote['exch'] ?? 'NFO',
                    'product_type' => $remote['prd'] ?? 'I',
                    'net_quantity' => $netQty,
                    'net_avg_price' => (float) ($remote['netavgprc'] ?? 0),
                    'ltp' => (float) ($remote['lp'] ?? 0),
                    'unrealized_pnl' => $unrealized,
                    'realized_pnl' => $realized,
                    'price_factor' => (float) ($remote['prcftr'] ?? 1),
                    'opened_at' => now(),
                ]
            );
        }

        Position::query()
            ->where('user_id', $user->id)
            ->where('is_open', true)
            ->when(count($activeSymbols) > 0, fn ($q) => $q->whereNotIn('tradingsymbol', $activeSymbols))
            ->when(count($activeSymbols) === 0, fn ($q) => $q)
            ->update(['is_open' => false, 'closed_at' => now()]);

        $account->update([
            'unrealized_pnl_total' => $totalUnrealized,
            'realized_pnl_total' => $totalRealized,
        ]);
    }

    private function syncOrders(User $user, BrokerAccount $account): void
    {
        $remoteOrders = $this->client->getOrderBook();
        if (! is_array($remoteOrders)) {
            return;
        }

        foreach ($remoteOrders as $remote) {
            $brokerOrderNo = (string) ($remote['norenordno'] ?? '');
            if ($brokerOrderNo === '') {
                continue;
            }

            $instrument = Instrument::where('tradingsymbol', $remote['tsym'] ?? '')->first();

            Order::updateOrCreate(
                [
                    'user_id' => $user->id,
                    'broker_order_no' => $brokerOrderNo,
                ],
                [
                    'instrument_id' => $instrument?->id,
                    'exchange' => $remote['exch'] ?? 'NFO',
                    'tradingsymbol' => $remote['tsym'] ?? '',
                    'transaction_type' => $remote['trantype'] ?? 'B',
                    'product_type' => $remote['prd'] ?? 'I',
                    'price_type' => $remote['prctyp'] ?? 'LMT',
                    'quantity' => (int) ($remote['qty'] ?? 0),
                    'price' => (float) ($remote['prc'] ?? 0),
                    'trigger_price' => isset($remote['trgprc']) ? (float) $remote['trgprc'] : null,
                    'avg_price' => isset($remote['avgprc']) ? (float) $remote['avgprc'] : null,
                    'filled_quantity' => (int) ($remote['fillshares'] ?? 0),
                    'status' => $remote['status'] ?? 'UNKNOWN',
                    'remarks' => $remote['remarks'] ?? null,
                    'placed_at' => now(),
                ]
            );
        }
    }
}
