<?php
namespace Incoviba\Service;

use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Repository;
use Incoviba\Model;

class Search
{
    public function __construct(protected Venta $ventaService, protected Repository\Venta $ventaRepository, protected Repository\Venta\Unidad $unidadRepository, protected Repository\Venta\TipoUnidad $tipoUnidadRepository) {}

    public function query(string $query, string $tipo): array
    {
        if ($tipo === '*') {
            $results = $this->findCualquiera($query);
        } else {
            $results = $this->find($query, $tipo);
        }
        return $this->sort($results);
    }

    protected function findCualquiera(string $query): array
    {
        $tipos = [
            'departamento',
            'estacionamiento',
            'bodega',
            'propietario',
            'precio_venta',
            'proyecto',
            'pago',
            'unidad'
        ];
        $results = [];
        foreach ($tipos as $t) {
            $this->add($results, $this->find($query, $t));
        }
        return $results;
    }
    protected function find(string $query, string $tipo): array
    {
        $queries = explode(' ', $query);
        $tiposUnidades = $this->getTiposUnidades();
        $results = [];
        foreach ($queries as $q) {
            $this->add($results, $this->findVentas($q, $tipo));
            if (in_array($tipo, $tiposUnidades)) {
                $this->add($results, $this->findUnidadesDisponibles($q, $tipo));
            }
        }
        return $results;
    }
    protected function findVentas(string $query, string $tipo): array
    {
        $tiposUnidades = $this->getTiposUnidades();
        if ($tipo === 'unidad') {
            $results = [];
            foreach ($tiposUnidades as $t) {
                $this->add($results, $this->findVentas($query, $t));
            }
            return $results;
        }
        if (in_array($tipo, $tiposUnidades)) {
            return $this->findUnidad($query, $tipo);
        }
        if ($tipo === 'propietario') {
            return $this->findPropietario($query);
        }
        if ($tipo === 'precio_venta') {
            return $this->findPrecio($query);
        }
        if ($tipo === 'proyecto') {
            return $this->findProyecto($query);
        }
        if ($tipo === 'pago') {
            return $this->findPago($query);
        }
        return [];
    }
    protected function findUnidadesDisponibles(string $query, string $tipo): array
    {
        try {
            return $this->unidadRepository->fetchDisponiblesByDescripcionAndTipo($query, $tipo);
        } catch (EmptyResponse) {
            return [];
        }
    }
    protected function findUnidad(string $query, string $tipo): array
    {
        try {
            return $this->ventaService->getByUnidad($query, $tipo);
        } catch (EmptyResult) {
            return [];
        }
    }
    protected function findPropietario(string $query): array
    {
        try {
            return $this->ventaService->getByPropietario($query);
        } catch (EmptyResult) {
            return [];
        }
    }
    protected function findPrecio(string $query): array
    {
        try {
            $precio = str_replace(['$', '.', ','], ['', '', '.'], $query);
            return $this->ventaService->getByPrecio($precio);
        } catch (EmptyResult) {
            return [];
        }
    }
    protected function findProyecto(string $query): array
    {
        try {
            return $this->ventaService->getByProyecto($query);
        } catch (EmptyResult) {
            return [];
        }
    }
    protected function findPago(string $query): array
    {
        return [];
    }

    protected array $tipos;
    protected function getTiposUnidades(): array
    {
        if (!isset($this->tipos)) {
            $this->tipos = array_map(function(Model\Venta\TipoUnidad $tipoUnidad) {
                return $tipoUnidad->descripcion;
            }, $this->tipoUnidadRepository->fetchAll());
        }
        return $this->tipos;
    }
    protected function add(array &$results, array $found): void
    {
        foreach ($found as $item) {
            if (!$this->inResults($item, $results)) {
                $results []= $item;
            }
        }
    }
    protected function inResults($item, array $results): bool
    {
        foreach ($results as $result) {
            if (get_class($item) === get_class($result) and $item->id === $result->id) {
                return true;
            }
        }
        return false;
    }
    protected function sort(&$results): array
    {
        usort($results, function($a, $b) {
            if (is_a($a, Model\Venta::class)) {
                $pa = $a->proyecto()->descripcion;
                $ta = $a->propiedad()->departamentos()[0]->proyectoTipoUnidad->tipoUnidad->descripcion;
                $ua = $a->propiedad()->departamentos()[0]->descripcion;
            } else {
                $pa = $a->proyectoTipoUnidad->proyecto->descripcion;
                $ta = $a->proyectoTipoUnidad->tipoUnidad->descripcion;
                $ua = $a->descripcion;
            }
            if (is_a($b, Model\Venta::class)) {
                $pb = $b->proyecto()->descripcion;
                $tb = $b->propiedad()->departamentos()[0]->proyectoTipoUnidad->tipoUnidad->descripcion;
                $ub = $b->propiedad()->departamentos()[0]->descripcion;
            } else {
                $pb = $b->proyectoTipoUnidad->proyecto->descripcion;
                $tb = $b->proyectoTipoUnidad->tipoUnidad->descripcion;
                $ub = $b->descripcion;
            }
            $p = strcmp($pa, $pb);
            if ($p !== 0) {
                return $p;
            }
            $t = strcmp($ta, $tb);
            if ($t !== 0) {
                return $t;
            }
            return strcmp($ua, $ub);
        });
        return $results;
    }
}
