<?php

namespace App\Services\FlatTrade;

use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class FlatTradeClient
{
    private string $baseUrl;

    private ?string $uid = null;

    private ?string $actId = null;

    private ?string $sessionToken = null;

    public function __construct()
    {
        $this->baseUrl = rtrim(config('flattrade.api_base'), '/').'/';
    }

    public function setSession(string $uid, string $actId, string $sessionToken): self
    {
        $this->uid = $uid;
        $this->actId = $actId;
        $this->sessionToken = $sessionToken;

        return $this;
    }

    public function hasSession(): bool
    {
        return $this->uid && $this->actId && $this->sessionToken;
    }

    public function exchangeAuthCode(string $apiKey, string $requestCode, string $apiSecret): array
    {
        if (config('flattrade.mock')) {
            return [
                'stat' => 'Ok',
                'token' => 'mock_session_'.md5($requestCode),
                'client' => 'MOCK'.substr($apiKey, -4),
            ];
        }

        $hash = hash('sha256', $apiKey.$requestCode.$apiSecret);

        $response = Http::timeout(30)->post(config('flattrade.token_url'), [
            'api_key' => $apiKey,
            'request_code' => $requestCode,
            'api_secret' => $hash,
        ]);

        return $response->json() ?? ['stat' => 'Not_Ok', 'emsg' => 'Invalid token response'];
    }

    public function searchScrip(string $exchange, string $searchText): array
    {
        return $this->post('SearchScrip', [
            'uid' => $this->uid,
            'exch' => $exchange,
            'stext' => $searchText,
        ]);
    }

    public function getQuotes(string $exchange, string $token): array
    {
        return $this->post('GetQuotes', [
            'uid' => $this->uid,
            'exch' => $exchange,
            'token' => $token,
        ]);
    }

    public function getOptionChain(string $exchange, string $tradingsymbol, float $strikePrice, int $count = 5): array
    {
        return $this->post('GetOptionChain', [
            'uid' => $this->uid,
            'exch' => $exchange,
            'tsym' => $tradingsymbol,
            'strprc' => (string) $strikePrice,
            'cnt' => (string) $count,
        ]);
    }

    public function getTimePriceSeries(string $exchange, string $token, int $startTime, int $interval = 1): array
    {
        return $this->post('TPSeries', [
            'uid' => $this->uid,
            'exch' => $exchange,
            'token' => $token,
            'st' => (string) $startTime,
            'intrv' => (string) $interval,
        ]);
    }

    public function getPositions(): array
    {
        $response = $this->post('PositionBook', [
            'uid' => $this->uid,
            'actid' => $this->actId,
        ]);

        if (isset($response['stat']) && $response['stat'] === 'Not_Ok') {
            return [];
        }

        if (isset($response[0])) {
            return $response;
        }

        return $response['values'] ?? (is_array($response) ? $response : []);
    }

    public function getLimits(?string $productType = null, ?string $segment = null, ?string $exchange = null): array
    {
        $payload = [
            'uid' => $this->uid,
            'actid' => $this->actId,
        ];

        if ($productType) {
            $payload['prd'] = $productType;
        }
        if ($segment) {
            $payload['seg'] = $segment;
        }
        if ($exchange) {
            $payload['exch'] = $exchange;
        }

        return $this->post('Limits', $payload);
    }

    public function getOrderBook(): array
    {
        $response = $this->post('OrderBook', [
            'uid' => $this->uid,
            'actid' => $this->actId,
        ]);

        if (isset($response[0])) {
            return $response;
        }

        return $response['values'] ?? [];
    }

    public function placeOrder(array $order): array
    {
        return $this->post('PlaceOrder', array_merge([
            'uid' => $this->uid,
            'actid' => $this->actId,
            'ordersource' => 'API',
        ], $order));
    }

    public function modifyOrder(array $order): array
    {
        return $this->post('ModifyOrder', array_merge([
            'uid' => $this->uid,
            'actid' => $this->actId,
        ], $order));
    }

    public function cancelOrder(string $orderNo): array
    {
        return $this->post('CancelOrder', [
            'uid' => $this->uid,
            'actid' => $this->actId,
            'norenordno' => $orderNo,
        ]);
    }

    private function post(string $endpoint, array $payload): array
    {
        if (config('flattrade.mock')) {
            return $this->mockResponse($endpoint, $payload);
        }

        if (! $this->sessionToken) {
            return ['stat' => 'Not_Ok', 'emsg' => 'Session not configured'];
        }

        try {
            $response = Http::asForm()
                ->timeout(30)
                ->post($this->baseUrl.$endpoint, [
                    'jData' => json_encode($payload, JSON_UNESCAPED_SLASHES),
                    'jKey' => $this->sessionToken,
                ]);

            $body = $response->body();

            if ($body === '' || $body === 'null') {
                return [];
            }

            $decoded = json_decode($body, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                Log::warning('FlatTrade invalid JSON', ['endpoint' => $endpoint, 'body' => $body]);

                return ['stat' => 'Not_Ok', 'emsg' => 'Invalid JSON response'];
            }

            return $decoded ?? [];
        } catch (ConnectionException $e) {
            Log::error('FlatTrade connection error', ['endpoint' => $endpoint, 'error' => $e->getMessage()]);

            return ['stat' => 'Not_Ok', 'emsg' => $e->getMessage()];
        }
    }

    private function mockResponse(string $endpoint, array $payload): array
    {
        return match ($endpoint) {
            'GetQuotes' => [
                'stat' => 'Ok',
                'lp' => '24503.60',
                'o' => '24480.00',
                'h' => '24550.00',
                'l' => '24460.00',
                'c' => '24490.00',
                'v' => '0',
                'oi' => '0',
            ],
            'Limits' => [
                'stat' => 'Ok',
                'cash' => '20000.00',
                'marginused' => '0.00',
            ],
            'PositionBook' => [],
            'OrderBook' => [],
            'PlaceOrder' => ['stat' => 'Ok', 'norenordno' => 'MOCK'.time()],
            'ModifyOrder', 'CancelOrder' => ['stat' => 'Ok'],
            'SearchScrip' => [
                'stat' => 'Ok',
                'values' => [
                    ['exch' => 'NFO', 'token' => '12345', 'tsym' => 'NIFTY30JUL25C24500'],
                ],
            ],
            'GetOptionChain' => [
                'stat' => 'Ok',
                'values' => [],
            ],
            default => ['stat' => 'Ok'],
        };
    }
}
