<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\Pwa;

use App\Http\Controllers\Api\Pwa\Traits\RoleAwareQueries;
use App\Http\Controllers\Controller;
use App\Models\Credit;
use App\Models\Installment;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class CollectionController extends Controller
{
    use RoleAwareQueries;

    /**
     * GET /api/pwa/collections
     *
     * Lista cuotas a cobrar del collector autenticado.
     * Filtros: today, overdue, all, upcoming
     */
    public function index(Request $request): JsonResponse
    {
        $user = $request->user();
        $filter = $request->get('filter', 'today');
        $today = Carbon::today()->toDateString();
        // Visibilidad supervisor→cobrador (#71): null = sin filtro (admin / sees_all);
        // []/array = scoped. Valida además el filtro puntual ?collector_id contra
        // lo que el usuario realmente puede ver (no puede espiar a otro equipo).
        $scopeIds = $this->visibleCollectorScope($request);

        $query = Installment::query()
            ->with([
                'credit:id,client_id,status,amount,periodicity,due_day_1,due_day_2,installments_count',
                'credit.client:id,name,phone,identification',
                'credit.client.addresses',
                'credit.installments:id,credit_id,status,total_amount,amount_paid',
                'credit.latestPayment',
            ])
            ->whereHas('credit', function ($q) use ($user, $scopeIds) {
                $q->where('company_id', $user->company_id)
                    ->whereIn('status', Credit::ACTIVE_STATUSES);

                if ($scopeIds !== null) {
                    $q->whereIn('collector_user_id', $scopeIds);
                }
            })
            ->whereIn('status', [
                Installment::STATUS_PENDING,
                Installment::STATUS_PARTIAL_PAID,
                Installment::STATUS_OVERDUE,
            ]);

        // Aging range filter (days overdue)
        $agingMin = $request->integer('aging_min');
        $agingMax = $request->integer('aging_max');

        // Aplicar filtro
        switch ($filter) {
            case 'today':
                $query->whereDate('due_date', $today);
                break;

            case 'overdue':
                $query->where('due_date', '<', $today);

                // Apply aging bucket filter if provided
                if ($agingMin > 0) {
                    $query->where('due_date', '<=', now()->subDays($agingMin)->toDateString());
                }
                if ($agingMax > 0) {
                    $query->where('due_date', '>=', now()->subDays($agingMax)->toDateString());
                }
                break;

            case 'upcoming':
                // Próximos 7 días (sin incluir hoy)
                $query->where('due_date', '>', $today)
                    ->where('due_date', '<=', now()->addDays(7)->toDateString());
                break;

            case 'all':
                // Todas las pendientes (vencidas primero, luego por fecha)
                break;
        }

        // Orden de ruta manual: si el scope se reduce a un único cobrador (el propio
        // collector, o un supervisor/admin filtrando por uno válido) se usa su orden.
        $orderUserId = is_array($scopeIds) && count($scopeIds) === 1 ? $scopeIds[0] : null;

        if ($orderUserId) {
            // JOIN con la tabla de orden manual del cobrador.
            // LEFT JOIN garantiza que créditos sin fila en collector_credit_order
            // aparezcan al final (sort_order IS NULL → ordenados por fecha como fallback).
            $query->leftJoin('collector_credit_order as cco', function ($join) use ($user, $orderUserId) {
                $join->on('installments.credit_id', '=', 'cco.credit_id')
                    ->where('cco.user_id', '=', $orderUserId)
                    ->where('cco.company_id', '=', $user->company_id);
            })
                ->select('installments.*', 'cco.sort_order as route_sort_order')
                ->orderByRaw('cco.sort_order IS NULL')  // nulls al final
                ->orderBy('cco.sort_order')
                ->orderBy('installments.due_date')
                ->orderBy('installments.installment_number');
        } else {
            // Supervisor/admin sin filtro de cobrador específico: orden por fecha
            $query->orderByRaw('
                CASE
                    WHEN due_date < ? THEN 0
                    WHEN due_date = ? THEN 1
                    ELSE 2
                END
            ', [$today, $today])
                ->orderBy('due_date')
                ->orderBy('installment_number');
        }

        // Sin tope: offline-first — el cobrador necesita todas las cuotas por cobrar
        // sincronizadas localmente. El `->limit(100)` fijo truncaba en silencio (mismo
        // defecto que /credits), ocultando cobros pendientes cuando la ruta supera 100.
        $installments = $query->get();

        $data = $installments->map(function ($installment) use ($today) {
            $dueDate = $installment->due_date->toDateString();
            $daysOverdue = $dueDate < $today
                ? now()->diffInDays($installment->due_date)
                : 0;

            // Calcular saldo pendiente del crédito
            $creditTotal = $installment->credit->installments->sum('total_amount');
            $creditPaid = $installment->credit->installments->sum('amount_paid');
            $creditRemainingBalance = round((float) ($creditTotal - $creditPaid), 2);

            return [
                'installment_id' => $installment->id,
                'credit_id' => $installment->credit_id,
                'client' => [
                    'id' => $installment->credit->client->id,
                    'name' => $installment->credit->client->name,
                    'phone' => $installment->credit->client->phone,
                    'identification' => $installment->credit->client->identification,
                    'address' => $installment->credit->client->formatted_address,
                ],
                'installment_number' => $installment->installment_number,
                'total_installments' => (int) ($installment->credit->installments_count ?? 0),
                'installments_paid' => $installment->credit->installments
                    ->where('status', 'paid')->count(),
                'installments_pending' => $installment->credit->installments
                    ->where('status', '!=', 'paid')->count(),
                'due_date' => $dueDate,
                'days_overdue' => $daysOverdue,
                'status' => $installment->status,
                'status_label' => $installment->getStatusLabel(),
                'total_amount' => round((float) $installment->total_amount, 2),
                'amount_paid' => round((float) $installment->amount_paid, 2),
                'remaining' => round((float) $installment->remaining_amount, 2),
                'periodicity' => $installment->credit->periodicity,
                'due_day_1' => $installment->credit->due_day_1,
                'due_day_2' => $installment->credit->due_day_2,
                'credit_status' => $installment->credit->status,
                'credit_remaining_balance' => $creditRemainingBalance,
                'last_payment_date' => $installment->credit->latestPayment?->payment_date?->toDateString(),
                'last_payment_amount' => $installment->credit->latestPayment
                    ? round((float) $installment->credit->latestPayment->amount, 2)
                    : null,
                // Orden manual del cobrador (null si no tiene fila en collector_credit_order)
                'sort_order' => isset($installment->route_sort_order)
                    ? (int) $installment->route_sort_order
                    : null,
            ];
        });

        // Estadísticas del filtro actual
        $totalAmount = $data->sum('remaining');
        $totalCount = $data->count();

        return response()->json([
            'data' => $data->values(),
            'meta' => [
                'total' => $totalCount,
                'total_amount' => round($totalAmount, 2),
                'filter' => $filter,
                'date' => $today,
            ],
        ]);
    }

    /**
     * GET /api/pwa/collections/stats
     *
     * Estadísticas rápidas por filtro para tabs.
     */
    public function stats(Request $request): JsonResponse
    {
        $user = $request->user();
        $today = Carbon::today()->toDateString();

        // Visibilidad supervisor→cobrador (#71), con validación del ?collector_id.
        $scopeIds = $this->visibleCollectorScope($request);

        $baseQuery = fn () => Installment::query()
            ->whereHas('credit', function ($q) use ($user, $scopeIds) {
                $q->where('company_id', $user->company_id)
                    ->whereIn('status', Credit::ACTIVE_STATUSES);

                if ($scopeIds !== null) {
                    $q->whereIn('collector_user_id', $scopeIds);
                }
            })
            ->whereIn('status', [
                Installment::STATUS_PENDING,
                Installment::STATUS_PARTIAL_PAID,
                Installment::STATUS_OVERDUE,
            ]);

        // Hoy
        $todayStats = (clone $baseQuery())
            ->whereDate('due_date', $today)
            ->selectRaw('COUNT(*) as count, COALESCE(SUM(total_amount - amount_paid), 0) as amount')
            ->first();

        // Vencidas
        $overdueStats = (clone $baseQuery())
            ->where('due_date', '<', $today)
            ->selectRaw('COUNT(*) as count, COALESCE(SUM(total_amount - amount_paid), 0) as amount')
            ->first();

        // Próximas (7 días)
        $upcomingStats = (clone $baseQuery())
            ->where('due_date', '>', $today)
            ->where('due_date', '<=', now()->addDays(7)->toDateString())
            ->selectRaw('COUNT(*) as count, COALESCE(SUM(total_amount - amount_paid), 0) as amount')
            ->first();

        // Todas las pendientes (sin filtro de fecha)
        $allStats = (clone $baseQuery())
            ->selectRaw('COUNT(*) as count, COALESCE(SUM(total_amount - amount_paid), 0) as amount')
            ->first();

        return response()->json([
            'today' => [
                'count' => (int) $todayStats->count,
                'amount' => round((float) $todayStats->amount, 2),
            ],
            'overdue' => [
                'count' => (int) $overdueStats->count,
                'amount' => round((float) $overdueStats->amount, 2),
            ],
            'upcoming' => [
                'count' => (int) $upcomingStats->count,
                'amount' => round((float) $upcomingStats->amount, 2),
            ],
            'all' => [
                'count' => (int) $allStats->count,
                'amount' => round((float) $allStats->amount, 2),
            ],
        ]);
    }
}
