<?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\Services\CollectorVisibilityResolver;
use App\Services\Metrics\DelinquencyMetricsService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class DelinquencyController extends Controller
{
    use RoleAwareQueries;

    public function __construct(
        private readonly DelinquencyMetricsService $delinquencyService,
        private readonly CollectorVisibilityResolver $visibility,
    ) {}

    /**
     * Traduce la visibilidad del usuario al filtro de cobradores que esperan
     * los métodos de DelinquencyMetricsService:
     *   - mode 'all'        → null  (company-wide).
     *   - mode 'assigned'   → IDs asignados (no vacío).
     *   - mode 'unassigned' → []    (el servicio devuelve ceros/vacío).
     *
     * @return array{0: int[]|null, 1: bool} [collectorIds, isUnassigned]
     */
    private function resolveScope(Request $request): array
    {
        $visibility = $this->visibility->resolve($request->user());

        return match ($visibility['mode']) {
            CollectorVisibilityResolver::MODE_ALL => [null, false],
            CollectorVisibilityResolver::MODE_ASSIGNED => [$visibility['collector_ids'], false],
            default => [[], true], // unassigned
        };
    }

    /** Claves de bucket válidas para el aging drill-down. */
    private const VALID_AGING_BUCKETS = ['1_7', '8_15', '16_30', '31_60', '61_90', '90_plus'];

    /** Labels de cada bucket (se exponen al cliente para el header de la vista). */
    private const AGING_BUCKET_LABELS = [
        '1_7' => '1-7 días',
        '8_15' => '8-15 días',
        '16_30' => '16-30 días',
        '31_60' => '31-60 días',
        '61_90' => '61-90 días',
        '90_plus' => '+90 días',
    ];

    /**
     * GET /api/pwa/delinquency
     *
     * Reporte de morosidad del tenant: PAR metrics, aging buckets y top créditos vencidos.
     * Accesible solo para supervisor y admin.
     */
    public function index(Request $request): JsonResponse
    {
        if (! $this->canViewAllCredits($request)) {
            return response()->json(['message' => 'No autorizado.'], 403);
        }

        $companyId = $request->user()->company_id;
        [$collectorIds, $unassigned] = $this->resolveScope($request);

        $summary = $this->delinquencyService->getDelinquencySummary($companyId, $collectorIds);
        $aging = $this->delinquencyService->getAgingReport($companyId, $collectorIds);
        $topOverdue = $this->delinquencyService->getTopOverdueCredits($companyId, 10, $collectorIds);
        $parTrend = $this->delinquencyService->getPARTrend($companyId, 30, $collectorIds);

        // Filtrar bucket "current" del aging para la tabla de mora (solo los vencidos)
        $agingBuckets = collect($aging)
            ->filter(fn ($_, $key) => $key !== 'current')
            ->map(fn ($bucket, $key) => [
                'key' => $key,
                'label' => $bucket['label'],
                'credits_count' => $bucket['credits_count'],
                'overdue_balance' => (float) $bucket['overdue_balance'],
                'percentage' => (float) $bucket['percentage'],
            ])
            ->values();

        return response()->json([
            'unassigned' => $unassigned,
            'summary' => [
                'par1' => (float) ($summary['par1'] ?? 0),
                'par7' => (float) ($summary['par7'] ?? 0),
                'par30' => (float) ($summary['par30'] ?? 0),
                'par60' => (float) ($summary['par60'] ?? 0),
                'par90' => (float) ($summary['par90'] ?? 0),
                'total_overdue_balance' => (float) ($summary['total_overdue_balance'] ?? 0),
                'total_portfolio' => (float) ($summary['total_portfolio'] ?? 0),
                'credits_at_risk_severe' => (int) ($summary['credits_at_risk_severe'] ?? 0),
                'health_indicator' => $summary['health_indicator'] ?? 'fair',
            ],
            'aging' => $agingBuckets,
            'par_trend' => [
                'is_partial_data' => $parTrend['is_partial_data'],
                'snapshots_count' => $parTrend['snapshots_count'],
                'data' => $parTrend['data'],
            ],
            'top_overdue' => $topOverdue->map(fn ($credit) => [
                'credit_id' => $credit['credit_id'],
                'client_name' => $credit['client_name'],
                'client_id' => $credit['client_id'],
                'collector_name' => $credit['collector_name'],
                'periodicity' => $credit['periodicity'],
                'total_balance' => (float) $credit['total_balance'],
                'overdue_balance' => (float) $credit['overdue_balance'],
                'max_days_overdue' => (int) $credit['max_days_overdue'],
                'overdue_installments_count' => (int) $credit['overdue_installments_count'],
                'last_payment_date' => $credit['last_payment_date'],
                'status' => $credit['status'],
            ])->values(),
        ]);
    }

    /**
     * GET /api/pwa/delinquency/aging/{bucket}
     *
     * Listado de créditos en mora para un bucket de antigüedad específico.
     * Permite el drill-down desde la tabla de antigüedad en la vista de morosidad.
     */
    public function agingBucket(Request $request, string $bucket): JsonResponse
    {
        if (! $this->canViewAllCredits($request)) {
            return response()->json(['message' => 'No autorizado.'], 403);
        }

        if (! in_array($bucket, self::VALID_AGING_BUCKETS, true)) {
            return response()->json(['message' => 'Rango de antigüedad no válido.'], 422);
        }

        $companyId = $request->user()->company_id;
        [$collectorIds] = $this->resolveScope($request);
        $credits = $this->delinquencyService->getAgingBucketCredits($companyId, $bucket, $collectorIds);

        return response()->json([
            'bucket' => $bucket,
            'label' => self::AGING_BUCKET_LABELS[$bucket],
            'credits' => $credits->map(fn (array $credit) => [
                'credit_id' => $credit['credit_id'],
                'client_name' => $credit['client_name'],
                'client_id' => $credit['client_id'],
                'collector_name' => $credit['collector_name'],
                'periodicity' => $credit['periodicity'],
                'total_balance' => (float) $credit['total_balance'],
                'overdue_balance' => (float) $credit['overdue_balance'],
                'max_days_overdue' => (int) $credit['max_days_overdue'],
                'overdue_installments_count' => (int) $credit['overdue_installments_count'],
                'last_payment_date' => $credit['last_payment_date'],
                'status' => $credit['status'],
            ])->values(),
        ]);
    }
}
