<?php

namespace App\Policies;

use App\Models\Credit;
use App\Models\User;

class CreditPolicy
{
    /**
     * Verifica pertenencia multi-tenant.
     */
    protected function inSameCompany(User $user, Credit $credit): bool
    {
        return $user->company_id === $credit->company_id || $user->hasRole('super_admin');
    }

    /**
     * Un crédito NO puede eliminarse si tiene CAPITAL amortizado.
     */
    public function delete(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        // Se puede eliminar si NO hay CAPITAL amortizado — la baja en cascada
        // (`CreditCascadeDeletionService`) usa el mismo `ensureNoCapitalPaid`.
        return ! $credit->hasCapitalPayments();
    }

    /**
     * Un crédito NO puede editarse si está cerrado.
     */
    public function update(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        // NO editar si está cerrado
        if ($credit->isClosed()) {
            return false;
        }

        // Editar REGENERA el plan de cuotas (borra y recrea las cuotas del propio
        // crédito, ver CreditCalculator), así que NO puede haber ningún pago EFECTIVO
        // —a diferencia de las operaciones, que solo exigen que no haya CAPITAL pagado—.
        // `hasPayments()` ya excluye pagos anulados y reversas, por lo que un crédito
        // al que se le anuló su único pago vuelve a ser editable (#387).
        if ($credit->hasPayments()) {
            return false;
        }

        return true;
    }

    /**
     * Control de edición POR CAMPO.
     * POLÍTICA A PRO: si tiene pagos → no se puede editar NINGÚN campo.
     */
    public function updateField(User $user, Credit $credit, string $field): bool
    {
        if (! $this->update($user, $credit)) {
            return false;
        }

        // Si llega aquí, es porque:
        // - mismo company
        // - NO está cerrado
        // - NO tiene pagos
        return true;
    }

    /**
     * ¿Puede clonar este crédito?
     * Solo si NO está cerrado y NO tiene CAPITAL amortizado.
     */
    public function clone(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        if ($credit->isClosed()) {
            return false;
        }

        // Clonar/extender usan `ensureNoCapitalPaid` y NO regeneran el plan del padre:
        // se permiten mientras no haya CAPITAL amortizado (interés solo sí se permite).
        return ! $credit->hasCapitalPayments();
    }

    /**
     * ¿Puede extender?
     * Solo si NO está cerrado y NO tiene pagos.
     */
    public function extend(User $user, Credit $credit): bool
    {
        return $this->clone($user, $credit);
    }

    /**
     * Cancelar crédito:
     * - Debe ser misma compañía o super_admin.
     * - NO puede estar cerrado.
     * - NO puede tener CAPITAL amortizado (`ensureNoCapitalPaid`).
     */
    public function cancel(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        if ($credit->isClosed()) {
            return false;
        }

        // Cancelar usa `ensureNoCapitalPaid` (solo cambia el status, no regenera cuotas).
        if ($credit->hasCapitalPayments()) {
            return false;
        }

        return true;
    }

    /**
     * Archivar retira el crédito de la cartera operativa: el cobrador deja de
     * verlo, su saldo sale de "por cobrar" y baja el patrimonio, y no se le
     * pueden registrar pagos. Es una decisión de negocio, no de campo — por eso
     * queda reservada al dueño y no al cobrador ni al supervisor.
     *
     * A diferencia de cancelar, NO exige que no haya capital amortizado: archivar
     * no borra ni reescribe nada, solo esconde. Y por eso mismo se permite sobre
     * un crédito con pagos: es justo el caso de "no sé si lo voy a cerrar".
     */
    public function archive(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        if (! $user->hasAnyRole(['admin', 'super_admin'])) {
            return false;
        }

        // Un crédito ya cerrado —pagado, cancelado, sustituido por un hijo— ya
        // está fuera de la cartera: archivarlo no significaría nada.
        return ! $credit->isClosed();
    }

    /** Devolver el crédito a la cartera operativa. */
    public function unarchive(User $user, Credit $credit): bool
    {
        if (! $this->inSameCompany($user, $credit)) {
            return false;
        }

        if (! $user->hasAnyRole(['admin', 'super_admin'])) {
            return false;
        }

        return $credit->status === Credit::STATUS_ARCHIVED;
    }
}
