<?php
namespace Incoviba\Service\Venta;

use DateTimeInterface;
use DateTimeImmutable;
use DateMalformedStringException;
use Incoviba\Exception\ServiceAction\Read;
use Incoviba\Exception\ServiceAction\Update;
use PDOException;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Repository;
use Incoviba\Model;
use Incoviba\Service;

class Pago
{
    public function __construct(
        protected Repository\Venta\Pago $pagoRepository,
        protected Repository\Venta\EstadoPago $estadoPagoRepository,
        protected Repository\Venta\TipoEstadoPago $tipoEstadoPagoRepository,
        protected Service\UF $ufService,
        protected Service\Valor $valorService
    ) {}

    public function depositar(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool
    {
        $tipo_estado = $this->tipoEstadoPagoRepository->fetchByDescripcion('depositado');
        $data = [
            'pago' => $pago->id,
            'estado' => $tipo_estado->id,
            'fecha' => $fecha->format('Y-m-d')
        ];
        try {
            $estado = $this->estadoPagoRepository->create($data);
            $this->estadoPagoRepository->save($estado);
            $pago = $this->process($this->pagoRepository->fetchById($pago->id));
            $this->getUF($pago);
            return true;
        } catch (PDOException) {
            return false;
        }
    }
    public function abonar(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool
    {
        $tipo_estado = $this->tipoEstadoPagoRepository->fetchByDescripcion('abonado');
        $data = [
            'pago' => $pago->id,
            'estado' => $tipo_estado->id,
            'fecha' => $fecha->format('Y-m-d')
        ];
        try {
            $estado = $this->estadoPagoRepository->create($data);
            $this->estadoPagoRepository->save($estado);
            return true;
        } catch (PDOException) {
            return false;
        }
    }
    public function devolver(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool
    {
        $tipo_estado = $this->tipoEstadoPagoRepository->fetchByDescripcion('devuelto');
        $data = [
            'pago' => $pago->id,
            'estado' => $tipo_estado->id,
            'fecha' => $fecha->format('Y-m-d')
        ];
        try {
            $estado = $this->estadoPagoRepository->create($data);
            $this->estadoPagoRepository->save($estado);
            return true;
        } catch (PDOException) {
            return false;
        }
    }
    public function anular(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool
    {
        $tipo_estado = $this->tipoEstadoPagoRepository->fetchByDescripcion('anulado');
        $data = [
            'pago' => $pago->id,
            'estado' => $tipo_estado->id,
            'fecha' => $fecha->format('Y-m-d')
        ];
        try {
            $estado = $this->estadoPagoRepository->create($data);
            $this->estadoPagoRepository->save($estado);
            return true;
        } catch (PDOException) {
            return false;
        }
    }
    public function getById(?int $pago_id): ?Model\Venta\Pago
    {
        if ($pago_id === null) {
            return null;
        }
        $pago = $this->pagoRepository->fetchById($pago_id);
        return $this->process($pago);
    }

    public function getByVenta(int $venta_id): array
    {
        return array_map([$this, 'process'], $this->pagoRepository->fetchByVenta($venta_id));
    }

    public function getPendientes(): array
    {
        return [];
    }
    public function getDepositados(): array
    {
        return [];
    }
    public function getRebotes(): array
    {
        return [];
    }

    /**
     * @param int $venta_id
     * @return Model\Venta\Pago
     * @throws Read
     */
    public function getDevolucionByVenta(int $venta_id): Model\Venta\Pago
    {
        try {
            return $this->process($this->pagoRepository->fetchDevolucionByVenta($venta_id));
        } catch (EmptyResult $exception) {
            throw new Read(__CLASS__, $exception);
        }
    }

    public function add(array $data): Model\Venta\Pago
    {
        if (array_key_exists('fecha', $data)) {
            try {
                $fecha = new DateTimeImmutable($data['fecha']);
            } catch (DateMalformedStringException) {
                $fecha = new DateTimeImmutable();
            }
            $data['fecha'] = $fecha->format('Y-m-d');
            if (!array_key_exists('uf', $data)) {
                $data['uf'] = $this->ufService->get($fecha);
            }
        }
        $data['valor'] = $this->valorService->toPesos($this->valorService->clean($data['valor']), $data['fecha']);

        $filtered_data = $this->pagoRepository->filterData($data);

        $pago = $this->pagoRepository->create($filtered_data);
        $pago = $this->pagoRepository->save($pago);

        $tipoEstado = $this->tipoEstadoPagoRepository->fetchByDescripcion('no pagado');
        $estado = $this->estadoPagoRepository->create([
            'pago' => $pago->id,
            'fecha' => $pago->fecha->format('Y-m-d'),
            'estado' => $tipoEstado->id
        ]);
        $estado = $this->estadoPagoRepository->save($estado);
        $pago->currentEstado = $estado;
        return $pago;
    }

    /**
     * @param Model\Venta\Pago $pago
     * @param array $data
     * @return Model\Venta\Pago
     * @throws Update
     */
    public function edit(Model\Venta\Pago $pago, array $data): Model\Venta\Pago
    {
        if (array_key_exists('fecha', $data)) {
            try {
                $fecha = new DateTimeImmutable($data['fecha']);
            } catch (DateMalformedStringException) {
                $fecha = new DateTimeImmutable();
            }
            $data['fecha'] = $fecha->format('Y-m-d');
            if (!array_key_exists('uf', $data)) {
                $data['uf'] = $this->ufService->get($fecha);
            }
        }
        $filteredData = $this->pagoRepository->filterData($data);
        if (array_key_exists('valor', $filteredData)) {
            $filteredData['valor'] = $this->valorService->toPesos($this->valorService->clean($filteredData['valor']), $filteredData['fecha']);
        }
        try {
            $pago = $this->pagoRepository->edit($pago, $filteredData);
        } catch (PDOException | EmptyResult $exception) {
            throw new Update(__CLASS__, $exception);
        }
        $pago->currentEstado = $this->estadoPagoRepository->fetchCurrentByPago($pago->id);
        return $pago;
    }
    public function delete(Model\Venta\Pago $pago): bool
    {
        try {
            $this->pagoRepository->remove($pago);
            return true;
        } catch (PDOException) {
            return false;
        }
    }
    public function updateEstado(Model\Venta\Pago $pago, array $data): Model\Venta\Pago
    {
        if ($pago->currentEstado->tipoEstadoPago->id === $data['estado']) {
            return $pago;
        }

        $data['pago'] = $pago->id;
        $estado = $this->estadoPagoRepository->create($data);
        $this->estadoPagoRepository->save($estado);

        return $this->process($this->pagoRepository->fetchById($pago->id));
    }

    protected function process($pago): Model\Venta\Pago
    {
        $pago->estados = $this->estadoPagoRepository->fetchByPago($pago->id);
        $pago->currentEstado = $this->estadoPagoRepository->fetchCurrentByPago($pago->id);
        if ($pago->uf === null or $pago->uf === 0.0) {
            $pago->uf = $this->getUF($pago);
        }
        return $pago;
    }
    protected function getUF(Model\Venta\Pago $pago): ?float
    {
        if (in_array($pago->currentEstado->tipoEstadoPago->descripcion, ['depositado', 'abonado'])
            and $pago->currentEstado->fecha <= new DateTimeImmutable()) {
            $uf = $this->ufService->get($pago->currentEstado->fecha);
            if ($pago->uf === $uf) {
                return $uf;
            }
            if ($uf !== 0.0) {
                try {
                    $this->pagoRepository->edit($pago, ['uf' => $uf]);
                } catch (EmptyResult) {}
                return $uf;
            }
        } elseif ($pago->uf === 0.0) {
            try {
                $this->pagoRepository->edit($pago, ['uf' => null]);
            } catch (EmptyResult) {}
            return null;
        }
        return $pago->uf;
    }
}
