<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\Deposit;
use App\Models\Expense;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class FinanceDashboardController extends Controller
{
    public function __construct()
    {
        $this->middleware('finance.treasury');
    }

    public function index()
    {
        $companyId = Auth::user()?->company_id;
        $now = Carbon::now();
        $monthStart = $now->copy()->startOfMonth();
        $monthEnd = $now->copy()->endOfMonth();

        $accountsQuery = Account::query();
        $expensesQuery = Expense::query();
        $depositsQuery = Deposit::query();

        if ($companyId) {
            $accountsQuery->where('company_id', $companyId);
            $expensesQuery->where('company_id', $companyId);
            $depositsQuery->where('company_id', $companyId);
        }

        $accountCashBalance = (float) (clone $accountsQuery)->sum('current_balance');
        $accountCount = (clone $accountsQuery)->count();

        $mtdDeposits = (float) (clone $depositsQuery)
            ->whereBetween('date', [$monthStart, $monthEnd])
            ->sum('amount');

        $mtdExpenses = (float) (clone $expensesQuery)
            ->whereBetween('date', [$monthStart, $monthEnd])
            ->whereIn('status', ['approved', 'paid'])
            ->sum('amount');

        $totalCashBalance = $accountCashBalance - $mtdExpenses;

        $pendingExpensesCount = (clone $expensesQuery)->where('status', 'pending')->count();
        $pendingExpensesAmount = (float) (clone $expensesQuery)->where('status', 'pending')->sum('amount');

        $statusKeys = ['pending', 'approved', 'paid', 'rejected'];
        $byStatus = (clone $expensesQuery)
            ->select('status', DB::raw('count(*) as total'))
            ->groupBy('status')
            ->pluck('total', 'status')
            ->toArray();

        foreach ($statusKeys as $key) {
            $byStatus[$key] = (int) ($byStatus[$key] ?? 0);
        }

        $accountBalances = (clone $accountsQuery)
            ->orderByDesc('current_balance')
            ->limit(5)
            ->get(['id', 'name', 'type', 'current_balance']);

        $expenseByCategory = (clone $expensesQuery)
            ->whereBetween('date', [$monthStart, $monthEnd])
            ->select('category', DB::raw('sum(amount) as total'))
            ->groupBy('category')
            ->orderByDesc('total')
            ->limit(6)
            ->get()
            ->map(fn ($row) => ['name' => $row->category ?: 'Uncategorized', 'total' => (float) $row->total])
            ->values()
            ->toArray();

        $monthlyFlow = $this->monthlyCashFlow($depositsQuery, $expensesQuery, 6);

        $recentTransactions = $this->recentTransactions($depositsQuery, $expensesQuery, 8);

        $netCashFlowMtd = $mtdDeposits - $mtdExpenses;

        return view('finance.dashboard', compact(
            'totalCashBalance',
            'accountCashBalance',
            'accountCount',
            'mtdDeposits',
            'mtdExpenses',
            'netCashFlowMtd',
            'pendingExpensesCount',
            'pendingExpensesAmount',
            'byStatus',
            'accountBalances',
            'expenseByCategory',
            'monthlyFlow',
            'recentTransactions',
        ));
    }

    /**
     * @return array<int, array{label: string, deposits: float, expenses: float}>
     */
    protected function monthlyCashFlow($depositsQuery, $expensesQuery, int $months): array
    {
        $start = Carbon::now()->subMonths($months - 1)->startOfMonth();
        $labels = [];
        $depositTotals = [];
        $expenseTotals = [];

        for ($i = 0; $i < $months; $i++) {
            $month = $start->copy()->addMonths($i);
            $labels[] = $month->format('M Y');
            $from = $month->copy()->startOfMonth();
            $to = $month->copy()->endOfMonth();

            $depositTotals[] = (float) (clone $depositsQuery)
                ->whereBetween('date', [$from, $to])
                ->sum('amount');

            $expenseTotals[] = (float) (clone $expensesQuery)
                ->whereBetween('date', [$from, $to])
                ->whereIn('status', ['approved', 'paid'])
                ->sum('amount');
        }

        $flow = [];
        foreach ($labels as $index => $label) {
            $flow[] = [
                'label' => $label,
                'deposits' => $depositTotals[$index],
                'expenses' => $expenseTotals[$index],
            ];
        }

        return $flow;
    }

    protected function recentTransactions($depositsQuery, $expensesQuery, int $limit): Collection
    {
        $deposits = (clone $depositsQuery)
            ->with('account')
            ->orderByDesc('date')
            ->orderByDesc('id')
            ->limit($limit)
            ->get()
            ->map(fn (Deposit $d) => [
                'type' => 'deposit',
                'label' => $d->account?->name ?? 'Deposit',
                'reference' => $d->reference ?: ($d->description ?: '—'),
                'amount' => (float) $d->amount,
                'date' => $d->date,
                'edit_route' => route('deposits.edit', $d),
            ]);

        $expenses = (clone $expensesQuery)
            ->with('account')
            ->orderByDesc('date')
            ->orderByDesc('id')
            ->limit($limit)
            ->get()
            ->map(fn (Expense $e) => [
                'type' => 'expense',
                'label' => $e->category ?: 'Expense',
                'reference' => $e->description ?: ($e->account?->name ?? '—'),
                'amount' => (float) $e->amount,
                'date' => $e->date,
                'status' => $e->status,
                'edit_route' => $e->status === 'paid' || $e->status === 'rejected'
                    ? route('expenses.show', $e)
                    : route('expenses.edit', $e),
            ]);

        return $deposits
            ->concat($expenses)
            ->sortByDesc(fn ($row) => $row['date']?->timestamp ?? 0)
            ->take($limit)
            ->values();
    }
}
