<?php

class CasoAAction
{
    use AttributeAccessTrait;

    protected $attributes;

    public function __construct($attributes)
    {
        $this->attributes = $attributes;
    }

    public function execute()
    {
        list($_, $sinDefinir, $excluidas) = $this->conciliarCuotas();

        $sinDefinir += (new CrearCuotasSinDefinirAction($this->attributes))->execute();

        $excluidas += (new ExcluirCuotasPrenominaNoPresentesEnNominaAction($this->attributes))->execute();

        // Lleva la nómina al estatus correspondiente dependiedo de la cantidad de cuotas sin definir y excluidas
        (new CrearEstatusNominaAction($this->id_nomina, NominaEstatusObject::get($sinDefinir, $excluidas)))->execute();

        // Lleva la prenómina al estatus correspondiente dependiedo de la cantidad de cuotas sin definir y excluidas
        (new ActualizarEstatusPrenominaAction(
            $this->id_prenomina,
            PrenominaEstatusObject::get($sinDefinir, $excluidas)
        ))->execute();

        return true;
    }

    private function conciliarCuotas()
    {
        if (ConfPreNomina::config('id_metodo_distribucion') === MetodosDistribucion::COINCIDENTE) {
            return $this->cuotasCoincidentes();
        }

        return $this->cuotasNoCoincidentes();
    }

    private function cuotasCoincidentes()
    {
        $prenominaConfig = ConfPreNomina::model()->find();

        // Agrupa las cuotas de prenómina dependiendo del metodo de distribución y el metodo de liberación
        $cuotasPrenomina = AgruparObject::make(
            CuotasPrenominaQuery::all([
                'prenomina' => $this->id_prenomina,
                'nomina' => $this->id_nomina
            ])
        );
        // Agrupa las cuotas de nómina dependiendo del metodo de distribución y el metodo de liberación
        $cuotasNomina = AgruparObject::make(CuotasNominaQuery::all(['nomina' => $this->id_nomina]));

        $sinDefinir = 0;
        $excluidas = 0;
        // Intera sobre cada uno de los asociados con cuotas en prenomina
        foreach ($cuotasPrenomina as $cedula => $prestamos) {
            // Intera sobre cada uno de los tipos de préstamo del asociado
            foreach ($prestamos as $id_tipo_prestamo => $prestamo) {
                // Reinicia el monto restante para cada grupo de cuotas del asociado
                $restanteAnterior = 0;

                // Intera sobre cada una de las cuotas del asociado
                foreach ($prestamo['cuotas'] as $index => $cuota) {
                    // Obtiene el total en nómina del asociado
                    $totalEnNomina = $cuotasNomina[$cedula][$id_tipo_prestamo]['total'];

                    // Determina si la cuota actual es la ultima cuota del asociado
                    $nextIndex = $index + 1;
                    $next = (new Warp($prestamo))->get("cuotas.{$nextIndex}.id", false);

                    // Aplica procedimiento de conciliacion para la cuota
                    $this->procesar(
                        $cuota,
                        $index,
                        $totalEnNomina,
                        $restanteAnterior,
                        $prenominaConfig,
                        $next,
                        $sinDefinir,
                        $excluidas
                    );
                }
            }
        }

        return [true, $sinDefinir, $excluidas];
    }

    private function cuotasNoCoincidentes()
    {
        $prenominaConfig = ConfPreNomina::model()->find();

        // Agrupa las cuotas de prenómina dependiendo del metodo de distribución y el metodo de liberación
        $cuotasPrenomina = AgruparObject::make(
            CuotasPrenominaQuery::all([
                'prenomina' => $this->id_prenomina,
                'nomina' => $this->id_nomina
            ])
        );

        // Agrupa las cuotas de nómina dependiendo del metodo de distribución y el metodo de liberación
        $cuotasNomina = AgruparObject::make(CuotasNominaQuery::all(['nomina' => $this->id_nomina]));

        $sinDefinir = 0;
        $excluidas = 0;
        // Intera sobre los asociados en la prenomina
        foreach ($cuotasPrenomina as $cedula => $datos) {
            // Reinicia el monto restante para cada grupo de cuotas del asociado
            $restanteAnterior = 0;

            // Intera sobre las cuotas del asociado
            foreach ($datos['cuotas'] as $index => $cuota) {
                $totalEnNomina = $cuotasNomina[$cedula]['total'];

                // Determina si la cuota actual es la ultima cuota del asociado
                $nextIndex = $index + 1;
                $next = (new Warp($datos))->get("cuotas.{$nextIndex}.id", false);

                // Aplica procedimiento de conciliacion para la cuota
                $this->procesar(
                    $cuota,
                    $index,
                    $totalEnNomina,
                    $restanteAnterior,
                    $prenominaConfig,
                    $next,
                    $sinDefinir,
                    $excluidas
                );
            }
        }

        return [true, $sinDefinir, $excluidas];
    }

    private function procesar(
        $cuota,
        $index,
        $totalEnNomina,
        &$restanteAnterior,
        $prenominaConfig,
        $hasNext,
        &$sinDefinir,
        &$excluidas
    ) {
        // El monto total cargado en la nomina es menor al monto de la primera cuota en prenomina
        if ($totalEnNomina <= $cuota['monto']) {
            // Cuota a la cual se le va a aplicar todo el monto cargado en nómina
            if ($index === 0) {
                $cuotaConciliada = new DatosTxtTablaAmortizacion();
                $cuotaConciliada->id_txt = $this->id_nomina;
                $cuotaConciliada->id_tabla_amortizacion = $cuota['id_cuota'];
                $cuotaConciliada->monto_cuota_pagado = Yii::app()->format->number($totalEnNomina);

                $montoPendientePorCobrar = $cuota['monto'] - $totalEnNomina;
                $cuotaConciliada->monto_cobro = $montoPendientePorCobrar <= 0 ? 0 : $montoPendientePorCobrar;
                $cuotaConciliada->monto_remanente = 0;
                $ok = $cuotaConciliada->insert();

                // Seguimiento para el monto por cobrar de la cuota conciliada
                if ($ok && $cuotaConciliada->monto_cobro > 0) {
                    $metodo_cobro = $prenominaConfig->distribucionCuota(
                        $totalEnNomina,
                        (object) [
                            'monto_capital' => $cuota['capital'],
                            'monto_interes' => $cuota['interes'],
                            'monto_cuota' => $cuota['capital'] + $cuota['interes']
                        ]
                    );
                    $diferencialCobro = new CobroDiferencialCuota();
                    $diferencialCobro->id_proceso = $cuotaConciliada->id;
                    $diferencialCobro->id_nombre_proceso = 1;
                    $diferencialCobro->total_cobro = $cuotaConciliada->monto_cobro;
                    $diferencialCobro->capital_pagado = $metodo_cobro['capital_pagado'];
                    $diferencialCobro->interes_pagado = $metodo_cobro['interes_pagado'];
                    $diferencialCobro->capital_cobro = $metodo_cobro['capital_cobro'];
                    $diferencialCobro->interes_cobro = $metodo_cobro['interes_cobro'];

                    if ($diferencialCobro->save()) {
                        $diferencialCobroSeguimiento = new CobroDiferencialCuotaSeguimiento();
                        $diferencialCobroSeguimiento->id_cobro_diferencial_cuota = $diferencialCobro->id;
                        $diferencialCobroSeguimiento->id_estatus_cobro_cuota = 1;
                        $diferencialCobroSeguimiento->save();
                    }
                }
            } elseif ($index > 0 && $restanteAnterior === 0) {
                // Se excluyen las otras cuotas del asociado, cuando se ha aplicado todo el monto cargado a la primera cuota
                (new ExcluirCuotaAction($cuota['id']))->execute();
                $excluidas++;
            }
        } elseif ($totalEnNomina > $cuota['monto']) {
            // Evalua la primera cuota en prenomina
            if ($restanteAnterior === 0 && $index == 0) {
                $cuotaConciliada = new DatosTxtTablaAmortizacion();
                $cuotaConciliada->id_txt = $this->id_nomina;
                $cuotaConciliada->id_tabla_amortizacion = $cuota['id_cuota'];
                $cuotaConciliada->monto_cuota_pagado = Yii::app()->format->number($cuota['monto']);
                $cuotaConciliada->monto_cobro = 0;
                $cuotaConciliada->monto_remanente = 0;
                $cuotaConciliada->insert();

                // Se calcula el restante del pago de la cuota
                $restanteAnterior = bcsub($totalEnNomina, $cuota['monto'], 2);

                // Si es la ultima cuota y el restante de la interaccion anterior es mayor a cero, se envia a la seccion
                // de cuotas sin definir el monto restante de la interaccion
                if (!$hasNext && $restanteAnterior > 0) {
                    $model = new DatosTxtTablaAmortizacionDiferido();
                    $model->id_txt = $this->id_nomina;
                    $model->cedula = $cuota['cedula'];
                    $model->id_tipo_credito = $cuota['id_tipo_credito'];
                    $model->monto_cuota = $restanteAnterior;
                    $model->tipo_cuota = $cuota['tipo_cuota'];
                    $model->observaciones = 'Diferencia al conciliar la cuota del asociado';
                    $model->id_estatus_amortizacion_diferida = 1; // No definida
                    $model->save();

                    // Se resetea el valor restante anterior ya que está es la última cuota
                    $restanteAnterior = 0;
                    $sinDefinir++;
                }
            } elseif ($restanteAnterior === 0 && $index > 0) {
                // Excluye las cuotas siguientes del asociado cuando no tiene monto restante que repartir entre las cuota siguientes
                // El monto restante no cubre las siguientes cuotas del asociado
                (new ExcluirCuotaAction($cuota['id']))->execute();
                $excluidas++;
            } elseif ($restanteAnterior > 0) {
                // El monto restante cubre algunos de los montos de las cuotas siguientes
                $cuotaConciliada = new DatosTxtTablaAmortizacion();
                $cuotaConciliada->id_txt = $this->id_nomina;
                $cuotaConciliada->id_tabla_amortizacion = $cuota['id_cuota'];
                $cuotaConciliada->monto_cuota_pagado = Yii::app()->format->number(
                    $restanteAnterior >= $cuota['monto'] ? $cuota['monto'] : $restanteAnterior
                );
                $cuotaConciliada->monto_cobro =
                    $restanteAnterior >= $cuota['monto'] ? 0 : abs(bcsub($restanteAnterior, $cuota['monto'], 2));
                $ok = $cuotaConciliada->insert();

                // Seguimiento para el monto por cobrar de la cuota conciliada
                if ($ok && $cuotaConciliada->monto_cobro > 0) {
                    $metodo_cobro = $prenominaConfig->distribucionCuota(
                        $restanteAnterior >= $cuota['monto'] ? $cuota['monto'] : $restanteAnterior,
                        (object) [
                            'monto_capital' => $cuota['capital'],
                            'monto_interes' => $cuota['interes'],
                            'monto_cuota' => $cuota['capital'] + $cuota['interes']
                        ]
                    );
                    $diferencialCobro = new CobroDiferencialCuota();
                    $diferencialCobro->id_proceso = $cuotaConciliada->id;
                    $diferencialCobro->id_nombre_proceso = 1;
                    $diferencialCobro->total_cobro = $cuotaConciliada->monto_cobro;
                    $diferencialCobro->capital_pagado = $metodo_cobro['capital_pagado'];
                    $diferencialCobro->interes_pagado = $metodo_cobro['interes_pagado'];
                    $diferencialCobro->capital_cobro = $metodo_cobro['capital_cobro'];
                    $diferencialCobro->interes_cobro = $metodo_cobro['interes_cobro'];

                    if ($diferencialCobro->save()) {
                        $diferencialCobroSeguimiento = new CobroDiferencialCuotaSeguimiento();
                        $diferencialCobroSeguimiento->id_cobro_diferencial_cuota = $diferencialCobro->id;
                        $diferencialCobroSeguimiento->id_estatus_cobro_cuota = 1;
                        $diferencialCobroSeguimiento->save();
                    }
                }

                // Se almacena el restante para la interacción con la proxima cuota
                $restanteAnterior =
                    $restanteAnterior > $cuota['monto'] ? bcsub($restanteAnterior, $cuota['monto'], 2) : 0;

                // Si es la ultima cuota y el restante de la interaccion anterior es mayor a cero, se envia a la seccion
                // de cuotas sin definir el monto restante de la interaccion
                if (!$hasNext && $restanteAnterior > 0) {
                    $cuotaSinDefinir = new DatosTxtTablaAmortizacionDiferido();
                    $cuotaSinDefinir->id_txt = $this->id_nomina;
                    $cuotaSinDefinir->cedula = $cuota['cedula'];
                    $cuotaSinDefinir->id_tipo_credito = $cuota['id_tipo_credito'];
                    $cuotaSinDefinir->monto_cuota = $restanteAnterior;
                    $cuotaSinDefinir->tipo_cuota = $cuota['tipo_cuota'];
                    $cuotaSinDefinir->observaciones = 'Diferencia al conciliar la cuota del asociado';
                    $cuotaSinDefinir->id_estatus_amortizacion_diferida = 1; // No definida
                    $cuotaSinDefinir->save();

                    // Se resetea el valor restante anterior ya que está es la última cuota
                    $restanteAnterior = 0;
                    $sinDefinir++;
                }
            }
        }
    }
}
