<?php

class FichaLiquidacion
{
    public $datos = null;
    protected $config;
    protected $asociado;

    public function __construct($asociado = null)
    {
        $this->asociado = $asociado;
        $this->config = ConfLiquidacion::model()->find();

        if ($asociado !== null && $this->datos === null) {
            $this->calcularFicha();
        }
    }

    public function setDatos($datos)
    {
        $this->datos = json_decode($datos, true);
    }

    public function find($path = null, $default = null)
    {
        return (new Warp($this->datos))->get($path, $default);
    }

    public function toJson()
    {
        return json_encode($this->datos);
    }

    public function config($key)
    {
        return $this->config->config($key);
    }

    public function modify($array = [])
    {
        foreach ($array as $key => $value) {
            $this->array_set($this->datos, $key, $value);
        }

        $this->calcularTotal();
    }

    public function hasDatos()
    {
        return $this->datos !== null;
    }

    public function hasDeduccionesCuentasXCobrar()
    {
        return $this->find('totales.deducciones') > 0;
    }

    protected function array_set(&$array, $key, $value)
    {
        if (is_null($key)) {
            return $array = $value;
        }
        $keys = explode('.', $key);
        while (count($keys) > 1) {
            $key = array_shift($keys);
            // If the key doesn't exist at this depth, we will just create an empty array
            // to hold the next value, allowing us to create the arrays to hold final
            // values at the correct depth. Then we'll keep digging into the array.
            if (! isset($array[$key]) || ! is_array($array[$key])) {
                $array[$key] = [];
            }
            $array = &$array[$key];
        }
        $array[array_shift($keys)] = $value;

        return $array;
    }

    protected function calcularFicha()
    {
        $asignaciones = $this->asignaciones();

        $aportesOrdinarios = (new AporteOrdinario)->aporteOrdinarioFichaLiquidacion($this->asociado->idasociado);

        $this->datos = [
            'asignaciones' => [
                'conceptos' => [
                    [
                        'index' => 0,
                        'type' => 'aporte_asociado',
                        'descripcion' => 'Aporte del asociado',
                        'monto' => (float) $asignaciones['aporte_asociado'],
                        'detalle' => $aportesOrdinarios,
                    ],
                    [
                        'index' => 1,
                        'type' => 'aporte_patrono',
                        'descripcion' => 'Aporte patronal',
                        'monto' => (float) $asignaciones['aporte_patrono'],
                        'detalle' => $aportesOrdinarios,
                    ],
                    [
                        'index' => 2,
                        'type' => 'aporte_extra_socio',
                        'descripcion' => 'Aporte voluntario asociado',
                        'monto' => (float) $asignaciones['aporte_extra_socio'],
                        'detalle_individual' => (new AporteVoluntario)->aporteVoluntarioIndividualFichaLiquidacion($this->asociado->idasociado, 1),
                        'detalle_masivo' => (new AporteVoluntario)->aportevoluntarioMasivoFichaLiquidacion($this->asociado->idasociado, 1),
                    ],
                    [
                        'index' => 3,
                        'type' => 'aporte_extra_patrono',
                        'descripcion' => 'Aporte extraordinario del patrono',
                        'monto' => (float) $asignaciones['aporte_extra_patrono'],
                        'detalle_individual' => (new AporteVoluntario)->aporteVoluntarioIndividualFichaLiquidacion($this->asociado->idasociado, 2),
                        'detalle_masivo' => (new AporteVoluntario)->aportevoluntarioMasivoFichaLiquidacion($this->asociado->idasociado, 2),
                    ],
                    [
                        'index' => 4,
                        'type' => 'asignacion_monto_asociado',
                        'descripcion' => 'Ajuste por asignación de haberes asociado',
                        'monto' => (float) $asignaciones['asignacion_monto_asociado'],
                        'detalle' => (new AjusteHaberes)->ajusteSocioFichaLiquidacion($this->asociado->idasociado, [
                            'id_tipo_ajuste' => 2,
                            'id_tipo_movimiento' => [1, 3],
                        ]),
                    ],
                    [
                        'index' => 5,
                        'type' => 'asignacion_monto_patrono',
                        'descripcion' => 'Ajuste por asignación de haberes patrono',
                        'monto' => (float) $asignaciones['asignacion_monto_patrono'],
                        'detalle' => (new AjusteHaberes)->ajusteSocioFichaLiquidacion($this->asociado->idasociado, [
                            'id_tipo_ajuste' => 2,
                            'id_tipo_movimiento' => [2, 3],
                        ]),
                    ],
                    [
                        'index' => 6,
                        'type' => 'dividendos',
                        'descripcion' => 'Dividendos capitalizados',
                        'monto' => (float) $asignaciones['dividendos'],
                        'detalle' => (new AsociadoDividendos)->dividendosSocioFichaLiquidacion($this->asociado->idasociado),
                    ],
                ],
                'total' => [
                    'descripcion' => 'Total asignación de aportes para liquidación',
                    'monto' => (float) $asignaciones['total'],
                ],
            ],
        ];

        $this->datos = array_merge($this->datos, [
            'retiro_deducciones' => [
                'conceptos' => [
                    [
                        'index' => 0,
                        'type' => 'descuento_monto_asociado',
                        'descripcion' => 'Ajuste por deducción de haberes asociado',
                        'monto' => (float) $asignaciones['descuento_monto_asociado'],
                        'detalle' => (new AjusteHaberes)->ajusteSocioFichaLiquidacion($this->asociado->idasociado, [
                            'id_tipo_ajuste' => 1,
                            'id_tipo_movimiento' => [1, 3],
                        ]),
                    ],
                    [
                        'index' => 1,
                        'type' => 'descuento_monto_patrono',
                        'descripcion' => 'Ajuste por deducción de haberes patronal',
                        'monto' => (float) $asignaciones['descuento_monto_patrono'],
                        'detalle' => (new AjusteHaberes)->ajusteSocioFichaLiquidacion($this->asociado->idasociado, [
                            'id_tipo_ajuste' => 1,
                            'id_tipo_movimiento' => [2, 3],
                        ]),
                    ],
                    [
                        'index' => 2,
                        'type' => 'retiro_parcial',
                        'descripcion' => 'Retiro parcial',
                        'monto' => (float) $asignaciones['retiro_parcial'],
                        'detalle' => (new RetiroParcial)->retiroAsociadoFichaLiquidacion($this->asociado->idasociado),
                    ],
                ],
                'total' => [
                    'descripcion' => 'Total deducción de aportes para liquidación',
                    'monto' => (float) $asignaciones['descuento_monto_asociado'] + $asignaciones['descuento_monto_patrono'] + $asignaciones['retiro_parcial'],
                ],
            ],
        ]);

        $cuotasNoAfectadasPorPrenomina = $this->cuotasNoAfectadasPorPrenomina();

        $this->datos = array_merge($this->datos, [
            'capital_cartera_prestamos' => [
                'conceptos' => $cuotasNoAfectadasPorPrenomina,
                'total' => [
                    'descripcion' => 'Total capital cartera de préstamo',
                    'monto' => (new Warp($cuotasNoAfectadasPorPrenomina))->sum('monto'),
                ],
            ],
        ]);

        $cuotasAfectadasPorPrenominaBase = $this->cuotasAfectadasPorPrenomina();
        $cuotasAfectadasPorPrenomina = (new Warp($cuotasAfectadasPorPrenominaBase))->map(function ($prestamo) {
            return [
                'id_credito' => $prestamo['id_credito'],
                'id_tipo_credito' => $prestamo['id_tipo_credito'],
                'descripcion' => $prestamo['descripcion'],
                'monto' => $prestamo['monto_capital'],
                'detalle' => (new CreditoTablaAmortizacion)->cuotasAfectadasFichaLiquidacion($this->asociado->idasociado, $prestamo['id_credito']),
            ];
        });

        $this->datos = array_merge($this->datos, [
            'capital_afectado_prenomina' => [
                'conceptos' => $cuotasAfectadasPorPrenomina,
                'total' => [
                    'descripcion' => 'Total cuenta por cobrar capital de préstamo',
                    'monto' => (new Warp($cuotasAfectadasPorPrenominaBase))->sum('monto_capital'),
                ],
            ],
        ]);

        $interésresAfectado = (new Warp($cuotasAfectadasPorPrenominaBase))->map(function ($prestamo) {
            return [
                'id_credito' => $prestamo['id_credito'],
                'id_tipo_credito' => $prestamo['id_tipo_credito'],
                'descripcion' => $prestamo['descripcion'],
                'monto' => $prestamo['monto_interes'],
                'detalle' => (new CreditoTablaAmortizacion)->cuotasAfectadasFichaLiquidacion($this->asociado->idasociado, $prestamo['id_credito']),
            ];
        });

        $this->datos = array_merge($this->datos, [
            'interes_afectado_prenomina' => [
                'conceptos' => $interésresAfectado,
                'total' => [
                    'descripcion' => 'Total cuenta por cobrar interés de préstamo',
                    'monto' => (new Warp($cuotasAfectadasPorPrenominaBase))->sum('monto_interes'),
                ],
            ],
        ]);

        $this->calcularTotal();
    }

    protected function calcularTotal()
    {
        $asignaciones = (new Warp($this->datos))->get('asignaciones.total.monto', 0) - (new Warp($this->datos))->get('retiro_deducciones.total.monto', 0);
        $deducciones = $this->deducciones();
        $subtotal = round($asignaciones - $deducciones, 2);

        $administrativa = $this->config->comisionAdministrativa($subtotal);
        $bancaria = $this->config->comisionBancaria($subtotal);

        if ($subtotal > 0 && $subtotal > ($administrativa + $bancaria)) {
            $totales = [
                'comision_administrativa' => $administrativa,
                'comision_bancaria' => $bancaria,
                'total_a_depositar' => $subtotal - ($administrativa + $bancaria),
                'cuenta_x_cobrar' => 0,
            ];
        } elseif ($subtotal > 0 && $subtotal > $administrativa && $bancaria > ($subtotal - $administrativa)) {
            $totales = [
                'comision_administrativa' => $subtotal,
                'comision_bancaria' => 0,
                'total_a_depositar' => 0,
                'cuenta_x_cobrar' => 0,
            ];
        } elseif ($subtotal > 0 && $subtotal < $administrativa && $subtotal < $bancaria) {
            $totales = [
                'comision_administrativa' => $subtotal + $bancaria,
                'comision_bancaria' => 0,
                'total_a_depositar' => 0,
                'cuenta_x_cobrar' => 0,
            ];
        } elseif ($subtotal < 0) {
            $totales = [
                'comision_administrativa' => 0,
                'comision_bancaria' => 0,
                'total_a_depositar' => 0,
                'cuenta_x_cobrar' => abs($subtotal),
            ];
        } else {
            $totales = [
                'comision_administrativa' => 0,
                'comision_bancaria' => 0,
                'total_a_depositar' => 0,
                'cuenta_x_cobrar' => 0,
            ];
        }

        $this->datos = array_merge($this->datos, [
            'totales' => (new Warp([
                'asignaciones' => $asignaciones,
                'deducciones' => $deducciones,
                'subtotal' => $subtotal,
                $totales,
            ]))->flatten(),
        ]);
    }

    protected function deducciones()
    {
        return array_sum([
            (new Warp($this->datos))->get('capital_cartera_prestamos.total.monto'),
            (new Warp($this->datos))->get('capital_afectado_prenomina.total.monto'),
            (new Warp($this->datos))->get('interes_afectado_prenomina.total.monto'),
        ]);
    }

    private function asignaciones()
    {
        return Yii::app()->getDb()->createCommand('
            SELECT idasociado,
                    aporte_total,
                    retiro_parcial,
                    aporte_asociado,
                    aporte_patrono,
                    aporte_extra_socio,
                    aporte_extra_patrono,
                    asignacion_monto_patrono,
                    asignacion_monto_asociado,
                    descuento_monto_asociado,
                    descuento_monto_patrono,
                    dividendos,
                    aporte_asociado + aporte_patrono + aporte_extra_socio + aporte_extra_patrono + asignacion_monto_asociado + asignacion_monto_patrono + dividendos as total
            FROM "reporte_haberes" "t"
            WHERE idasociado=:id;
        ')->bindValues([
            'id' => $this->asociado->idasociado,
        ])->queryRow();
    }

    private function cuotasNoAfectadasPorPrenomina()
    {
        $datos = Yii::app()->getDb()->createCommand('
            select c.id as id_credito,
                   c.id_tipo_credito,
                   concat(\'prestamos_no_afectados\', c.id_tipo_credito) as type,
                   concat(css.fecha_solicitud, \' \', tc.descripcion) as descripcion,
                   sum(cta.monto_capital) as monto
            from prestamos.credito_tabla_amortizacion cta
            inner join prestamos.credito c on c.id=cta.idcredito
                and c.blnborrado is false
            inner join prestamos.credito_seguimiento cs on cs.id_credito=c.id
                and cs.actual is true
            inner join prestamos.tipo_credito tc on c.id_tipo_credito=tc.id
            left join (
                select id_credito, TO_CHAR(fecha_registro::date, \'DD-MM-YYYY\') as fecha_solicitud
                from prestamos.credito_seguimiento
                where id_estatus_credito in (1,6,8)
            ) as css on css.id_credito=c.id
            where c.idasociado=:id
                AND cta.blnborrado is false
                and id_estatus_cuota is null
                and cs.id_estatus_credito in (4,15,16)
            group by c.id, c.id_tipo_credito, tc.descripcion, css.fecha_solicitud
            order by c.id DESC
        ')->bindValues([
            'id' => $this->asociado->idasociado,
        ])->queryAll();

        return (new Warp($datos))->map(function ($prestamo) {
            return array_merge($prestamo, [
                'detalle' => (new CreditoTablaAmortizacion)->cuotasNoafectadasFichaLiquidacion(
                    $this->asociado->idasociado, 
                    $prestamo['id_credito']
                )
            ]);
        });
    }

    private function cuotasAfectadasPorPrenomina()
    {
        return Yii::app()->getDb()->createCommand('
            select id_credito,
                   id_tipo_credito,
                   descripcion,
                   sum(monto_capital) as monto_capital,
                   sum(monto_interes) as monto_interes
            from (
                select c.id as id_credito,
                       c.id_tipo_credito,
                       css.fecha_solicitud,
                       concat(css.fecha_solicitud, \' \', tc.descripcion) as descripcion,
                       case when cta.id_estatus_cuota=12
                           then coalesce(pp.capital_cobro,0)
                           else cta.monto_capital
                       end as monto_capital,
                       case when cta.id_estatus_cuota=12
                           then coalesce(pp.interes_cobro,0)
                           else cta.monto_interes
                       end as monto_interes
                from prestamos.credito_tabla_amortizacion cta
                inner join prestamos.credito c on c.id=cta.idcredito
                    and c.blnborrado is false
                inner join prestamos.tipo_credito tc on c.id_tipo_credito=tc.id
                left join (
                    select cta.id as id_cuota,
                        cta.id_estatus_cuota,
                        cdc.capital_cobro,
                        cdc.interes_cobro
                    from prestamos.credito_tabla_amortizacion cta
                    inner join prestamos.credito c on c.id=cta.idcredito
                        and c.blnborrado is false
                    inner join prestamos.datos_txt_tabla_amortizacion dtta on dtta.id_tabla_amortizacion=cta.id
                        and dtta.actual is true
                    inner join prestamos.cobro_diferencial_cuota cdc on cdc.id_proceso=dtta.id
                        and cdc.blnborrado is false
                ) pp on pp.id_cuota=cta.id
                left join (
                    select id_credito, 
                        TO_CHAR(fecha_registro::date, \'DD-MM-YYYY\') as fecha_solicitud
                    from prestamos.credito_seguimiento
                    where id_estatus_credito in (1,6,8)
                ) as css on css.id_credito=c.id
                where cta.blnborrado is false
                    and c.idasociado=:id
                    and cta.id_estatus_cuota in (1,3,6,7,8,9,12)
            ) d
            group by id_credito, id_tipo_credito, fecha_solicitud, descripcion
            order by id_credito DESC
        ')->bindValues([
            'id' => $this->asociado->idasociado,
        ])->queryAll();
    }
}
