<?php

class CasoCAction
{
    use AttributeAccessTrait;

    protected $attributes;

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

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

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

        // Lleva la nomina a estatus  "cargado"
        (new CrearEstatusNominaAction(
            $this->id_nomina,
            NominaEstatusObject::get($sinDefinir + $cuotasSinDefinir, 0)
        ))->execute();

        // Lleva la prenomina a estatus  "cargado"
        (new ActualizarEstatusPrenominaAction(
            $this->id_prenomina,
            PrenominaEstatusObject::get($sinDefinir + $cuotasSinDefinir, 0)
        ))->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;
        foreach ($cuotasPrenomina as $cedula => $prestamos) {
            foreach ($prestamos as $id_tipo_prestamo => $prestamo) {
                foreach ($prestamo['cuotas'] as $index => $cuota) {
                    if (! $this->asociadoEnNomina($cuotasNomina, $cedula, $id_tipo_prestamo, $index)) {
                        continue;
                    }

                    // Monto de la cuota en nómina
                    $cuotaEnNomina = $cuotasNomina[$cedula][$id_tipo_prestamo]['cuotas'][$index]['monto'];
                    // Calcula la diferencia general de ambos grupos de cuotas nómina - prenómina
                    $diferencia = $cuotasNomina[$cedula][$id_tipo_prestamo]['total'] - $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);

                    $this->procesar($cuota, $cuotaEnNomina, $prenominaConfig, $next, $sinDefinir, $diferencia);
                }
            }
        }

        return [true, $sinDefinir];
    }

    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;
        foreach ($cuotasPrenomina as $cedula => $datos) {
            foreach ($datos['cuotas'] as $index => $cuota) {
                if (! $this->asociadoEnNomina($cuotasNomina, $cedula, null, $index)) {
                    continue;
                }

                // Monto de la cuota en nómina
                $cuotaEnNomina = $cuotasNomina[$cedula]['cuotas'][$index]['monto'];
                // Calcula la diferencia general de ambos grupos de cuotas nómina - prenómina
                $diferencia = $cuotasNomina[$cedula]['total'] - $datos['total'];

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

                $this->procesar($cuota, $cuotaEnNomina, $prenominaConfig, $next, $sinDefinir, $diferencia);
            }
        }

        return [true, $sinDefinir];
    }

    private function procesar($cuota, $montoCuotaNomina, $prenominaConfig, $hasNext, &$sinDefinir, $diferenciaGeneral)
    {
        // Se concilia la cuota de prenomina
        $cuotaConciliada = new DatosTxtTablaAmortizacion();
        $cuotaConciliada->id_txt = $this->id_nomina;
        $cuotaConciliada->id_tabla_amortizacion = $cuota['id_cuota'];
        $cuotaConciliada->monto_cuota_pagado = Yii::app()->format->number($montoCuotaNomina);

        // Calcula la diferencia entre la cuota cargada en nomina y la cuota en prenomina
        $diferencia = abs(bcsub($cuota['monto'], $montoCuotaNomina, 2));

        // Determina a donde va a parar la diferencia
        if ($montoCuotaNomina > $cuota['monto']) {
            $cuotaConciliada->monto_remanente = $diferencia;
        } elseif ($montoCuotaNomina < $cuota['monto']) {
            $cuotaConciliada->monto_cobro = $diferencia;
        }

        $ok = $cuotaConciliada->insert();

        if ($ok && $cuotaConciliada->monto_remanente > 0) {
            DatosTxtTablaAmortizacionSeguimiento::create([
                'id_estatus' => 1,
                'id_cuota' => $cuotaConciliada->id,
            ]);
        }

        if ($ok && $cuotaConciliada->monto_cobro > 0) {
            $metodo_cobro = $prenominaConfig->distribucionCuota(
                $montoCuotaNomina,
                (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();
            }
        }

        // Si es la última cuota del asociado para enviar la diferencia a la sección de cuotas sin definir.
        if (! $hasNext && $diferenciaGeneral > 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 = abs($diferencia - $diferenciaGeneral);
            $model->tipo_cuota = $cuota['tipo_cuota'];
            $model->observaciones = 'Diferencia resultante al conciliar todos las cuotas del asociado';
            $model->id_estatus_amortizacion_diferida = 1; // No definida
            $model->save();

            // Se cuenta cuantas cuotas sin definir hay
            $sinDefinir++;
        }
    }

    private function asociadoEnNomina($cuotasNomina, $cedula, $id_tipo_prestamo, $index)
    {
        if (! array_key_exists($cedula, $cuotasNomina)) {
            return false;
        }

        if (ConfPreNomina::config('id_metodo_distribucion') === MetodosDistribucion::COINCIDENTE) {
            if (! array_key_exists($id_tipo_prestamo, $cuotasNomina[$cedula])) {
                return false;
            }
            if (! array_key_exists('cuotas', $cuotasNomina[$cedula][$id_tipo_prestamo])) {
                return false;
            }

            if (! array_key_exists($index, $cuotasNomina[$cedula][$id_tipo_prestamo]['cuotas'])) {
                return false;
            }
        } else {
            if (! array_key_exists('cuotas', $cuotasNomina[$cedula])) {
                return false;
            }

            if (! array_key_exists($index, $cuotasNomina[$cedula]['cuotas'])) {
                return false;
            }
        }

        return true;
    }
}
