<?php

class Masivo extends Utilidades implements Agrupable
{
    protected $nomina;
    public $escenario = 4;
    protected $reverso = false;
    protected $tipo_prestamo = 3;
    protected $tipo_afianzadora = 2;
    protected $prestamos_seleccionados = null;
    public $tipo_parametro_rebaja_prestamos = 3;

    public function __construct($nomina)
    {
        $this->nomina = $nomina;
        Yii::import('application.modules.contable.models.*', true);
    }

    public function contabilizar()
    {
        if (! ParamEscenarios::conectado($this->escenario)) {
            return;
        }

        ////////////////////////////////// COMPROBANTE /////////////////////////////////
        $comprobante = $this->comprobante();

        $this->seguimientoComprobante(
            $comprobante->id,
            $this->nomina->id,
            $this->nomina->getIdComprobantePago(),
            $this->nomina->getTituloProceso()
        );
        ////////////////////////// ULTIMO COMPROBANTE /////////////////////////
        if (! $this->reverso) {
            $this->nomina->saveAttributes(['id_comprobante' => $comprobante->id]);
        } else {
            $this->nomina->saveAttributes(['id_comprobante' => null]);
        }
        ////////////////////////// MOVIMIENTOS DEL COMPROBANTE /////////////////////////
        $this->movimientos($comprobante);
    }

    public function groupScenario()
    {
        return $this->tipo_parametro_rebaja_prestamos;
    }

    public function groupData()
    {
        return $this->prestamoAnterior();
    }

    protected function comprobante()
    {
        $this->setProceso($this->nomina);

        $comprobante = new Comprobante();
        $comprobante->fecha_comprobante = $this->fechaComprobante();
        $comprobante->descripcion = "Pago masivo préstamo {$this->nomina->descripcion}";
        $comprobante->status = 1;
        $comprobante->nro_documento = $this->nomina->num_comprobante;

        if (! $comprobante->save()) {
            throw new Exception('Error al generar comprobante contable.', $this->codigo_error);
        }

        return $comprobante;
    }

    protected function movimientos($comprobante)
    {
        $movimiento = new Movimiento(
            $comprobante,
            $this->nomina->fecha_referencia
        );

        $montos = $this->getMontos();

        foreach ($this->creditos() as $credito) {
            $movimiento->crear(
                Parametros::getCuentaContable([
                    'clave' => 'cta_plazo_prestamo',
                    'escenario' => $this->escenario,
                    'tipo_parametro' => $this->tipo_prestamo,
                    'proceso' => $credito['id'],
                ]),
                $montos["cta_plazo_prestamo_{$credito['id']}"]
            );
        }

        if (! $this->configuracionAuxiliar('cta_comision_administrativa') && array_key_exists('cta_comision_administrativa', $montos)) {
            $movimiento->crear(
                Parametros::getCuentaContable([
                    'clave' => 'cta_comision_administrativa',
                    'escenario' => $this->escenario,
                    'tipo_parametro' => $this->tipo_prestamo,
                    'proceso' => 0,
                ]),
                0,
                $montos['cta_comision_administrativa']
            );
        } else {
            foreach ($this->gastosAdministrativos() as $gasto) {
                $movimiento->crear(
                    Parametros::getCuentaContable([
                        'clave' => 'cta_comision_administrativa',
                        'escenario' => $this->escenario,
                        'tipo_parametro' => $this->tipo_prestamo,
                        'proceso' => $gasto['id'],
                    ]),
                    0,
                    $montos['cta_comision_administrativa_'.$gasto['id']]
                );
            }
        }

        foreach ($this->afianzadoras() as $afianzadora) {
            if ($afianzadora['gastos_administrativos_afianzadora'] > 0) {
                $movimiento->crear(
                    $afianzadora['id_cuenta'],
                    0,
                    $montos['cta_fianza_prestamo_'.$afianzadora['id']]
                );
            }
        }

        if (array_key_exists('cta_rebaja_prestamo', $montos)) {
            $movimiento->crear(
                $this->nomina->bancoCuentaContable($this->nomina->id_param_banco),
                0,
                $montos['cta_rebaja_prestamo']
            );
        }

        $datosAgrupados = OrderByCuentaContable::comprobante($this)
            ->clave('cta_plazo_prestamo')
            ->groupBy('cta_rebaja_prestamo_anterior');

        if (count($datosAgrupados) > 0) {
            foreach ($datosAgrupados as $prestamo) {
                $movimiento->crear(
                    $prestamo['cuenta_contable'],
                    0,
                    $prestamo['monto']
                );
            }
        }
    }

    protected function getMontos()
    {
        $debe = (new Warp([
            'cta_plazo_prestamo' => $this->montoPrestamos(),
            'cta_retenciones_ingreso_diferido' => $this->prestamosAnteriores('cta_retenciones_ingreso_diferido', 'interes'),
        ]))->flatten();

        $haber = (new Warp([
            'cta_comision_administrativa' => $this->getGastosAdministrativos(),
            'cta_fianza_prestamo' => $this->getGastosAdministrativosAfianzadoras(),
            'cta_rebaja_prestamo' => $this->rebajaPrestamo(),
            'cta_rebaja_prestamo_anterior' => $this->prestamosAnteriores('cta_rebaja_prestamo_anterior', 'monto'),
            'cta_retenciones_cta_x_cobrar_capital' => $this->prestamosAnteriores('cta_retenciones_cta_x_cobrar_capital', 'capital'),
            'cta_retenciones_cta_x_cobrar_interes' => $this->prestamosAnteriores('cta_retenciones_cta_x_cobrar_interes', 'interes'),
            'cta_retenciones_ingreso_interes' => $this->prestamosAnteriores('cta_retenciones_ingreso_interes', 'interes'),
        ]))->flatten();

        // $this->debug($debe, $haber);

        $this->validate($debe, $haber);

        return array_merge([], $debe, $haber);
    }

    protected function montoPrestamos()
    {
        return (new Warp($this->creditos()))->flatMap(function ($credito) {
            return ["cta_plazo_prestamo_{$credito['id']}" => $credito['monto']];
        });
    }

    protected function getGastosAdministrativos()
    {
        if (! $this->configuracionAuxiliar('cta_comision_administrativa')) {
            return $this->gastoAdministrativoConsolidado();
        }

        $ga = [];
        foreach ($this->gastosAdministrativos() as $gastoAdministrativo) {
            $ga['cta_comision_administrativa_'.$gastoAdministrativo['id']] = $gastoAdministrativo['gastos_administrativo'];
        }

        if (empty(array_filter($ga))) {
            return null;
        }

        return $ga;
    }

    protected function getGastosAdministrativosAfianzadoras()
    {
        $haber = [];
        foreach ($this->afianzadoras() as $afianzadora) {
            $haber['cta_fianza_prestamo_'.$afianzadora['id']] = $afianzadora['gastos_administrativos_afianzadora'];
        }

        if (empty(array_filter($haber))) {
            return null;
        }

        return $haber;
    }

    protected function rebajaPrestamo()
    {
        return Yii::app()->getDb()->createCommand('
            SELECT sum(c.monto_credito_depositar) AS monto_credito_depositar
            FROM pagos.pagos_masivos_detalle pmd
            INNER JOIN prestamos.credito c
                ON pmd.id_proceso=c.id
            WHERE pmd.id_pagos_masivos=:id
                    AND pmd.id_estatus_detalle=2
                    AND pmd.blnborrado is false
        ')->bindValues([
            'id' => $this->nomina->id,
        ])->queryRow()['monto_credito_depositar'];
    }

    protected function prestamosAnteriores($key, $field)
    {
        return (new Warp($this->prestamoAnterior()))->flatMap(function ($prestamo) use ($key, $field) {
            return ["{$key}_{$prestamo['id_credito']}" => $prestamo[$field]];
        });
    }

    private function gastoAdministrativoConsolidado()
    {
        return Yii::app()->getDb()->createCommand('
            SELECT sum(c.gastos_administrativo) AS gastos_administrativo
            FROM pagos.pagos_masivos_detalle pmd
            INNER JOIN prestamos.credito c ON pmd.id_proceso=c.id
            WHERE pmd.id_pagos_masivos=:id
                AND pmd.id_estatus_detalle=2
                AND pmd.blnborrado is false
        ')->bindValues([
            'id' => $this->nomina->id,
        ])->queryRow()['gastos_administrativo'];
    }

    private function gastosAdministrativos()
    {
        return Yii::app()->getDb()->createCommand('
            SELECT c.id_tipo_credito as id, sum(c.gastos_administrativo) AS gastos_administrativo
            FROM pagos.pagos_masivos_detalle pmd
            INNER JOIN prestamos.credito c ON pmd.id_proceso=c.id
            WHERE pmd.id_pagos_masivos=:id
                AND pmd.id_estatus_detalle=2
                AND pmd.blnborrado is false
                AND c.gastos_administrativo IS NOT NULL
            GROUP BY c.id_tipo_credito
        ')->bindValues([
            'id' => $this->nomina->id,
        ])->queryAll();
    }

    private function afianzadoras()
    {
        return $this->nomina->afianzadoras();
    }

    private function creditos()
    {
        return Yii::app()->getDb()->createCommand('
            SELECT
                c.id_tipo_credito AS id,
                sum(c.monto_credito) AS monto_base,
                CASE
                    WHEN tcga.descontar is false AND coalesce(sum(c.gastos_administrativo), NULL) IS NOT NULL THEN coalesce(sum(c.gastos_administrativo), 0)
                    ELSE 0
                END AS gastos_administrativos,
                CASE
                    WHEN tc.descontar_grantia_afianzadora is false AND c.id_afianzadora IS NOT NULL THEN COALESCE(SUM(c.gastos_administrativos_afianzadora), 0)
                    ELSE 0
                END AS gastos_administrativos_afianzadora,
                sum(c.monto_credito)
                + CASE
                    WHEN tcga.descontar is false AND coalesce(sum(c.gastos_administrativo), NULL) IS NOT NULL THEN coalesce(sum(c.gastos_administrativo), 0)
                    ELSE 0
                END
                + CASE
                    WHEN tc.descontar_grantia_afianzadora is false AND c.id_afianzadora IS NOT NULL THEN COALESCE(SUM(c.gastos_administrativos_afianzadora), 0)
                    ELSE 0
                END AS monto
            FROM pagos.pagos_masivos_detalle pmd
            INNER JOIN prestamos.credito c ON pmd.id_proceso=c.id
            LEFT JOIN  prestamos.tipo_credito_gastos_administrativo tcga ON tcga.id_tipo_credito=c.id_tipo_credito AND tcga.blnborrado is false
            LEFT JOIN  prestamos.tipo_credito tc ON tc.id=c.id_tipo_credito
            WHERE pmd.id_pagos_masivos=:id
                AND pmd.id_estatus_detalle=2
                AND pmd.blnborrado is false
            GROUP BY c.id_tipo_credito, c.id_afianzadora, tcga.descontar, tc.descontar_grantia_afianzadora;
        ')->bindValues(['id' => $this->nomina->id])->queryAll();
    }

    private function prestamoAnterior()
    {
        if ($this->prestamos_seleccionados == null) {
            $this->prestamos_seleccionados = Yii::app()->getDb()->createCommand("
                SELECT cast(t.datos->>'id_credito' AS integer) AS id_credito,
                       cast(t.datos->>'id_tipo_credito' AS integer) AS id_tipo_credito,
                       cast(t.datos->>'monto' AS numeric) AS monto,
                       cast(t.datos->>'capital_cuotas_no_afectadas' AS numeric) AS capital,
                       cast(t.datos->>'interes_cuotas_no_afectadas' AS numeric) AS interes
                FROM (
                    SELECT json_array_elements(c.prestamos_seleccionados) AS datos
                    FROM pagos.pagos_masivos_detalle pmd
                    INNER JOIN prestamos.credito c ON pmd.id_proceso=c.id
                    WHERE pmd.id_pagos_masivos=:id
                        AND c.prestamos_seleccionados IS NOT NULL
                        AND c.refinanciado IN (1, 2)
                        AND pmd.blnborrado IS FALSE
                        AND pmd.id_estatus_detalle=2
                ) t
            ")->bindValues(['id' => $this->nomina->id])->queryAll();
        }

        if (count($this->prestamos_seleccionados) == 0) {
            return [];
        }

        return $this->prestamos_seleccionados;
    }
}
