<?php

declare(strict_types=1);

namespace App\Services;

use App\Events\CreditStatusSynced;
use App\Models\Credit;
use App\Models\CreditAuditLog;

/**
 * Sincroniza el estado de un crédito con el resultado calculado por SmartCreditStateResolver.
 *
 * Responsabilidad: estado del crédito (leer → resolver → persistir → auditar → notificar).
 * La actualización del orden de cobro fue extraída a CollectorSyncService,
 * que escucha el evento CreditStatusSynced disparado por este servicio.
 *
 * Deduplicación:
 *   sync() es idempotente por request/job: ignora llamadas repetidas para el mismo ID.
 *   forceSync() omite la deduplicación (usado por Jobs y el comando de reconciliación).
 */
class CreditStatusSyncService
{
    /**
     * IDs ya procesados en este request/job (deduplicación en memoria).
     *
     * @var array<int, true>
     */
    private array $processedIds = [];

    /**
     * Sincroniza el estado del crédito una sola vez por request/job.
     * Llamadas subsiguientes para el mismo ID son no-op.
     */
    public function sync(Credit $credit): void
    {
        if (isset($this->processedIds[$credit->id])) {
            return;
        }

        $this->processedIds[$credit->id] = true;

        $this->perform($credit);
    }

    /**
     * Sincroniza el estado sin deduplicar.
     * Usar en Jobs asíncronos y en el comando de reconciliación nocturna.
     */
    public function forceSync(Credit $credit): void
    {
        $this->perform($credit);
    }

    private function perform(Credit $credit): void
    {
        // Fresh read con installments + children para evitar queries adicionales
        // en isParent() (relation-aware) y en SmartCreditStateResolver.
        $credit = $credit->fresh(['installments', 'children']);

        if (! $credit) {
            return;
        }

        if (! $credit->canAutoUpdateStatus()) {
            return;
        }

        $resolver = app(SmartCreditStateResolver::class);
        $newStatus = $resolver->resolve($credit);

        if ($newStatus === $credit->status) {
            return;
        }

        $oldStatus = $credit->status;
        $credit->status = $newStatus;
        $credit->saveQuietly();

        // Evento interno: CollectorSyncListener actualizará el orden de cobro
        CreditStatusSynced::dispatch($credit, $oldStatus, $newStatus);

        $this->audit($credit, $oldStatus, $newStatus);
    }

    private function audit(Credit $credit, string $oldStatus, string $newStatus): void
    {
        CreditAuditLog::create([
            'credit_id' => $credit->id,
            'company_id' => $credit->company_id,
            'user_id' => null, // Evento de sistema, sin usuario
            'action' => 'status_auto_synced',
            'data' => [
                'old_status' => $oldStatus,
                'new_status' => $newStatus,
            ],
        ]);
    }
}
