<?php

namespace App\Filament\Pages;

use App\Enums\Currency;
use App\Enums\PaymentStatus;
use App\Models\Payment;
use App\Models\Sale;
use App\Models\SystemSetting;
use Carbon\Carbon;
use Filament\Pages\Page;
use Livewire\Attributes\Url;

class CxCReportPage extends Page
{
    protected static ?string $navigationIcon  = 'heroicon-o-banknotes';
    protected static ?string $navigationGroup = 'Reportes';
    protected static ?string $navigationLabel = 'Cuentas por Cobrar';
    protected static ?int    $navigationSort  = 11;
    protected static string  $view = 'filament.pages.cxc-report-page';

    #[Url(as: 'preset')]
    public string $preset = 'month';

    #[Url(as: 'desde')]
    public string $desde = '';

    #[Url(as: 'hasta')]
    public string $hasta = '';

    public function mount(): void
    {
        if (! $this->desde || ! $this->hasta) {
            [$this->desde, $this->hasta] = SalesReportPage::presetDates($this->preset);
        }
    }

    public function setPreset(string $preset): void
    {
        $this->preset = $preset;
        [$this->desde, $this->hasta] = SalesReportPage::presetDates($preset);
    }

    // ── Datos del reporte ────────────────────────────────────────────────────

    public function getReportData(): array
    {
        $rate  = (float) SystemSetting::current()->exchange_rate_usd_bob;
        $desde = $this->desde ?: now()->startOfMonth()->toDateString();
        $hasta = $this->hasta ?: now()->toDateString();

        // ── Todas las CxC pendientes/parciales (sin filtro de fecha) ──────────
        $pending = Sale::with('client')
            ->whereIn('payment_status', [PaymentStatus::Unpaid->value, PaymentStatus::Partial->value])
            ->orderBy('expected_collect_date')
            ->get();

        // ── Cobradas en el período (payment_date en el rango) ─────────────────
        $paidInPeriod = Sale::with('client')
            ->where('payment_status', PaymentStatus::Paid->value)
            ->whereHas('payments', fn ($q) => $q->whereBetween('payment_date', [$desde, $hasta]))
            ->orderBy('sale_date', 'desc')
            ->get();

        // ── KPIs pendientes ────────────────────────────────────────────────────
        $totalPendingBs = $pending->sum(function (Sale $s) use ($rate) {
            return $s->currency === Currency::BOB
                ? $s->balance
                : round($s->balance * $rate, 2);
        });

        $overdue = $pending->filter(
            fn ($s) => $s->expected_collect_date && $s->expected_collect_date->isPast()
        );
        $totalOverdueBs = $overdue->sum(function (Sale $s) use ($rate) {
            return $s->currency === Currency::BOB
                ? $s->balance
                : round($s->balance * $rate, 2);
        });

        // ── KPI cobrado en período ─────────────────────────────────────────────
        $paymentsInPeriod = Payment::with('sale.client')
            ->whereBetween('payment_date', [$desde, $hasta])
            ->get();

        $collectedInPeriodBs = $paymentsInPeriod->sum(function (Payment $p) use ($rate) {
            $currency = $p->currency ?? ($p->sale?->currency ?? Currency::USD);
            return $currency === Currency::BOB
                ? (float) $p->net_amount
                : round((float) $p->net_amount * $rate, 2);
        });

        // ── Aging buckets (días vencido desde expected_collect_date) ──────────
        $aging = $this->buildAging($pending, $rate);

        // ── Promedio días cobro (ventas pagadas con ambas fechas) ──────────────
        $avgDays = $this->avgDaysToCollect();

        return [
            'pending'              => $pending,
            'paidInPeriod'         => $paidInPeriod,
            'aging'                => $aging,
            'totalPendingBs'       => $totalPendingBs,
            'totalOverdueBs'       => $totalOverdueBs,
            'collectedInPeriodBs'  => $collectedInPeriodBs,
            'avgDays'              => $avgDays,
            'rate'                 => $rate,
            'desde'                => $desde,
            'hasta'                => $hasta,
            'paymentsInPeriod'     => $paymentsInPeriod,
        ];
    }

    private function buildAging($pending, float $rate): array
    {
        $buckets = [
            'current'  => ['label' => 'Al día',    'color' => 'success', 'days' => '0',    'count' => 0, 'totalBs' => 0],
            'late30'   => ['label' => '1–30 días',  'color' => 'warning', 'days' => '1-30', 'count' => 0, 'totalBs' => 0],
            'late60'   => ['label' => '31–60 días', 'color' => 'danger',  'days' => '31-60','count' => 0, 'totalBs' => 0],
            'late60p'  => ['label' => '60+ días',   'color' => 'gray',    'days' => '60+',  'count' => 0, 'totalBs' => 0],
            'nodate'   => ['label' => 'Sin F.Cobro','color' => 'gray',    'days' => '—',    'count' => 0, 'totalBs' => 0],
        ];

        foreach ($pending as $sale) {
            $bs = $sale->currency === Currency::BOB
                ? $sale->balance
                : round($sale->balance * $rate, 2);

            if (! $sale->expected_collect_date) {
                $key = 'nodate';
            } else {
                $days = $sale->expected_collect_date->isPast()
                    ? (int) now()->diffInDays($sale->expected_collect_date)
                    : 0;

                $key = match (true) {
                    $days === 0  => 'current',
                    $days <= 30  => 'late30',
                    $days <= 60  => 'late60',
                    default      => 'late60p',
                };
            }

            $buckets[$key]['count']++;
            $buckets[$key]['totalBs'] += $bs;
        }

        return $buckets;
    }

    private function avgDaysToCollect(): ?float
    {
        $paid = Sale::whereNotNull('sale_date')
            ->whereNotNull('expected_collect_date')
            ->where('payment_status', PaymentStatus::Paid->value)
            ->get();

        if ($paid->isEmpty()) return null;

        $totalDays = $paid->sum(
            fn ($s) => max(0, (int) $s->sale_date->diffInDays($s->expected_collect_date))
        );

        return round($totalDays / $paid->count(), 1);
    }

    public function getExportUrl(): string
    {
        return route('export.cxc', [
            'date_from' => $this->desde,
            'date_to'   => $this->hasta,
        ]);
    }

    public function getTitle(): string
    {
        return 'Cuentas por Cobrar';
    }
}
