<?php

namespace App\Services\Finance;

use App\Models\Expense;
use App\Models\FnfSettlement;
use App\Models\PrePayment;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class FinanceDashboardStatsService
{
    /**
     * @return array<string, mixed>
     */
    public function selfStats(User $user): array
    {
        $companyId = $user->company_id;
        $monthStart = Carbon::now()->startOfMonth();
        $monthEnd = Carbon::now()->endOfMonth();

        $expenseBase = Expense::query()->where(function ($q) use ($user) {
            $q->where('user_id', $user->id)->orWhere('submitted_by', $user->id);
        });
        if ($companyId) {
            $expenseBase->where('company_id', $companyId);
        }

        $advanceBase = PrePayment::query()->where('user_id', $user->id);
        if ($companyId) {
            $advanceBase->where('company_id', $companyId);
        }

        $pendingClaims = (clone $expenseBase)->where('status', 'pending');
        $nextDeduction = (clone $advanceBase)
            ->where('status', PrePayment::STATUS_PENDING)
            ->orderBy('year')
            ->orderBy('month')
            ->orderBy('installment_number')
            ->first();

        return [
            'pending_claims' => (clone $pendingClaims)->count(),
            'pending_claims_amount' => (float) (clone $pendingClaims)->sum('amount'),
            'approved_mtd' => (float) (clone $expenseBase)
                ->where('status', 'approved')
                ->whereBetween('date', [$monthStart, $monthEnd])
                ->sum('amount'),
            'paid_mtd' => (float) (clone $expenseBase)
                ->where('status', 'paid')
                ->whereBetween('date', [$monthStart, $monthEnd])
                ->sum('amount'),
            'outstanding_advance' => (float) (clone $advanceBase)
                ->where('status', PrePayment::STATUS_PENDING)
                ->sum('amount'),
            'next_deduction' => $nextDeduction,
            'recent_claims' => (clone $expenseBase)
                ->orderByDesc('date')
                ->limit(5)
                ->get(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function teamStats(User $user): array
    {
        $companyId = $user->company_id;
        $ids = $user->visibleStaffUserIds();

        $query = Expense::query()->where(function ($q) use ($ids) {
            $q->whereIn('user_id', $ids)->orWhereIn('submitted_by', $ids);
        });
        if ($companyId) {
            $query->where('company_id', $companyId);
        }

        $pending = (clone $query)->where('status', 'pending');

        return [
            'pending_count' => (clone $pending)->count(),
            'pending_amount' => (float) (clone $pending)->sum('amount'),
            'team_size' => count($ids),
            'recent_pending' => (clone $pending)
                ->with('claimant')
                ->orderByDesc('date')
                ->limit(8)
                ->get(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function operationalStats(?int $companyId): array
    {
        $expensesQuery = Expense::query();
        $advancesQuery = PrePayment::query();
        $fnfQuery = FnfSettlement::query();

        if ($companyId) {
            $expensesQuery->where('company_id', $companyId);
            $advancesQuery->where('company_id', $companyId);
            $fnfQuery->where('company_id', $companyId);
        }

        $pendingExpenses = (clone $expensesQuery)->where('status', 'pending');

        return [
            'pending_expenses' => (clone $pendingExpenses)->count(),
            'pending_expenses_amount' => (float) (clone $pendingExpenses)->sum('amount'),
            'pending_advances' => (clone $advancesQuery)->where('status', PrePayment::STATUS_PENDING)->count(),
            'fnf_awaiting_action' => (clone $fnfQuery)
                ->whereIn('status', [FnfSettlement::STATUS_DRAFT, FnfSettlement::STATUS_PENDING_APPROVAL])
                ->count(),
            'recent_pending_expenses' => (clone $pendingExpenses)
                ->with('claimant')
                ->orderByDesc('date')
                ->limit(8)
                ->get(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function myAdvanceSummary(User $user): array
    {
        $companyId = $user->company_id;
        $base = PrePayment::query()->where('user_id', $user->id);
        if ($companyId) {
            $base->where('company_id', $companyId);
        }

        $pending = (clone $base)->where('status', PrePayment::STATUS_PENDING);

        $next = (clone $pending)
            ->orderBy('year')
            ->orderBy('month')
            ->orderBy('installment_number')
            ->first();

        return [
            'outstanding' => (float) (clone $pending)->sum('amount'),
            'pending_installments' => (clone $pending)->count(),
            'next_deduction' => $next,
        ];
    }

    /**
     * @return Collection<int, object{group_id: ?string, total_amount: float, remaining: float, installments: int}>
     */
    public function groupedAdvancePlans(User $user): Collection
    {
        $companyId = $user->company_id;
        $rows = PrePayment::query()
            ->where('user_id', $user->id)
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->where('status', PrePayment::STATUS_PENDING)
            ->orderByDesc('advance_date')
            ->get();

        return $rows
            ->groupBy(fn (PrePayment $p) => $p->installment_group_id ?: 'single-'.$p->id)
            ->map(function (Collection $group) {
                $first = $group->first();

                return (object) [
                    'group_id' => $first->installment_group_id,
                    'advance_date' => $first->advance_date,
                    'total_amount' => (float) ($first->total_amount ?? $group->sum('amount')),
                    'remaining' => (float) $group->sum('amount'),
                    'installments' => $first->installment_count ?? 1,
                    'paid_of' => $group->min('installment_number'),
                    'pending_of' => $group->count(),
                ];
            })
            ->values();
    }
}
