<?php

namespace App\Support;

use App\Models\Expense;
use App\Models\Sale;
use App\Models\SalePayment;
use Carbon\Carbon;

class DailyCashSummary
{
    /**
     * @return array{date_from: string, date_to: string, quick_range: string}
     */
    public static function defaultFilters(): array
    {
        $today = Carbon::now()->toDateString();

        return [
            'date_from' => $today,
            'date_to' => $today,
            'quick_range' => 'today',
        ];
    }

    /**
     * @param  array{date_from?: string|null, date_to?: string|null, quick_range?: string|null}  $filters
     * @return array<string, mixed>|null
     */
    public static function overview(array $filters = []): ?array
    {
        $user = auth()->user();

        if ($user === null) {
            return null;
        }

        $canViewSales = $user->can('sales.view');
        $canViewExpenses = $user->can('expense-reports.view');

        if (! $canViewSales && ! $canViewExpenses) {
            return null;
        }

        $defaults = self::defaultFilters();
        $dateFrom = $filters['date_from'] ?? $defaults['date_from'];
        $dateTo = $filters['date_to'] ?? $defaults['date_to'];
        $quickRange = $filters['quick_range'] ?? $defaults['quick_range'];
        $isSingleDay = $dateFrom === $dateTo;

        $salesTotal = 0.0;
        $receivedTotal = 0.0;
        $salesDue = 0.0;

        if ($canViewSales) {
            $salesTotal = (float) Sale::query()
                ->where('type', SaleType::SALE)
                ->whereDate('sold_at', '>=', $dateFrom)
                ->whereDate('sold_at', '<=', $dateTo)
                ->sum('net_amount');

            $receivedTotal = (float) SalePayment::query()
                ->whereDate('paid_at', '>=', $dateFrom)
                ->whereDate('paid_at', '<=', $dateTo)
                ->whereHas('sale', fn ($query) => $query->where('type', SaleType::SALE))
                ->sum('bill_amount');

            $salesDue = (float) Sale::query()
                ->where('type', SaleType::SALE)
                ->whereDate('sold_at', '>=', $dateFrom)
                ->whereDate('sold_at', '<=', $dateTo)
                ->get(['net_amount', 'paid_amount'])
                ->sum(fn (Sale $sale): float => max(
                    0,
                    round((float) $sale->net_amount - (float) $sale->paid_amount, 2),
                ));
        }

        $expensesPaid = 0.0;
        $expensesPending = 0.0;

        if ($canViewExpenses) {
            $expensesPaid = self::sumExpensesForRange($dateFrom, $dateTo, ExpenseStatus::PAID);
            $expensesPending = self::sumExpensesForRange($dateFrom, $dateTo, ExpenseStatus::PENDING);
        }

        $expensesTotal = round($expensesPaid + $expensesPending, 2);
        $netCash = round($receivedTotal - $expensesPaid, 2);
        $finalToReceive = round($netCash + $expensesPending, 2);

        return [
            'date_from' => $dateFrom,
            'date_to' => $dateTo,
            'quick_range' => $quickRange,
            'is_single_day' => $isSingleDay,
            'can_view_sales' => $canViewSales,
            'can_view_expenses' => $canViewExpenses,
            'sales_total_afn' => $canViewSales ? ExpenseReport::formatAmount($salesTotal) : null,
            'received_total_afn' => $canViewSales ? ExpenseReport::formatAmount($receivedTotal) : null,
            'sales_due_afn' => $canViewSales ? ExpenseReport::formatAmount($salesDue) : null,
            'expenses_total_afn' => $canViewExpenses ? ExpenseReport::formatAmount($expensesTotal) : null,
            'expenses_paid_afn' => $canViewExpenses ? ExpenseReport::formatAmount($expensesPaid) : null,
            'expenses_pending_afn' => $canViewExpenses ? ExpenseReport::formatAmount($expensesPending) : null,
            'net_cash_afn' => self::formatNetCash($canViewSales, $canViewExpenses, $netCash, $receivedTotal),
            'final_to_receive_afn' => self::formatFinalToReceive(
                $canViewSales,
                $canViewExpenses,
                $finalToReceive,
                $receivedTotal,
                $expensesPending,
            ),
        ];
    }

    private static function formatNetCash(
        bool $canViewSales,
        bool $canViewExpenses,
        float $netCash,
        float $receivedTotal,
    ): ?string {
        if ($canViewSales && $canViewExpenses) {
            return ExpenseReport::formatAmount($netCash);
        }

        if ($canViewSales) {
            return ExpenseReport::formatAmount($receivedTotal);
        }

        return null;
    }

    private static function formatFinalToReceive(
        bool $canViewSales,
        bool $canViewExpenses,
        float $finalToReceive,
        float $receivedTotal,
        float $expensesPending,
    ): ?string {
        if ($canViewSales && $canViewExpenses) {
            return ExpenseReport::formatAmount($finalToReceive);
        }

        if ($canViewSales) {
            return ExpenseReport::formatAmount($receivedTotal);
        }

        if ($canViewExpenses) {
            return ExpenseReport::formatAmount($expensesPending);
        }

        return null;
    }

    private static function sumExpensesForRange(string $dateFrom, string $dateTo, string $status): float
    {
        $query = Expense::query()
            ->whereDate('expense_date', '>=', $dateFrom)
            ->whereDate('expense_date', '<=', $dateTo)
            ->where('status', $status);

        BranchScope::apply($query);

        return (float) $query->sum('amount_original');
    }
}
