<?php

namespace App\Http\Controllers\Client;

use App\Http\Controllers\Controller;
use App\Models\Market;
use App\Models\Trade;
use App\Models\TradePeriod;
use App\Services\TradeExecutionService;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;

class TradeController extends Controller
{
    public function __construct(
        private TradeExecutionService $trades
    ) {}

    public function index(Request $request)
    {
        $user = $request->user();
        $marketGroups = [
            'Coins' => Market::where('type', 'crypto')->orderBy('sort_order')->orderBy('name')->get(),
            'Commodities' => Market::where('type', 'commodity')->orderBy('sort_order')->orderBy('name')->get(),
            'Metals' => Market::where('type', 'metal')->orderBy('sort_order')->orderBy('name')->get(),
        ];

        $allMarkets = $marketGroups['Coins']
            ->concat($marketGroups['Commodities'])
            ->concat($marketGroups['Metals']);

        $selectedMarket = Market::find($request->get('market', $allMarkets->first()?->id));

        if (! $selectedMarket && $request->has('symbol')) {
            $selectedMarket = Market::where('symbol', $request->get('symbol'))->first();
        }

        if (! $selectedMarket) {
            $selectedMarket = Market::where('symbol', 'XAUUSD')->first()
                ?? $allMarkets->first();
        }

        $periods = TradePeriod::query()
            ->where('is_active', true)
            ->forMarketType($selectedMarket?->type)
            ->orderBy('duration_seconds')
            ->get();

        $tradingViewSymbol = $selectedMarket?->tradingViewSymbol() ?? 'TVC:GOLD';

        $defaultDirection = in_array($request->get('direction'), ['buy', 'sell'], true)
            ? $request->get('direction')
            : 'buy';

        $activeTrade = $this->trades->findActiveTrade($user);

        if ($activeTrade && $activeTrade->ends_at?->isPast()) {
            $this->trades->settleTrade($activeTrade);
            $activeTrade = null;
        }

        $tradeConfig = [
            'storeUrl' => route('client.trade.store'),
            'settleUrl' => route('client.trade.settle'),
            'tradePageUrl' => route('client.trade'),
            'quoteCurrency' => $selectedMarket?->quoteCurrency() ?? 'USDT',
            'currency' => \App\Models\SiteSetting::get('site_currency', 'USDT'),
            'csrf' => csrf_token(),
            'activeTrade' => null,
        ];

        if ($activeTrade) {
            $tradeConfig['activeTrade'] = [
                'trade_id' => $activeTrade->id,
                'ends_at' => $activeTrade->ends_at?->toIso8601String(),
                'duration_seconds' => $activeTrade->period->duration_seconds,
                'direction' => $activeTrade->direction,
                'amount' => (float) $activeTrade->amount,
                'pair' => $activeTrade->market->tradePairLabel(),
            ];
        }

        return view('client.trade.index', compact(
            'marketGroups',
            'selectedMarket',
            'periods',
            'tradingViewSymbol',
            'defaultDirection',
            'activeTrade',
            'tradeConfig',
        ));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'market_id' => ['required', 'exists:markets,id'],
            'trade_period_id' => ['required', 'exists:trade_periods,id'],
            'direction' => ['required', 'in:buy,sell'],
            'amount' => ['required', 'numeric', 'min:1'],
        ]);

        $user = $request->user();
        $market = Market::findOrFail($data['market_id']);
        $period = TradePeriod::findOrFail($data['trade_period_id']);

        if ($period->market_type && $period->market_type !== $market->type) {
            return $this->tradeError($request, 'Selected period is not available for this market.');
        }

        try {
            $trade = $this->trades->openTrade($user, $data, $market, $period);
        } catch (\Illuminate\Validation\ValidationException $e) {
            return $this->tradeError($request, $e->validator->errors()->first());
        }

        $user->refresh();
        $trade->load(['market', 'period']);

        $payload = [
            'trade_id' => $trade->id,
            'ends_at' => $trade->ends_at?->toIso8601String(),
            'duration_seconds' => $trade->period->duration_seconds,
            'direction' => $trade->direction,
            'amount' => (float) $trade->amount,
            'pair' => $trade->market->tradePairLabel(),
            'balance' => (float) $user->balance,
        ];

        if ($request->expectsJson()) {
            return response()->json($payload);
        }

        return redirect()->route('client.trade')->with('trade_active', $payload);
    }

    public function settle(Request $request)
    {
        $data = $request->validate([
            'trade_id' => ['required', 'integer', 'min:1'],
        ]);

        $trade = Trade::query()
            ->ownedBy($request->user())
            ->findOrFail($data['trade_id']);

        try {
            $result = $this->trades->settleTrade($trade);
        } catch (\Illuminate\Validation\ValidationException $e) {
            if ($request->expectsJson()) {
                return response()->json(['message' => $e->validator->errors()->first()], 422);
            }

            return back()->with('error', $e->validator->errors()->first());
        } catch (QueryException $e) {
            report($e);
            $message = 'Could not save trade result. Run php artisan migrate on the server, then try again.';
            if ($request->expectsJson()) {
                return response()->json(['message' => $message], 500);
            }

            return back()->with('error', $message);
        }

        if ($request->expectsJson()) {
            return response()->json($result);
        }

        return redirect()->route('client.trade')->with('trade_result', $result);
    }

    private function tradeError(Request $request, string $message)
    {
        if ($request->expectsJson()) {
            return response()->json(['message' => $message], 422);
        }

        return back()->with('error', $message)->withInput();
    }
}
