<?php
namespace Incoviba\Repository;

use DateTimeImmutable;
use Incoviba\Common\Ideal;
use Incoviba\Common\Define;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Service;

class Venta extends Ideal\Repository
{
    public function __construct(
        Define\Connection $connection,
        protected Venta\Propietario $propietarioRepository,
        protected Venta\Propiedad $propiedadRepository,
        protected Service\Venta\Pie $pieService,
        protected Venta\BonoPie $bonoPieRepository,
        protected Venta\Credito $creditoRepository,
        protected Venta\Escritura $escrituraRepository,
        protected Venta\Subsidio $subsidioRepository,
        protected Venta\Entrega $entregaRepository,
        protected Service\Venta\Pago $pagoService
    )
    {
        parent::__construct($connection);
        $this->setTable('venta');
    }

    public function create(?array $data = null): Model\Venta
    {
        $map = (new Implement\Repository\MapperParser(['uf']))
            ->register('propietario', (new Implement\Repository\Mapper())
                ->setFactory((new Implement\Repository\Factory())
                    ->setCallable([$this->propietarioRepository, 'fetchById'])
                    ->setArgs([$data['propietario']])))
            ->register('propiedad', (new Implement\Repository\Mapper())
                ->setFactory((new Implement\Repository\Factory())
                    ->setCallable([$this->propiedadRepository, 'fetchById'])
                    ->setArgs([$data['propiedad']])))
            ->register('pie', (new Implement\Repository\Mapper())
                ->setProperty('formaPago')
                ->setFactory((new Implement\Repository\Factory())
                    ->setCallable(function($repositories, $data) {
                        $fp = new Model\Venta\FormaPago();
                        $map = [
                            'pie' => [
                                'service' => $repositories->pieService
                            ],
                            'bono_pie' => [
                                'property' => 'bonoPie',
                                'repository' => $repositories->bonoPieRepository
                            ],
                            'credito' => [
                                'repository' => $repositories->creditoRepository
                            ],
                            'escritura' => [
                                'repository' => $repositories->escrituraRepository
                            ],
                            'subsidio' => [
                                'repository' => $repositories->subsidioRepository
                            ],
                            'devolucion' => [
                                'service' => $repositories->pagoService
                            ]
                        ];
                        foreach ($map as $column => $settings) {
                            if (isset($data[$column]) and $data[$column] !== 0) {
                                if (isset($settings['repository'])) {
                                    $fp->{$settings['property'] ?? $column} = $settings['repository']->fetchById($data[$column]);
                                    continue;
                                }
                                $fp->{$settings['property'] ?? $column} = $settings['service']->getById($data[$column]);
                                continue;
                            }
                            $fp->{$settings['property'] ?? $column} = null;
                        }
                        return $fp;
                    })
                    ->setArgs([(object) [
                        'pieService' => $this->pieService,
                        'bonoPieRepository' => $this->bonoPieRepository,
                        'creditoRepository' => $this->creditoRepository,
                        'escrituraRepository' => $this->escrituraRepository,
                        'subsidioRepository' => $this->subsidioRepository,
                        'pagoService' => $this->pagoService
                    ], $data])))
            /*->register('escriturado', (new Implement\Repository\Mapper())
                ->setFunction(function($data) {
                    return $data['escritura'] !== null;
                }))*/
            /*->register('entrega', (new Implement\Repository\Mapper())
                ->setFactory((new Implement\Repository\Factory())
                    ->setCallable(function($entrega_id) {
                        if ($entrega_id !== null and $entrega_id !== 0) {
                            return $this->entregaRepository->fetchById($entrega_id);
                        }
                        return null;
                    })
                    ->setArgs([$data['entrega']]))
                ->setDefault(null))*/
            /*->register('entregado', (new Implement\Repository\Mapper())
                ->setFactory((new Implement\Repository\Factory())
                    ->setCallable(function($entrega_id) {
                            if ($entrega_id !== null and $entrega_id !== 0) {
                                return $entrega_id != null;
                            }
                            return false;
                        })
                    ->setArgs([$data['entrega']])))*/
            ->register('fecha', new Implement\Repository\Mapper\DateTime('fecha'))
            ->register('valor_uf', (new Implement\Repository\Mapper())
                ->setProperty('valor'))
            //->register('estado')
            ->register('fecha_ingreso', new Implement\Repository\Mapper\DateTime('fecha_ingreso', 'fechaIngreso'))
            //->register('avalchile')
            //->register('agente')
            ->register('relacionado', new Implement\Repository\Mapper\Boolean('relacionado'));
            //->register('promocion')
            //->register('resciliacion')
            //->register('devolucion');
        return $this->parseData(new Model\Venta(), $data, $map);
    }
    public function save(Define\Model $model): Model\Venta
    {
        $model->id = $this->saveNew(
            ['propietario', 'propiedad', 'pie', 'bono_pie', 'credito', 'escritura', 'subsidio', 'escriturado',
                'entrega', 'entregado', 'fecha', 'valor_uf', 'estado', 'fecha_ingreso', 'avalchile', 'agente', 'uf',
                'relacionado', 'promocion', 'resciliacion', 'devolucion'],
            [$model->propietario()->rut, $model->propiedad()->id, $model->formaPago()->pie?->id, $model->formaPago()->bonoPie?->id,
                $model->formaPago()->credito?->id, $model->formaPago()->escritura?->id, $model->formaPago()->subsidio?->id,
                $model->formaPago()->escritura !== null ? $model->formaPago()->escritura->pago->fecha->format('Y-m-d') : null,
                null, null, $model->fecha->format('Y-m-d'), $model->valor, 1, $model->fechaIngreso->format('Y-m-d'),
                null, null, $model->uf, $model->relacionado ? 1 : 0, null, null, null]
        );
        return $model;
    }
    public function edit(Define\Model $model, array $new_data): Model\Venta
    {
        return $this->update($model, ['propietario', 'propiedad', 'pie', 'bono_pie', 'credito', 'escritura', 'subsidio', 'escriturado',
            'entrega', 'entregado', 'fecha', 'valor_uf', 'estado', 'fecha_ingreso', 'avalchile', 'agente', 'uf',
            'relacionado', 'promocion', 'resciliacion', 'devolucion'],  $new_data);
    }

    public function fetchByProyecto(int $proyecto_id): array
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propiedad_unidad` pu ON pu.`propiedad` = a.`propiedad`
    JOIN `unidad` ON `unidad`.`id` = pu.`unidad` AND pu.`principal` = 1
    JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt`
    JOIN (SELECT e1.* FROM `estado_venta` e1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) e0 ON e0.`id` = e1.`id`) ev ON ev.`venta` = a.`id`
    JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado`
WHERE ptu.`proyecto` = ? AND tev.`activa`
GROUP BY a.`id`";
        return $this->fetchMany($query, [$proyecto_id]);
    }
    public function fetchActivaByProyecto(int $proyecto_id): array
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propiedad_unidad` pu ON pu.`propiedad` = a.`propiedad`
    JOIN `unidad` ON `unidad`.`id` = pu.`unidad` AND pu.`principal` = 1
    JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt`
    JOIN (SELECT e1.* FROM `estado_venta` e1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) e0 ON e0.`id` = e1.`id`) ev ON ev.`venta` = a.`id`
    JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado`
WHERE ptu.`proyecto` = ? AND tev.`activa`
GROUP BY a.`id`";
        return $this->fetchMany($query, [$proyecto_id]);
    }
    public function fetchByProyectoAndUnidad(string $proyecto_nombre, int $unidad_descripcion): Model\Venta
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propiedad_unidad` pu ON pu.`propiedad` = a.`propiedad`
    JOIN `unidad` ON `unidad`.`id` = pu.`unidad` AND pu.`principal` = 1
    JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt`
    JOIN `proyecto` ON `proyecto`.`id` = ptu.`proyecto`
    JOIN (SELECT e1.* FROM `estado_venta` e1 JOIN (SELECT MAX(`id`) AS 'id', `venta` FROM `estado_venta` GROUP BY `venta`) e0 ON e0.`id` = e1.`id`) ev ON ev.`venta` = a.`id`
    JOIN `tipo_estado_venta` tev ON tev.`id` = ev.`estado`
WHERE `proyecto`.`descripcion` = ? AND `unidad`.`descripcion` = ? AND tev.`activa`";
        return $this->fetchOne($query, [$proyecto_nombre, $unidad_descripcion]);
    }
    public function fetchByPie(int $pie_id): Model\Venta
    {
        $query = "SELECT * FROM `{$this->getTable()}` WHERE `pie` = ?";
        return $this->fetchOne($query, [$pie_id]);
    }
    public function fetchByUnidad(string $unidad, string $tipo): array
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propiedad_unidad` pu ON pu.`propiedad` = a.`propiedad`
    JOIN `unidad` ON `unidad`.`id` = pu.`unidad`
    JOIN `proyecto_tipo_unidad` ptu ON ptu.`id` = `unidad`.`pt`
    JOIN `tipo_unidad` tu ON tu.`id` = ptu.`tipo`
WHERE `unidad`.`descripcion` LIKE ? AND tu.`descripcion` = ?";
        return $this->fetchMany($query, [$unidad, $tipo]);
    }
    public function fetchByPrecio(string $precio): array
    {
        $query = "SELECT * FROM `{$this->getTable()}` WHERE `valor_uf` = ?";
        return $this->fetchMany($query, [$precio]);
    }
    public function fetchByPropietario(string $propietario): array
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propietario` ON `propietario`.`rut` = a.`propietario`
WHERE CONCAT_WS('-', `propietario`.`rut`, `propietario`.`dv`) LIKE :propietario OR `propietario`.`nombres` LIKE :propietario
    OR `propietario`.`apellido_paterno` LIKE :propietario OR `propietario`.`apellido_materno` LIKE :propietario
    OR CONCAT_WS(' ', `propietario`.`nombres`, `propietario`.`apellido_paterno`, `propietario`.`apellido_materno`) LIKE :propietario";
        return $this->fetchMany($query, [':propietario' => "%{$propietario}%"]);
    }
    public function fetchByPropietarioNombreCompleto(string $propietario): array
    {
        $query = "SELECT a.*
FROM `{$this->getTable()}` a
    JOIN `propietario` ON `propietario`.`rut` = a.`propietario`
WHERE CONCAT_WS(' ', `propietario`.`nombres`, `propietario`.`apellido_paterno`, `propietario`.`apellido_materno`) LIKE ?";
        return $this->fetchMany($query, [$propietario]);
    }
}
