<?php
namespace Incoviba\Service;

use DateTimeImmutable;
use Incoviba\Common\Implement;
use Incoviba\Repository;
use Incoviba\Model;

class Venta
{
    public function __construct(
        protected Repository\Venta $ventaRepository,
        protected Repository\Venta\EstadoVenta $estadoVentaRepository,
        protected Repository\Venta\TipoEstadoVenta $tipoEstadoVentaRepository,
        protected Venta\Propietario $propietarioService,
        protected Venta\Propiedad $propiedadService,
        protected Venta\Pie $pieService,
        protected Venta\Subsidio $subsidioService,
        protected Venta\Credito $creditoService,
        protected Venta\BonoPie $bonoPieService,
        protected Money $moneyService
    ) {}

    public function getById(int $venta_id): Model\Venta
    {
        return $this->process($this->ventaRepository->fetchById($venta_id));
    }
    public function getByProyecto(int $proyecto_id): array
    {
        $ventas = $this->ventaRepository->fetchByProyecto($proyecto_id);
        foreach ($ventas as &$venta) {
            $venta = $this->process($venta);
        }
        return $ventas;
    }
    public function getActivaByProyecto(int $proyecto_id): array
    {
        $ventas = $this->ventaRepository->fetchActivaByProyecto($proyecto_id);
        foreach ($ventas as &$venta) {
            $venta = $this->process($venta);
        }
        return $ventas;
    }
    public function getByProyectoAndUnidad(string $proyecto_nombre, int $unidad_descripcion): Model\Venta
    {
        $venta = $this->ventaRepository->fetchByProyectoAndUnidad($proyecto_nombre, $unidad_descripcion);
        return $this->process($venta);
    }
    public function getByUnidad(string $unidad, string $tipo): array
    {
        $ventas = $this->ventaRepository->fetchByUnidad($unidad, $tipo);
        foreach ($ventas as &$venta) {
            $venta = $this->process($venta);
        }
        return $ventas;
    }
    public function getByPropietario(string $propietario): array
    {
        $ventas = $this->ventaRepository->fetchByPropietario($propietario);
        foreach ($ventas as &$venta) {
            $venta = $this->process($venta);
        }
        return $ventas;
    }
    public function getByPrecio(string $precio): array
    {
        $ventas = $this->ventaRepository->fetchByPrecio($precio);
        foreach ($ventas as &$venta) {
            $venta = $this->process($venta);
        }
        return $ventas;
    }

    protected function process(Model\Venta $venta): Model\Venta
    {
        $venta->addFactory('estados', (new Implement\Repository\Factory())->setCallable([$this->estadoVentaRepository, 'fetchByVenta'])->setArgs([$venta->id]));
        $venta->addFactory('currentEstado', (new Implement\Repository\Factory())->setCallable([$this->estadoVentaRepository, 'fetchCurrentByVenta'])->setArgs([$venta->id]));
        return $venta;
    }

    public function add(array $data): void
    {
        $fecha = new DateTimeImmutable($data['fecha_venta']);
        $data['uf'] = $this->moneyService->getUF($fecha);
        $propietario = $this->addPropietario($data);
        $propiedad = $this->addPropiedad($data);
        $forma_pago = $this->addFormaPago($data);
        $venta_data = [
            'propietario' => $propietario->rut,
            'propiedad' => $propiedad->id,
            'fecha' => $fecha->format('Y-m-d'),
            'valor_uf' => $data['valor'],
            'fecha_ingreso' => (new DateTimeImmutable())->format('Y-m-d'),
            'uf' => $data['uf']
        ];
        $map = ['pie', 'subsidio', 'credito', 'bono_pie'];
        foreach ($map as $field) {
            $name = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $field))));
            if (isset($forma_pago->{$name})) {
                $venta_data[$field] = $forma_pago->{$name}->id;
            }
        }
        $venta = $this->ventaRepository->create($venta_data);
        $venta = $this->ventaRepository->save($venta);
        $tipoEstado = $this->tipoEstadoVentaRepository->fetchByDescripcion('vigente');
        $estado = $this->estadoVentaRepository->create([
            'venta' => $venta->id,
            'estado' => $tipoEstado->id,
            'fecha' => $venta->fecha->format('Y-m-d')
        ]);
        $this->estadoVentaRepository->save($estado);
    }
    protected function addPropietario(array $data): Model\Venta\Propietario
    {
        if (isset($data['natural_uno'])) {
            if (isset($data['natural_multiple'])) {
                return $this->addDosPropietarios($data);
            }
            return $this->addUnPropietario($data);
        }
        return $this->addSociedad($data);
    }
    protected function addUnPropietario(array $data): Model\Venta\Propietario
    {
        $fields = array_fill_keys([
            'rut',
            'nombres',
            'apellido_paterno',
            'apellido_materno',
            'calle',
            'numero',
            'extra',
            'comuna'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        return $this->propietarioService->addPropietario($filtered_data);
    }
    protected function addDosPropietarios(array $data): Model\Venta\Propietario
    {
        $fields = array_fill_keys([
            'rut_otro',
            'nombres_otro',
            'apellido_paterno_otro',
            'apellido_materno_otro',
            'calle_otro',
            'numero_otro',
            'extra_otro',
            'comuna_otro'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'rut',
            'nombres',
            'apellido_paterno',
            'apellido_materno',
            'calle',
            'numero',
            'extra',
            'comuna'
        ], $filtered_data);
        $otro = $this->propietarioService->addPropietario($mapped_data);

        $data['otro'] = $otro->rut;
        $fields = array_fill_keys([
            'rut',
            'nombres',
            'apellido_paterno',
            'apellido_materno',
            'calle',
            'numero',
            'extra',
            'comuna',
            'otro'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        return $this->propietarioService->addPropietario($filtered_data);
    }
    protected function addSociedad(array $data): Model\Venta\Propietario
    {
        $representante = $this->addUnPropietario($data);

        $data['representante'] = $representante->rut;
        $fields = array_fill_keys([
            'rut_sociedad',
            'razon_social',
            'calle_comercial',
            'numero_comercial',
            'extra_comercial',
            'comuna_comercial',
            'representante'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'rut',
            'razon_social',
            'calle',
            'numero',
            'extra',
            'comuna',
            'representante'
        ], $filtered_data);
        return $this->propietarioService->addSociedad($mapped_data);
    }
    protected function addPropiedad(array $data): Model\Venta\Propiedad
    {
        $ids = array_filter($data, function($key) {
            return str_contains($key, 'unidad');
        }, ARRAY_FILTER_USE_KEY);

        return $this->propiedadService->addPropiedad($ids);
    }
    protected function addFormaPago(array $data): Model\Venta\FormaPago
    {
        $fields = [
            'pie',
            'subsidio',
            'credito',
            'bono_pie'
        ];
        $forma_pago = new Model\Venta\FormaPago();
        foreach ($fields as $name) {
            if (isset($data["has_{$name}"])) {
                $method = 'add' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
                $obj = $this->{$method}($data);
                $forma_pago->{$name} = $obj;
            }
        }
        return $forma_pago;
    }
    protected function addPie(array $data): Model\Venta\Pie
    {
        $fields = array_fill_keys([
            'fecha_venta',
            'pie',
            'cuotas',
            'uf'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'fecha',
            'valor',
            'cuotas',
            'uf'
        ], $filtered_data);
        return $this->pieService->add($mapped_data);
    }
    protected function addSubsidio(array $data): Model\Venta\Subsidio
    {
        $fields = array_fill_keys([
            'fecha_venta',
            'ahorro',
            'subsidio',
            'uf'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'fecha',
            'ahorro',
            'subsidio',
            'uf'
        ], $filtered_data);
        return $this->subsidioService->add($mapped_data);
    }
    protected function addCredito(array $data): Model\Venta\Credito
    {
        $fields = array_fill_keys([
            'fecha_venta',
            'credito',
            'uf'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'fecha',
            'valor',
            'uf'
        ], $filtered_data);
        return $this->creditoService->add($mapped_data);
    }
    protected function addBonoPie(array $data): Model\Venta\BonoPie
    {
        $fields = array_fill_keys([
            'fecha_venta',
            'bono_pie'
        ], 0);
        $filtered_data = array_intersect_key($data, $fields);
        $mapped_data = array_combine([
            'fecha',
            'valor'
        ], $filtered_data);
        return $this->bonoPieService->add($mapped_data);
    }
}
