<?php
namespace Incoviba\Service\Contabilidad;

use DateTimeImmutable;
use DateTimeInterface;
use Incoviba\Common\Define\Cartola\Banco;
use Incoviba\Common\Define\Contabilidad\Exporter;
use Incoviba\Common\Ideal\Service;
use Incoviba\Common\Implement\Exception;
use Incoviba\Model;
use Incoviba\Repository;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Log\LoggerInterface;

class Cartola extends Service
{
    public function __construct(LoggerInterface                              $logger,
                                protected StreamFactoryInterface             $streamFactory, protected Exporter $exporter,
                                protected Repository\Inmobiliaria            $inmobiliariaRepository,
                                protected Repository\Inmobiliaria\Cuenta     $cuentaRepository,
                                protected Repository\Contabilidad\Movimiento $movimientoRepository,
                                protected Movimiento                         $movimientoService,
                                protected Repository\Contabilidad\Cartola    $cartolaRepository) {
        parent::__construct($logger);
    }

    protected array $bancos;
    public function register(string $name, Banco $banco): Cartola
    {
        $this->bancos[$name] = $banco;
        return $this;
    }
    public function process(Model\Contabilidad\Banco $banco, UploadedFileInterface $file): array
    {
        return $this->bancos[strtolower($banco->nombre)]->process($file);
    }
    public function export(Model\Inmobiliaria $inmobiliaria, Model\Contabilidad\Banco $banco, DateTimeInterface $mes, array $movimientos): string
    {
        return $this->exporter->export($inmobiliaria, $banco, $mes, $movimientos);
    }
    public function diaria(Model\Inmobiliaria\Cuenta $cuenta, DateTimeInterface $fecha, UploadedFileInterface $file): array
    {
        $ms = $this->getMovimientosDiarios($cuenta->banco, $file);

        $this->groupedMovimientos = [];
        $movimientos = [];
        foreach ($ms as $m) {
            $movimiento = $this->buildMovimiento($cuenta, $m);
            $movimiento = $this->movimientoService->process($movimiento);
            $this->groupMovimientoByDay($movimiento);
            if ($movimiento->fecha->getTimestamp() === $fecha->getTimestamp()) {
                $movimientos []= $movimiento;
            }
        }
        foreach ($this->groupedMovimientos as $timestamp => $movimientos) {
            $cartolaData = [
                'cargos' => 0,
                'abonos' => 0,
                'saldo' => end($movimientos)->saldo
            ];
            foreach ($movimientos as $movimiento) {
                $cartolaData['cargos'] += $movimiento->cargo;
                $cartolaData['abonos'] += $movimiento->abono;
            }
            $this->buildCartola($cuenta, end($movimientos)->fecha, $cartolaData);
        }
        $cartola = $this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $fecha);
        return compact('cartola', 'movimientos');
    }
    public function diariaManual(Model\Inmobiliaria\Cuenta $cuenta, DateTimeInterface $fecha, array $data): array
    {
        $cartolaData = [
            'cargos' => 0,
            'abonos' => 0,
            'saldos' => 0
        ];
        $movimientos = [];
        foreach ($data as $row) {
            $dataMovimiento = $row;
            $dataMovimiento['fecha'] = $fecha->format('Y-m-d');
            $dataMovimiento['documento'] = '';
            $movimiento = $this->buildMovimiento($cuenta, $dataMovimiento);
            $movimiento = $this->movimientoService->process($movimiento);

            $movimientos []= $movimiento;
            $cartolaData['cargos'] += $movimiento->cargo;
            $cartolaData['abonos'] += $movimiento->abono;
            $cartolaData['saldo'] = $movimiento->saldo;
        }
        $cartola = $this->buildCartola($cuenta, $fecha, $cartolaData);
        return compact('cartola', 'movimientos');
    }
    public function import(int $cuenta_id, UploadedFileInterface $file): array
    {
        $cuenta = $this->cuentaRepository->fetchById($cuenta_id);
        $movimientos = $this->process($cuenta->banco, $file);

        foreach ($movimientos as $dataMovimiento) {
            $dataMovimiento['cuenta_id'] = $cuenta->id;
            if (array_key_exists('centro_costo', $dataMovimiento)) {
                $dataMovimiento['centro_costo_id'] = $dataMovimiento['centro_costo'];
            }
            $dataMovimiento['fecha'] = new DateTimeImmutable($dataMovimiento['fecha']);
            if (array_key_exists('rut', $dataMovimiento)) {
                list($rut, $digito) = explode('-', $dataMovimiento['rut']);
                $dataMovimiento['rut'] = trim(preg_replace('/\D+/', '', $rut));
                $dataMovimiento['digito'] = trim($digito);
            }
            $this->movimientoService->add($dataMovimiento);
        }

        $inmobiliaria = $cuenta->inmobiliaria;
        return array_map(function($movimiento) use ($inmobiliaria) {
            $movimiento['sociedad'] = $inmobiliaria;
            return $movimiento;
        }, $movimientos);
    }

    protected function getMovimientosDiarios(Model\Contabilidad\Banco $banco, UploadedFileInterface $file): array
    {
        $movimientos = $this->bancos[strtolower($banco->nombre)]->process($file);
        return $this->bancos[strtolower($banco->nombre)]->processMovimientosDiarios($movimientos);
    }
    protected array $groupedMovimientos = [];
    protected function groupMovimientoByDay(Model\Contabilidad\Movimiento $movimiento): Cartola
    {
        if (!isset($this->groupedMovimientos[$movimiento->fecha->getTimestamp()])) {
            $this->groupedMovimientos[$movimiento->fecha->getTimestamp()] = [];
        }
        $this->groupedMovimientos[$movimiento->fecha->getTimestamp()] []= $movimiento;
        return $this;
    }
    protected function buildCartola(Model\Inmobiliaria\Cuenta $cuenta, DateTimeInterface $fecha, array $data): Model\Contabilidad\Cartola
    {
        try {
            return $this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $fecha);
        } catch (Exception\EmptyResult) {
            $data['cuenta_id'] = $cuenta->id;
            $data['fecha'] = $fecha->format('Y-m-d');
            $cartola = $this->cartolaRepository->create($data);
            return $this->cartolaRepository->save($cartola);
        }
    }
    protected function buildMovimiento(Model\Inmobiliaria\Cuenta $cuenta, array $data): Model\Contabilidad\Movimiento
    {
        try {
            return $this->movimientoRepository
                ->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo(
                    $cuenta->id,
                    new DateTimeImmutable($data['fecha']),
                    $data['glosa'],
                    $data['cargo'] ?? 0,
                    $data['abono'] ?? 0,
                    $data['saldo']
                );
        } catch (Exception\EmptyResult $exception) {
            $data['cuenta_id'] = $cuenta->id;
            $movimiento = $this->movimientoRepository->create($data);
            try {
                return $this->movimientoRepository->save($movimiento);
            } catch (\PDOException $exception) {
                $this->logger->critical(var_export($data,true));
                throw $exception;
            }
        }
    }
}
