<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\BinaryLedger;
use App\Models\BinaryMatchingIncome;
use App\Models\JoiningPairIncome;
use App\Models\Rank;
use App\Models\WalletTransaction;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class IncomeEngineController extends Controller
{
    public function index(Request $request): View
    {
        [$from, $to] = $this->dateRange($request);

        $joiningSummary = JoiningPairIncome::query()
            ->whereDate('run_date', '>=', $from)
            ->whereDate('run_date', '<=', $to)
            ->selectRaw('COUNT(*) as runs')
            ->selectRaw('COALESCE(SUM(gross_income), 0) as gross_income')
            ->selectRaw('COALESCE(SUM(capping_deduction), 0) as capping_deduction')
            ->selectRaw('COALESCE(SUM(net_income), 0) as net_income')
            ->first();

        $repurchaseSummary = BinaryMatchingIncome::query()
            ->whereDate('run_date', '>=', $from)
            ->whereDate('run_date', '<=', $to)
            ->selectRaw('COUNT(*) as runs')
            ->selectRaw('COALESCE(SUM(gross_income), 0) as gross_income')
            ->selectRaw('COALESCE(SUM(capping_deduction), 0) as capping_deduction')
            ->selectRaw('COALESCE(SUM(net_income), 0) as net_income')
            ->first();

        $incomeBySource = WalletTransaction::query()
            ->where('wallet_type', 'income')
            ->where('direction', 'credit')
            ->whereDate('transacted_at', '>=', $from)
            ->whereDate('transacted_at', '<=', $to)
            ->selectRaw("COALESCE(NULLIF(source_type, ''), 'other') as source")
            ->selectRaw('COUNT(*) as entries')
            ->selectRaw('COALESCE(SUM(amount), 0) as total_amount')
            ->groupBy('source')
            ->orderByDesc('total_amount')
            ->get();

        $carrySummary = DB::table('user_binary_carry')
            ->selectRaw('COALESCE(SUM(joining_left), 0) as joining_left')
            ->selectRaw('COALESCE(SUM(joining_right), 0) as joining_right')
            ->selectRaw('COALESCE(SUM(repurchase_left), 0) as repurchase_left')
            ->selectRaw('COALESCE(SUM(repurchase_right), 0) as repurchase_right')
            ->first();

        $pendingLedger = BinaryLedger::query()
            ->whereNull('processed_at')
            ->selectRaw('ledger_type')
            ->selectRaw('COUNT(*) as rows_count')
            ->selectRaw("COALESCE(SUM(CASE WHEN direction = 'credit' THEN units ELSE -units END), 0) as units")
            ->groupBy('ledger_type')
            ->orderBy('ledger_type')
            ->get();

        $chartLabels = [];
        $joiningSeries = [];
        $repurchaseSeries = [];
        $creditedSeries = [];

        $period = CarbonPeriod::create($from, $to);
        foreach ($period as $day) {
            $date = $day->toDateString();
            $chartLabels[] = $date;

            $joiningSeries[] = (float) JoiningPairIncome::query()
                ->whereDate('run_date', $date)
                ->sum('net_income');

            $repurchaseSeries[] = (float) BinaryMatchingIncome::query()
                ->whereDate('run_date', $date)
                ->sum('net_income');

            $creditedSeries[] = (float) WalletTransaction::query()
                ->where('wallet_type', 'income')
                ->where('direction', 'credit')
                ->whereDate('transacted_at', $date)
                ->whereIn('source_type', ['joining_matching', 'binary_matching'])
                ->sum('amount');
        }

        $rankCapping = Rank::query()
            ->where('is_active', true)
            ->orderBy('priority')
            ->get(['name', 'priority', 'daily_capping', 'required_active_directs', 'required_weak_leg_bv']);

        $settingsSnapshot = [
            'joining_pair_value' => (float) setting('joining_pair_value', 0),
            'pair_rate' => (float) setting('pair_rate', 0),
            'joining_daily_capping' => (float) setting('joining_daily_capping', 0),
            'repurchase_daily_capping' => (float) setting('repurchase_daily_capping', 0),
            'tds_percent' => (float) setting('tds_percent', 0),
            'charge_percent' => (float) setting('charge_percent', 0),
        ];

        $calculatorInput = [
            'type' => old('calc_type', 'repurchase'),
            'left' => (float) old('calc_left', 0),
            'right' => (float) old('calc_right', 0),
            'rate' => (float) old('calc_rate', $settingsSnapshot['pair_rate']),
            'pair_value' => (float) old('calc_pair_value', $settingsSnapshot['joining_pair_value']),
            'capping' => (float) old('calc_capping', 0),
        ];

        $calculatorResult = session('calc_result');

        return view('admin.income-engine', [
            'from' => $from,
            'to' => $to,
            'joiningSummary' => $joiningSummary,
            'repurchaseSummary' => $repurchaseSummary,
            'incomeBySource' => $incomeBySource,
            'carrySummary' => $carrySummary,
            'pendingLedger' => $pendingLedger,
            'chartLabels' => $chartLabels,
            'joiningSeries' => $joiningSeries,
            'repurchaseSeries' => $repurchaseSeries,
            'creditedSeries' => $creditedSeries,
            'rankCapping' => $rankCapping,
            'settingsSnapshot' => $settingsSnapshot,
            'calculatorInput' => $calculatorInput,
            'calculatorResult' => $calculatorResult,
        ]);
    }

    public function calculate(Request $request): RedirectResponse|JsonResponse
    {
        $validated = $request->validate([
            'calc_type' => ['required', 'in:joining,repurchase'],
            'calc_left' => ['required', 'numeric', 'min:0'],
            'calc_right' => ['required', 'numeric', 'min:0'],
            'calc_rate' => ['nullable', 'numeric', 'min:0'],
            'calc_pair_value' => ['nullable', 'numeric', 'min:0'],
            'calc_capping' => ['nullable', 'numeric', 'min:0'],
        ]);

        $result = $this->buildProjection($validated);

        if ($request->expectsJson()) {
            return response()->json(['result' => $result]);
        }

        return back()
            ->withInput()
            ->with('calc_result', $result);
    }

    /**
     * @param  array<string, mixed>  $validated
     * @return array<string, float|string>
     */
    private function buildProjection(array $validated): array
    {
        $type = (string) $validated['calc_type'];
        $left = (float) $validated['calc_left'];
        $right = (float) $validated['calc_right'];
        $matched = min($left, $right);
        $carryLeft = max(0, $left - $matched);
        $carryRight = max(0, $right - $matched);
        $capping = (float) ($validated['calc_capping'] ?? 0);

        if ($type === 'joining') {
            $pairValue = (float) ($validated['calc_pair_value'] ?? setting('joining_pair_value', 0));
            $gross = round($matched * $pairValue, 2);
            $effectiveCap = $capping > 0 ? $capping : (float) setting('joining_daily_capping', 0);
        } else {
            $rate = (float) ($validated['calc_rate'] ?? setting('pair_rate', 0));
            $gross = round($matched * $rate, 2);
            $effectiveCap = $capping > 0 ? $capping : (float) setting('repurchase_daily_capping', 0);
        }

        $net = $effectiveCap > 0 ? min($gross, $effectiveCap) : $gross;
        $deduction = round($gross - $net, 2);

        return [
            'type' => $type,
            'left_input' => $left,
            'right_input' => $right,
            'matched_units' => $matched,
            'gross_income' => $gross,
            'capping_applied' => $effectiveCap,
            'capping_deduction' => $deduction,
            'net_income' => round($net, 2),
            'carry_left' => round($carryLeft, 4),
            'carry_right' => round($carryRight, 4),
        ];
    }

    /**
     * @return array{0: string, 1: string}
     */
    private function dateRange(Request $request): array
    {
        $fallbackFrom = now()->subDays(13)->toDateString();
        $fallbackTo = now()->toDateString();

        $from = (string) $request->query('from', $fallbackFrom);
        $to = (string) $request->query('to', $fallbackTo);

        try {
            $fromDate = Carbon::parse($from)->toDateString();
            $toDate = Carbon::parse($to)->toDateString();
        } catch (\Throwable) {
            return [$fallbackFrom, $fallbackTo];
        }

        if ($fromDate > $toDate) {
            [$fromDate, $toDate] = [$toDate, $fromDate];
        }

        return [$fromDate, $toDate];
    }
}

