<?php
namespace Incoviba\Service\Contabilidad;

use DateTimeInterface;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal\Service;
use Incoviba\Common\Implement;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model;
use Incoviba\Repository;
use Psr\Log\LoggerInterface;

class Movimiento extends Service
{
    public function __construct(LoggerInterface                                      $logger,
                                protected Repository\Contabilidad\Movimiento         $movimientoRepository,
                                protected Repository\Contabilidad\Movimiento\Detalle $detalleRepository,
                                protected Movimiento\Detalle                         $detalleService)
    {
        parent::__construct($logger);
    }

    public function getAll(null|string|array $ordering = null): array
    {
        return array_map([$this, 'process'], $this->movimientoRepository->fetchAll($ordering));
    }
    public function getById(int $movimiento_id): Model\Contabilidad\Movimiento
    {
        return $this->process($this->movimientoRepository->fetchById($movimiento_id));
    }
    public function getAmountStartingFrom(int $start, int $amount): array
    {
        return array_map([$this, 'process'], $this->movimientoRepository->fetchAmountStartingFrom($start, $amount));
    }
    public function getAmountBySociedadAndMes(int $sociedad_rut, DateTimeInterface $mes, ?int $amount): array
    {
        return array_map([$this, 'process'], $this->movimientoRepository->fetchAmountBySociedadAndMes($sociedad_rut, $mes, $amount));
    }
    public function getByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo(int $cuenta_id, DateTimeInterface $fecha, string $glosa, int $cargo, int $abono, int $saldo): Model\Contabilidad\Movimiento
    {
        return $this->process($this->movimientoRepository->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo($cuenta_id, $fecha, $glosa, $cargo, $abono, $saldo));
    }
    public function add(array $data): Model\Contabilidad\Movimiento
    {
        try {
            $movimiento = $this->movimientoRepository->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo($data['cuenta_id'], $data['fecha'], $data['glosa'], $data['cargo'] ?? 0, $data['abono'] ?? 0, $data['saldo']);
        } catch (Implement\Exception\EmptyResult) {
            $data['fecha'] = $data['fecha']->format('Y-m-d');
            $movimiento = $this->movimientoRepository->create($data);
            $movimiento = $this->movimientoRepository->save($movimiento);
        }
        $movimiento = $this->setDetalles($movimiento, $data);
        return $this->process($movimiento);
    }
    public function setDetalles(Model\Contabilidad\Movimiento $movimiento, array $data): Model\Contabilidad\Movimiento
    {
        try {
            $detalles = $this->detalleRepository->fetchByMovimiento($movimiento->id);
            $this->detalleRepository->edit($detalles, $data);
        } catch (Implement\Exception\EmptyResult) {
            $data['movimiento_id'] = $movimiento->id;
            $detalles = $this->detalleRepository->create($data);
            $this->detalleRepository->save($detalles);
        }
        return $movimiento;
    }
    public function edit(Model\Contabilidad\Movimiento $movimiento, array $data): Model\Contabilidad\Movimiento
    {
        try {
            $detalles = $this->detalleRepository->fetchByMovimiento($movimiento->id);
            $mappedData = $this->detalleRepository->filterData($data);
            $this->detalleRepository->edit($detalles, $mappedData);
        } catch (Implement\Exception\EmptyResult) {}
        return $this->process($movimiento);
    }
    public function remove(Model\Contabilidad\Movimiento $movimiento): void
    {
        $this->movimientoRepository->remove($movimiento);
    }

    /**
     * @throws EmptyResult
     */
    public function findRut(int $rut): Define\Model
    {
        return $this->detalleService->findRut($rut);
    }
    public function findMissing(Model\Inmobiliaria\Cuenta $cuenta, array $currentIdList, DateTimeInterface $startDate, DateTimeInterface $endDate): array
    {
        try {
            return $this->movimientoRepository->fetchMissingInDateRange($cuenta->id, $startDate, $endDate, $currentIdList);
        } catch (EmptyResult) {
            return [];
        }
    }

    public function process(Model\Contabilidad\Movimiento $movimiento): Model\Contabilidad\Movimiento
    {
        $movimiento->addFactory('detalles', (new Implement\Repository\Factory())
            ->setCallable(function(int $movimiento_id) {
                try {
                    return $this->detalleService->getByMovimiento($movimiento_id);
                } catch (Implement\Exception\EmptyResult) {
                    return null;
                }
            })
            ->setArgs(['movimiento_id' => $movimiento->id]));
        return $movimiento;
    }
}
