<?php

namespace App\Livewire\Financial;

use App\Domain\Facility\Models\Facility;
use App\Domain\Financial\Enums\AccountType;
use App\Domain\Financial\Enums\ExpenseCategory;
use App\Domain\Financial\Models\Expense;
use App\Domain\Financial\Models\FinancialAccount;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\Payment;
use App\Domain\Financial\Models\Transaction;
use App\Domain\Identity\Models\Branch;
use App\Domain\Inventory\Models\PurchaseOrder;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('النظرة المالية العامة')]
class FinancialOverview extends Component
{
    use UsesBranchScope;

    #[Url]
    public string $period = 'this_month';

    #[Url]
    public string $branch_id = '';

    public function mount()
    {
        $this->authorize('invoices.list');
    }

    public function render()
    {
        if (!$this->branch_id) {
            $activeBranch = $this->getActiveBranchId();
            if ($activeBranch) {
                $this->branch_id = (string) $activeBranch;
            }
        }

        [$from, $to] = $this->resolvePeriod();

        $branches = Branch::orderBy('name_ar')->get();

        $facilityCosts = $this->getFacilityCosts();
        $revenue = $this->getRevenue($from, $to);
        $expenses = $this->getExpenses($from, $to);
        $monthlyPL = $this->getMonthlyPL();
        $breakEven = $this->getBreakEvenData($facilityCosts, $revenue);
        $revenueBySource = $this->getRevenueBySource($from, $to);
        $collectionRate = $this->getCollectionRate($from, $to);

        return view('livewire.financial.financial-overview', [
            'branches' => $branches,
            'facilityCosts' => $facilityCosts,
            'revenue' => $revenue,
            'expenses' => $expenses,
            'monthlyPL' => $monthlyPL,
            'breakEven' => $breakEven,
            'revenueBySource' => $revenueBySource,
            'collectionRate' => $collectionRate,
            'periodLabel' => $this->getPeriodLabel(),
            'from' => $from,
            'to' => $to,
        ]);
    }

    private function resolvePeriod(): array
    {
        return match ($this->period) {
            'today' => [now()->startOfDay(), now()->endOfDay()],
            'this_week' => [now()->startOfWeek(), now()->endOfWeek()],
            'this_month' => [now()->startOfMonth(), now()->endOfMonth()],
            'last_month' => [now()->subMonth()->startOfMonth(), now()->subMonth()->endOfMonth()],
            'this_quarter' => [now()->firstOfQuarter(), now()->lastOfQuarter()->endOfDay()],
            'this_year' => [now()->startOfYear(), now()->endOfYear()],
            default => [now()->startOfMonth(), now()->endOfMonth()],
        };
    }

    private function getPeriodLabel(): string
    {
        return match ($this->period) {
            'today' => 'اليوم',
            'this_week' => 'هذا الأسبوع',
            'this_month' => 'هذا الشهر',
            'last_month' => 'الشهر الماضي',
            'this_quarter' => 'هذا الربع',
            'this_year' => 'هذا العام',
            default => 'هذا الشهر',
        };
    }

    private function getFacilityCosts(): array
    {
        $query = Facility::query()->whereNotNull('monthly_rental_cost')->where('monthly_rental_cost', '>', 0);

        if ($this->branch_id) {
            $query->where('branch_id', $this->branch_id);
        }

        $facilities = $query->with('branch')->get();

        $totalMonthly = $facilities->sum('monthly_rental_cost');

        return [
            'facilities' => $facilities->map(fn ($f) => [
                'name' => $f->name_ar,
                'branch' => $f->branch?->name_ar ?? '-',
                'monthly_cost' => $f->monthly_rental_cost,
                'hourly_cost' => $f->rental_cost_per_hour,
                'status' => $f->status->value,
            ])->toArray(),
            'total_monthly' => $totalMonthly,
            'total_yearly' => $totalMonthly * 12,
        ];
    }

    private function getRevenue($from, $to): array
    {
        $query = Payment::where('direction', 'inbound')
            ->where('status', 'confirmed')
            ->whereBetween('payment_date', [$from, $to]);

        if ($this->branch_id) {
            $query->where('branch_id', $this->branch_id);
        }

        $totalRevenue = $query->sum('amount');

        $byMethodQuery = Payment::where('direction', 'inbound')
            ->where('status', 'confirmed')
            ->whereBetween('payment_date', [$from, $to]);

        if ($this->branch_id) {
            $byMethodQuery->where('branch_id', $this->branch_id);
        }

        $byMethod = $byMethodQuery->select('method', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count'))
            ->groupBy('method')
            ->get();

        return [
            'total' => $totalRevenue,
            'by_method' => $byMethod->map(fn ($p) => [
                'method' => $p->method?->value ?? $p->method,
                'total' => $p->total,
                'count' => $p->count,
            ])->toArray(),
            'transaction_count' => $query->count(),
        ];
    }

    private function getExpenses($from, $to): array
    {
        $outboundPayments = Payment::where('direction', 'outbound')
            ->where('status', 'confirmed')
            ->whereBetween('payment_date', [$from, $to])
            ->sum('amount');

        $purchaseOrders = PurchaseOrder::whereIn('status', ['confirmed', 'received', 'partially_received'])
            ->whereBetween('created_at', [$from, $to])
            ->sum('total_amount');

        $facilityCostsThisPeriod = $this->calculateProRatedFacilityCost($from, $to);

        // Direct expenses from the expenses table
        $directExpensesQuery = Expense::whereBetween('expense_date', [$from, $to]);
        if ($this->branch_id) {
            $directExpensesQuery->where('branch_id', $this->branch_id);
        }
        $directExpensesTotal = (clone $directExpensesQuery)->sum('amount');

        // Breakdown by category
        $byCategory = (clone $directExpensesQuery)
            ->select('category', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count'))
            ->groupBy('category')
            ->orderByDesc('total')
            ->get()
            ->map(fn ($e) => [
                'category' => $e->category,
                'label' => $e->category instanceof ExpenseCategory ? $e->category->label() : $e->category,
                'total' => (int) $e->total,
                'count' => (int) $e->count,
            ])->toArray();

        // Recent expenses
        $recentExpenses = Expense::whereBetween('expense_date', [$from, $to])
            ->when($this->branch_id, fn ($q) => $q->where('branch_id', $this->branch_id))
            ->with('creator')
            ->orderByDesc('expense_date')
            ->limit(10)
            ->get();

        return [
            'outbound_payments' => $outboundPayments,
            'purchase_orders' => $purchaseOrders,
            'facility_costs' => $facilityCostsThisPeriod,
            'direct_expenses' => $directExpensesTotal,
            'by_category' => $byCategory,
            'recent_expenses' => $recentExpenses,
            'total' => $outboundPayments + $purchaseOrders + $facilityCostsThisPeriod + $directExpensesTotal,
        ];
    }

    private function calculateProRatedFacilityCost($from, $to): int
    {
        $query = Facility::whereNotNull('monthly_rental_cost')->where('monthly_rental_cost', '>', 0);
        if ($this->branch_id) {
            $query->where('branch_id', $this->branch_id);
        }

        $totalMonthly = $query->sum('monthly_rental_cost');
        $days = max(1, now()->parse($from)->diffInDays(now()->parse($to)));
        $daysInMonth = now()->daysInMonth;

        return (int) round($totalMonthly * ($days / $daysInMonth));
    }

    private function getMonthlyPL(): array
    {
        $months = [];
        for ($i = 5; $i >= 0; $i--) {
            $month = now()->subMonths($i);
            $start = $month->copy()->startOfMonth();
            $end = $month->copy()->endOfMonth();

            $income = Payment::where('direction', 'inbound')
                ->where('status', 'confirmed')
                ->whereBetween('payment_date', [$start, $end])
                ->sum('amount');

            $outbound = Payment::where('direction', 'outbound')
                ->where('status', 'confirmed')
                ->whereBetween('payment_date', [$start, $end])
                ->sum('amount');

            $purchases = PurchaseOrder::whereIn('status', ['confirmed', 'received', 'partially_received'])
                ->whereBetween('created_at', [$start, $end])
                ->sum('total_amount');

            $facilityQuery = Facility::whereNotNull('monthly_rental_cost')->where('monthly_rental_cost', '>', 0);
            if ($this->branch_id) {
                $facilityQuery->where('branch_id', $this->branch_id);
            }
            $facilityCost = $facilityQuery->sum('monthly_rental_cost');

            $directExpenses = Expense::whereBetween('expense_date', [$start, $end])
                ->when($this->branch_id, fn ($q) => $q->where('branch_id', $this->branch_id))
                ->sum('amount');

            $totalExpenses = $outbound + $purchases + $facilityCost + $directExpenses;

            $months[] = [
                'label' => $month->translatedFormat('M Y'),
                'month_key' => $month->format('Y-m'),
                'income' => $income,
                'expenses' => $totalExpenses,
                'profit' => $income - $totalExpenses,
            ];
        }

        return $months;
    }

    private function getBreakEvenData(array $facilityCosts, array $revenue): array
    {
        $monthlyFixedCosts = $facilityCosts['total_monthly'];

        if ($monthlyFixedCosts <= 0) {
            return [
                'has_data' => false,
                'message' => 'لم يتم تحديد تكاليف المنشآت الشهرية بعد',
            ];
        }

        $currentMonthRevenue = $revenue['total'];
        $percentage = min(100, round(($currentMonthRevenue / $monthlyFixedCosts) * 100));
        $remaining = max(0, $monthlyFixedCosts - $currentMonthRevenue);

        return [
            'has_data' => true,
            'fixed_costs' => $monthlyFixedCosts,
            'current_revenue' => $currentMonthRevenue,
            'percentage' => $percentage,
            'remaining' => $remaining,
            'is_profitable' => $currentMonthRevenue >= $monthlyFixedCosts,
        ];
    }

    private function getRevenueBySource($from, $to): array
    {
        $revenueAccounts = FinancialAccount::where('type', AccountType::Revenue)->pluck('id', 'name_ar');

        $bySource = Transaction::whereBetween('transaction_date', [$from, $to])
            ->whereIn('credit_account_id', $revenueAccounts->values())
            ->select('credit_account_id', DB::raw('SUM(amount) as total'))
            ->groupBy('credit_account_id')
            ->get();

        $accountNames = FinancialAccount::whereIn('id', $bySource->pluck('credit_account_id'))->pluck('name_ar', 'id');

        return $bySource->map(fn ($t) => [
            'source' => $accountNames[$t->credit_account_id] ?? 'أخرى',
            'total' => $t->total,
        ])->sortByDesc('total')->values()->toArray();
    }

    private function getCollectionRate($from, $to): array
    {
        $invoicedQuery = Invoice::whereBetween('issue_date', [$from, $to]);
        $overdueQuery = Invoice::where('status', 'overdue');

        if ($this->branch_id) {
            $invoicedQuery->whereHas('payments', fn ($q) => $q->where('branch_id', $this->branch_id));
            $overdueQuery->whereHas('payments', fn ($q) => $q->where('branch_id', $this->branch_id));
        }

        $invoiced = (clone $invoicedQuery)->sum('total_amount');
        $collected = (clone $invoicedQuery)->sum('paid_amount');

        $overdue = (clone $overdueQuery)->sum('due_amount');
        $overdueCount = (clone $overdueQuery)->count();

        return [
            'invoiced' => $invoiced,
            'collected' => $collected,
            'rate' => $invoiced > 0 ? round(($collected / $invoiced) * 100) : 0,
            'overdue_amount' => $overdue,
            'overdue_count' => $overdueCount,
        ];
    }
}
