<?php
namespace Incoviba\Controller\API;

use PDOException;
use DateTimeImmutable;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Incoviba\Common\Ideal\Controller;
use Incoviba\Common\Implement\Exception\EmptyRedis;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Controller\withRedis;
use Incoviba\Exception\ServiceAction\Read;
use Incoviba\Model;
use Incoviba\Repository;
use Incoviba\Service;

class Ventas extends Controller
{
    use withJson, withRedis;

    public function proyecto(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                             Repository\Proyecto    $proyectoRepository, Repository\Venta $ventaRepository): ResponseInterface
    {
        $body = $request->getBody();
        $json = json_decode($body->getContents());
        $proyecto_id = $json->proyecto_id;
        $output = [
            'proyecto' => [
                'id' => $proyecto_id
            ],
            'total' => 0
        ];
        $proyectosKey = "proyectos";
        try {
            $proyectos = $this->fetchRedis($redisService, $proyectosKey);
        } catch (EmptyRedis) {
            $proyectos = $proyectoRepository->fetchAllActive();
            $this->saveRedis($redisService, $proyectosKey, $proyectos);
        }
        $proyecto = array_values(array_filter($proyectos, function($proyecto) use ($proyecto_id) {return $proyecto->id === $proyecto_id;}))[0];
        $output['proyecto']['descripcion'] = $proyecto->descripcion;

        $redisKey = "ventas:proyecto:{$proyecto_id}";
        try {
            $output['ventas'] = $this->fetchRedis($redisService, $redisKey);
            $output['total'] = count($output['ventas']);
        } catch (EmptyRedis) {
            try {
                $ventas = $ventaRepository->fetchActivaByProyecto($proyecto_id);
                $output['ventas'] = array_map(function(Model\Venta $venta) {return $venta->id;}, $ventas);
                $output['total'] = count($ventas);
                $this->saveRedis($redisService, $redisKey, $output['ventas']);
            } catch (EmptyResult) {}
        }
        return $this->withJson($response, $output);
    }
    public function get(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                        Service\Venta $service, int $venta_id): ResponseInterface
    {
        $redisKey = "venta:{$venta_id}";
        $output = [
            'venta' => null
        ];
        try {
            $venta = $this->fetchRedis($redisService, $redisKey);
            $output = compact('venta');
        } catch (EmptyRedis) {
            try {
                $venta = $service->getById($venta_id);
                $output = compact('venta');
                $this->saveRedis($redisService, $redisKey, $venta);
            } catch (EmptyResult $exception) {
                $this->logger->notice($exception);
            }
        }
        return $this->withJson($response, $output);
    }
    public function getMany(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                        Service\Venta $service): ResponseInterface
    {
        $body = $request->getParsedBody();
        $output = [
            'input' => $body,
            'ventas' => []
        ];
        $ventas = explode(',', $body['ventas']);
        foreach ($ventas as $venta_id) {
            $redisKey = "venta:{$venta_id}";
            try {
                $venta = $this->fetchRedis($redisService, $redisKey);
                $output['ventas'] []= $venta;
            } catch (EmptyRedis) {
                try {
                    $venta = $service->getById($venta_id);
                    $output['ventas'] []= $venta;
                    $this->saveRedis($redisService, $redisKey, $venta);
                } catch (Read $exception) {
                    $this->logger->notice($exception);
                }
            }
        }
        return $this->withJson($response, $output);
    }
    public function porFirmar(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService, Service\Redis $redisService): ResponseInterface
    {
        $body = $request->getBody();
        $json = json_decode($body->getContents());
        $proyecto_id = $json->proyecto_id;
        $today = new DateTimeImmutable();
        $redisKey = "promesas:por_firmar:proyecto:{$proyecto_id}:{$today->format('Y-m-d')}";

        $output = [
            'proyecto_id' => $proyecto_id,
            'promesas' => 0
        ];
        try {
            $output = $this->fetchRedis($redisService, $redisKey);
        } catch (EmptyRedis) {
            try {
                $ventas = $ventaService->getByProyecto($proyecto_id);
                $promesas = array_filter($ventas, function(Model\Venta $venta) {
                    return $venta->currentEstado()->tipoEstadoVenta->descripcion === 'vigente';
                });
                $output['promesas'] = count($promesas);
                $this->saveRedis($redisService, $redisKey, $output);
            } catch (EmptyResult) {}
        }
        return $this->withJson($response, $output);
    }
    public function escrituras(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService, Service\Venta\Pago $pagoService, Service\Redis $redisService): ResponseInterface
    {
        $body = $request->getBody();
        $json = json_decode($body->getContents());
        $proyecto_id = $json->proyecto_id;
        $today = new DateTimeImmutable();
        $redisKey = "escrituras:proyecto:{$proyecto_id}:{$today->format('Y-m-d')}";

        $output = [
            'proyecto_id' => $proyecto_id,
            'escrituras' => [
                'firmar' => 0,
                'pagar' => 0,
                'abonar' => 0
            ]
        ];
        try {
            $output = $this->fetchRedis($redisService, $redisKey);
        } catch (EmptyRedis) {
            try {
                $ventas = $ventaService->getEscriturasByProyecto($proyecto_id);
                $porFirmar = array_filter($ventas, function(Model\Venta $venta) {
                    return $venta->currentEstado()->tipoEstadoVenta->descripcion === 'escriturando';
                });
                $output['escrituras']['firmar'] = count($porFirmar);
                unset($porFirmar);
                $escrituras = array_filter($ventas, function(Model\Venta $venta) {
                    return $venta->currentEstado()->tipoEstadoVenta->descripcion === 'firmado por inmobiliaria';
                });
                unset($ventas);
                $porPagar = array_filter($escrituras, function(Model\Venta $venta) use ($pagoService) {
                    $pagos = $pagoService->getByVenta($venta->id);
                    $porPagar = array_filter($pagos, function(Model\Venta\Pago $pago) {
                        return $pago->currentEstado->tipoEstadoPago->descripcion === 'no pagado';
                    });
                    return count($porPagar) > 0;
                });
                $output['escrituras']['pagar'] = count($porPagar);
                unset($porPagar);
                $porAbonar = array_filter($escrituras, function(Model\Venta $venta) use ($pagoService) {
                    $pagos = $pagoService->getByVenta($venta->id);
                    $porPagar = array_filter($pagos, function(Model\Venta\Pago $pago) {
                        return $pago->currentEstado->tipoEstadoPago->descripcion === 'depositado';
                    });
                    return count($porPagar) > 0;
                });
                $output['escrituras']['abonar'] = count($porAbonar);
                unset($porAbonar);
                $this->saveRedis($redisService, $redisKey, $output);
            } catch (EmptyResult) {}
        }
        return $this->withJson($response, $output);
    }
    public function comentarios(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                                Service\Venta $service, Repository\Venta\Comentario $comentarioRepository, int $venta_id): ResponseInterface
    {
        $venta = $service->getById($venta_id);
        $output = ['total' => 0, 'comentarios' => []];
        $redisKey = "comentarios:venta:{$venta_id}";
        try {
            $output['comentarios'] = $this->fetchRedis($redisService, $redisKey);
            $output['total'] = count($output['comentarios']);
        } catch (EmptyRedis) {
            try {
                $comentarios = $comentarioRepository->fetchActiveByVenta($venta->id);
                $output['total'] = count($comentarios);
                $output['comentarios'] = $comentarios;
                $this->saveRedis($redisService, $redisKey, $output['comentarios']);
            } catch (EmptyResult) {}
        }
        return $this->withJson($response, $output);
    }
    public function add(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                        Service\Venta $ventaService): ResponseInterface
    {
        $data = $request->getParsedBody();
        $output = [
            'status' => false,
            'venta_id' => null,
            'errors' => []
        ];
        try {
            $venta = $ventaService->add($data);
            $this->saveRedis($redisService, "venta:{$venta->id}", $venta);
            $output['venta_id'] = $venta->id;
            $output['status'] = true;
        } catch (EmptyResult | PDOException $exception) {
            $output['errors'] = [
                'code' => $exception->getCode(),
                'message' => $exception->getMessage(),
                'stack' => $exception->getTraceAsString()
            ];
        }
        return $this->withJson($response, $output);
    }
    public function edit(ServerRequestInterface $request, ResponseInterface $response, Repository\Venta $ventaRepository, int $venta_id): ResponseInterface
    {
        $body = $request->getParsedBody();
        $output = [
            'venta_id' => $venta_id,
            'input' => $body,
            'edited' => false
        ];
        try {
            $venta = $ventaRepository->fetchById($venta_id);
            $body['valor_uf'] = $body['valor'];
            $body['fecha'] = (new DateTimeImmutable($body['fecha']))->format('Y-m-d');
            $ventaRepository->edit($venta, $body);
            $output['edited'] = true;
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }
    public function unidades(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService,
                             Service\Venta\Unidad $unidadService, int $venta_id): ResponseInterface
    {
        $output = [
            'venta_id' => $venta_id,
            'unidades' => []
        ];
        $redisKey = "unidades:venta:{$venta_id}";
        try {
            $output['unidades'] = $this->fetchRedis($redisService, $redisKey);
        } catch (EmptyRedis) {
            try {
                $unidades = $unidadService->getByVenta($venta_id);
                $output['unidades'] = json_decode(json_encode($unidades));
                array_walk($output['unidades'], function($unidad, $index, $unidades) {
                    $unidad->prorrateo = $unidades[$index]->prorrateo;
                    $unidad->precios = $unidades[$index]->precios;
                    $unidad->current_precio = $unidades[$index]->currentPrecio;
                }, $unidades);
                $this->saveRedis($redisService, $redisKey, $output['unidades']);
            } catch (EmptyResult) {}
        }
        return $this->withJson($response, $output);
    }
    public function escriturar(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService, int $venta_id): ResponseInterface
    {
        $data = $request->getParsedBody();
        $output = [
            'input' => $data,
            'venta_id' => $venta_id,
            'status' => false
        ];
        try {
            $venta = $ventaService->getById($venta_id);
            $output['status'] = $ventaService->escriturar($venta, $data);
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }
    public function desistir(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService, int $venta_id): ResponseInterface
    {
        $data = $request->getParsedBody();
        $output = [
            'input' => $data,
            'venta_id' => $venta_id,
            'desistida' => false
        ];
        try {
            $venta = $ventaService->getById($venta_id);
            $output['desistida'] = $ventaService->desistir($venta, $data);
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }
    public function insistir(ServerRequestInterface $request, ResponseInterface $response, Service\Venta $ventaService, int $venta_id): ResponseInterface
    {
        $output = [
            'venta_id' => $venta_id,
            'eliminado' => false
        ];
        try {
            $venta = $ventaService->getById($venta_id);
            $output['eliminado'] = $ventaService->insistir($venta);
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }
    public function propietario(ServerRequestInterface    $request, ResponseInterface $response,
                                Service\Venta\Propietario $propietarioService,
                                Repository\Venta          $ventaRepository, int $venta_id): ResponseInterface
    {
        $body = $request->getBody();
        $data = json_decode($body->getContents(), true);
        $output = [
            'input' => $data,
            'venta_id' => $venta_id,
            'propietario' => false,
            'edited' => false
        ];
        try {
            $venta = $ventaRepository->fetchById($venta_id);
            $propietario = $propietarioService->getByRut($venta->propietario()->rut);
            $output['propietario'] = $propietario;
            if (isset($data['rut'])) {
                try {
                    $propietario = $propietarioService->getByRut($data['rut']);
                    $propietario = $propietarioService->edit($propietario, $data);
                } catch (EmptyResult) {
                    $propietario = $propietarioService->addPropietario($data);
                }
                $data = [
                    'propietario' => $propietario->rut
                ];
                $ventaRepository->edit($venta, $data);
            } else {
                $propietario = $propietarioService->edit($propietario, $data);
            }
            $output['edited'] = true;
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }

    public function addComentario(ServerRequestInterface $request, ResponseInterface $response,
                                  Repository\Venta $ventaRepository, Repository\Venta\Comentario $comentarioRepository,
                                  Service\Redis $redisService,
                                  int $venta_id): ResponseInterface
    {
        $body = $request->getParsedBody();
        $output = [
            'venta_id' => $venta_id,
            'input' => $body,
            'comentario' => null,
            'added' => false
        ];
        try {
            $venta = $ventaRepository->fetchById($venta_id);
            $body['venta'] = $venta->id;
            $body['estado'] = true;
            $filteredData = $comentarioRepository->filterData($body);
            try {
                $comentarioRepository->fetchByVentaAndFechaAndTexto($venta_id, new DateTimeImmutable($filteredData['fecha']), $filteredData['texto']);
            } catch (EmptyResult) {
                $redisKey = "comentarios:venta:{$venta_id}";
                $comentarios = $this->fetchRedis($redisService, $redisKey);
                $comentario = $comentarioRepository->create($filteredData);
                $output['comentario'] = $comentarioRepository->save($comentario);
                $comentarios []= $comentario;
                $this->saveRedis($redisService, $redisKey, $comentarios);
            }
            $output['added'] = true;
        } catch (EmptyResult) {}
        return $this->withJson($response, $output);
    }
    public function byUnidades(ServerRequestInterface $request, ResponseInterface $response,
                               Service\Venta $ventaService, Service\Venta\Unidad $unidadService): ResponseInterface
    {
        $input = $request->getParsedBody();
        $output = [
            'input' => $input,
            'ventas' => []
        ];
        try {
            $output['ventas'] = $ventaService->getActiveByUnidadIds($input['unidad_ids']);
        } catch (Read) {}
        return $this->withJson($response, $output);
    }
}
