<?php
declare(strict_types=1);

namespace FCA\StockApi\Collection\Filter;

use FCA\StockApi\Collection\Filter\Builder\Fields;
use FCA\StockApi\Exception\ApiException;

class Wizard
{
    /**
     * @var int[]
     */
    private $vehicleIDs = [];

    /**
     * @var string[]
     */
    private $brandCode = [];

    /**
     * @var string[]
     */
    private $dealerCode = [];

    /**
     * @var string[]
     */
    private $model = [];

    /**
     * @var string[]
     */
    private $model_groups_slug = [];

    /**
     * @var string[]
     */
    private $version = [];

    /**
     * @var string[]
     */
    private $fuelType = [];

    /**
     * @var int|null
     */
    private $priceFrom;

    /**
     * @var int|null
     */
    private $priceTo;

    /**
     * @var string One of `\Collection\Filter\Builder\Fields::*_PRICE_*` values
     */
    private $priceType;

    /**
     * @var int[]
     */
    private $productionYear = [];

    /**
     * @var string[]
     */
    private $gearboxType = [];

    /**
     * @var string[]
     */
    private $driveType = [];

    /**
     * @var int|null
     */
    private $enginePowerFrom;

    /**
     * @var int|null
     */
    private $enginePowerTo;

    /**
     * @var string[]
     */
    private $color = [];

    /**
     * @var string[]
     */
    private $interiorType = [];

    /**
     * @var string[]
     */
    private $equipment = [];

    /**
     * @var float|null
     */
    private $longitude;

    /**
     * @var float|null
     */
    private $latitude;

    /**
     * @var int|null
     */
    private $maxDistance;

    /**
     * @var string[]
     */
    private $group = [];

    /**
     * @var string[]
     */
    private $label = [];

    public function getFilters(): array
    {
        $b = new Builder();

        $filters = [];

        if (count($this->vehicleIDs)) {
            $filters[] = $b->is(Fields::ID, 'in', array_values($this->vehicleIDs));
        }

        if (count($this->brandCode)) {
            $filters[] = $b->is(Fields::BRAND_CODE, 'in', array_values($this->brandCode));
        }

        if (count($this->dealerCode)) {
            $filters[] = $b->is(Fields::DEALER_CODE, 'in', array_values($this->dealerCode));
        }

        if (count($this->model)) {
            $filters[] = $b->is(Fields::MODEL_COMMERCIAL_CODE, 'in', array_values($this->model));
        }

        if (count($this->model_groups_slug)) {
            $filters[] = $b->is(Fields::MODEL_GROUPS_SLUG, 'in', array_values($this->model_groups_slug));
        }

        if (count($this->version)) {
            $filters[] = $b->is(Fields::VERSION_COMMERCIAL_CODE, 'in', array_values($this->version));
        }

        if (count($this->fuelType)) {
            $filters[] = $b->is(Fields::FUEL_TYPE, 'in', array_values($this->fuelType));
        }

        if ($this->priceType == null and ($this->priceTo !== null or $this->priceFrom !== null)) {
            throw new ApiException('priceType cannot be null');
        }

        if ($this->priceTo !== null and $this->priceFrom === null) {
            $filters[] = $b->is($this->priceType, '<=', $this->priceTo);
        } elseif ($this->priceTo === null and $this->priceFrom !== null) {
            $filters[] = $b->is($this->priceType, '>=', $this->priceFrom);
        } elseif ($this->priceTo !== null and $this->priceFrom !== null) {
            $filters[] =
                $b->andWhere(
                    $b->is($this->priceType, '<=', $this->priceTo),
                    $b->is($this->priceType, '>=', $this->priceFrom)
                );
        }

        if (count($this->productionYear)) {
            $filters[] = $b->is(Fields::PRODUCTION_YEAR, 'in', array_values($this->productionYear));
        }

        if (count($this->gearboxType)) {
            $filters[] = $b->is(Fields::GEARBOX_TYPE, 'in', array_values($this->gearboxType));
        }

        if (count($this->driveType)) {
            $filters[] = $b->is(Fields::DRIVE_TYPE, 'in', array_values($this->driveType));
        }

        if ($this->enginePowerTo !== null and $this->enginePowerFrom === null) {
            $filters[] = $b->is(Fields::ENGINE_POWER, '<=', $this->enginePowerTo);
        } elseif ($this->enginePowerTo === null and $this->enginePowerFrom !== null) {
            $filters[] = $b->is(Fields::ENGINE_POWER, '>=', $this->enginePowerFrom);
        } elseif ($this->enginePowerTo !== null and $this->enginePowerFrom !== null) {
            $filters[] =
                $b->andWhere(
                    $b->is(Fields::ENGINE_POWER, '<=', $this->enginePowerTo),
                    $b->is(Fields::ENGINE_POWER, '>=', $this->enginePowerFrom)
                );
        }
        
        if (count($this->color)) {
            $filters[] = $b->is(Fields::COLOR_CATEGORY, 'in', array_values($this->color));
        }

        if (count($this->interiorType)) {
            $filters[] = $b->is(Fields::INTERIOR_TYPE, 'in', array_values($this->interiorType));
        }

        if (count($this->equipment)) {
            $filters[] = $b->is(Fields::EQUIPMENT_OPTIONS, 'all', array_values($this->equipment));
        }

        if ($this->latitude !== null and $this->longitude !== null && $this->maxDistance !== null) {
            $filters[] = $b->isNear(Fields::DEALER_COORDINATES, $this->longitude, $this->latitude, $this->maxDistance);
        }

        if (count($this->group)) {
            $filters[] = $b->is(Fields::GROUPS, 'all', array_values($this->group));
        }

        if (count($this->label)) {
            $filters[] = $b->is(Fields::LABELS, 'all', array_values($this->label));
        }

        return $b->andWhere(...$filters);
    }

    public function setVehicleIDs(array $ids): self
    {
        // Convert all values to integers, keys to strings
        $this->vehicleIDs = [];
        foreach ($ids as $id) {
            $this->vehicleIDs[(string) $id] = (int) $id;
        }

        return $this;
    }

    public function clearVehicleIDs(): self
    {
        $this->vehicleIDs = [];

        return $this;
    }

    public function addVehicleID(int $id): self
    {
        $this->vehicleIDs[$id] = $id;

        return $this;
    }

    public function removeVehicleID(string $id): self
    {
        unset($this->vehicleIDs[$id]);

        return $this;
    }

    public function setBrandCodes(array $brandCodes): self
    {
        // Convert all values to strings
        array_walk($brandCodes, function (&$value) {
            $value = (string)$value;
        });
        $this->brandCode = array_combine($brandCodes, $brandCodes);

        return $this;
    }

    public function clearBrandCodes(): self
    {
        $this->brandCode = [];

        return $this;
    }

    public function addBrandCode(string $brandCode): self
    {
        $this->brandCode[$brandCode] = $brandCode;

        return $this;
    }

    public function removeBrandCode(string $brandCode): self
    {
        unset($this->brandCode[$brandCode]);

        return $this;
    }

    public function setDealerCodes(array $dealerCode): self
    {
        // Convert all values to strings
        array_walk($dealerCode, function (&$value) {
            // Skip invenduto virtual dealer, which is represented with `null`
            if (!$value === null) {
                $value = (string)$value;
            }
        });

        $this->dealerCode = array_combine($dealerCode, $dealerCode);

        return $this;
    }

    public function clearDealerCodes(): self
    {
        $this->dealerCode = [];

        return $this;
    }

    public function addDealerCode(string $dealerCode): self
    {
        $this->dealerCode[$dealerCode] = $dealerCode;

        return $this;
    }

    public function removeDealerCode(string $dealerCode): self
    {
        unset($this->dealerCode[$dealerCode]);

        return $this;
    }

    public function setModels(array $models): self
    {
        // Convert all values to strings
        array_walk($models, function (&$value) {
            $value = (string)$value;
        });
        $this->model = array_combine($models, $models);

        return $this;
    }

    public function addModel(string $model): self
    {
        $this->model[$model] = $model;

        return $this;
    }

    public function removeModel(string $model): self
    {
        unset($this->model[$model]);

        return $this;
    }

    public function clearModels(): self
    {
        $this->model = [];

        return $this;
    }

    public function setModelGroupsSlugs(array $slugs): self
    {
        // Convert all values to strings
        array_walk($slugs, function (&$value) {
            $value = (string)$value;
        });
        $this->model_groups_slug = array_combine($slugs, $slugs);

        return $this;
    }

    public function addModelGroupsSlug(string $slug): self
    {
        $this->model_groups_slug[$slug] = $slug;

        return $this;
    }

    public function removeModelGroupsSlug(string $slug): self
    {
        unset($this->model_groups_slug[$slug]);

        return $this;
    }

    public function clearModelGroupsSlugs(): self
    {
        $this->model_groups_slug = [];

        return $this;
    }

    public function setVersions(array $versions): self
    {
        // Convert all values to strings
        array_walk($versions, function (&$value) {
            $value = (string)$value;
        });
        $this->version = array_combine($versions, $versions);

        return $this;
    }

    public function addVersion(string $version): self
    {
        $this->version[$version] = $version;

        return $this;
    }

    public function removeVersion(string $version): self
    {
        unset($this->version[$version]);

        return $this;
    }

    public function clearVersions(): self
    {
        $this->version = [];

        return $this;
    }

    public function addFuelType(string $fuelType): self
    {
        $this->fuelType[$fuelType] = $fuelType;

        return $this;
    }

    public function removeFuelType(string $fuelType): self
    {
        unset($this->fuelType[$fuelType]);

        return $this;
    }

    public function setFuelTypes(array $fuelTypes): self
    {
        // Convert all values to strings
        array_walk($fuelTypes, function (&$value) {
            $value = (string)$value;
        });
        $this->fuelType = array_combine($fuelTypes, $fuelTypes);

        return $this;
    }

    public function clearFuelTypes(): self
    {
        $this->fuelType = [];

        return $this;
    }

    public function setPriceFrom(int $priceFrom): self
    {
        $this->priceFrom = $priceFrom;

        return $this;
    }

    public function clearPriceFrom(): self
    {
        $this->priceFrom = null;

        return $this;
    }

    public function setPriceTo(int $priceTo): self
    {
        $this->priceTo = $priceTo;

        return $this;
    }

    public function clearPriceTo(): self
    {
        $this->priceTo = null;

        return $this;
    }

    public function addProductionYear(int $productionYear): self
    {
        $this->productionYear[(string) $productionYear] = $productionYear;

        return $this;
    }

    public function removeProductionYear(int $productionYear): self
    {
        unset($this->productionYear[(string) $productionYear]);

        return $this;
    }

    public function setProductionYears(array $productionYears): self
    {
        // Convert all values to integers, keys to strings
        $this->productionYear = [];
        foreach ($productionYears as $production_year) {
            $this->productionYear[(string) $production_year] = (int) $production_year;
        }

        return $this;
    }

    public function clearProductionYears(): self
    {
        $this->productionYear = [];

        return $this;
    }

    public function addGearboxType(string $gearboxType): self
    {
        $this->gearboxType[$gearboxType] = $gearboxType;

        return $this;
    }

    public function removeGearboxType(string $gearboxType): self
    {
        unset($this->gearboxType[$gearboxType]);

        return $this;
    }

    public function setGearboxTypes(array $gearboxTypes): self
    {
        // Convert all values to strings
        array_walk($gearboxTypes, function (&$value) {
            $value = (string)$value;
        });
        $this->gearboxType = array_combine($gearboxTypes, $gearboxTypes);

        return $this;
    }

    public function clearGearboxTypes(): self
    {
        $this->gearboxType = [];

        return $this;
    }

    public function setDriveTypes(array $driveTypes): self
    {
        // Convert all values to strings
        array_walk($driveTypes, function (&$value) {
            $value = (string)$value;
        });
        $this->driveType = array_combine($driveTypes, $driveTypes);

        return $this;
    }

    public function addDriveType(string $driveType): self
    {
        $this->driveType[$driveType] = $driveType;

        return $this;
    }

    public function removeDriveType(string $driveType): self
    {
        unset($this->driveType[$driveType]);

        return $this;
    }

    public function clearDriveTypes(): self
    {
        $this->driveType = [];

        return $this;
    }

    public function setEnginePowerFrom(int $enginePowerFrom): self
    {
        $this->enginePowerFrom = $enginePowerFrom;

        return $this;
    }

    public function clearEnginePowerFrom(): self
    {
        $this->enginePowerFrom = null;

        return $this;
    }

    public function setEnginePowerTo(int $enginePowerTo): self
    {
        $this->enginePowerTo = $enginePowerTo;

        return $this;
    }

    public function clearEnginePowerTo(): self
    {
        $this->enginePowerTo = null;

        return $this;
    }

    public function addColor(string $color): self
    {
        $this->color[$color] = $color;

        return $this;
    }

    public function removeColor(string $color): self
    {
        unset($this->color[$color]);

        return $this;
    }

    public function setColors(array $colors): self
    {
        // Convert all values to strings
        array_walk($colors, function (&$value) {
            $value = (string)$value;
        });
        $this->color = array_combine($colors, $colors);

        return $this;
    }

    public function clearColors(): self
    {
        $this->color = [];

        return $this;
    }

    public function addGroup(string $group): self
    {
        $this->group[$group] = $group;

        return $this;
    }

    public function removeGroup(string $group): self
    {
        unset($this->group[$group]);

        return $this;
    }

    public function setGroups(array $groups): self
    {
        // Convert all values to strings
        array_walk($groups, function (&$value) {
            $value = (string)$value;
        });
        $this->group = array_combine($groups, $groups);

        return $this;
    }

    public function clearGroups(): self
    {
        $this->group = [];

        return $this;
    }

    public function addLabel(string $label): self
    {
        $this->label[$label] = $label;

        return $this;
    }

    public function removeLabel(string $label): self
    {
        unset($this->group[$label]);

        return $this;
    }

    public function setLabels(array $labels): self
    {
        // Convert all values to strings
        array_walk($labels, function (&$value) {
            $value = (string)$value;
        });
        $this->label = array_combine($labels, $labels);

        return $this;
    }

    public function clearLabels(): self
    {
        $this->label = [];

        return $this;
    }

    public function addInteriorType(string $interiorType): self
    {
        $this->interiorType[$interiorType] = $interiorType;

        return $this;
    }

    public function removeInteriorType($interiorType): self
    {
        unset($this->interiorType[$interiorType]);

        return $this;
    }

    public function setInteriorTypes(array $interiorTypes): self
    {
        // Convert all values to strings
        array_walk($interiorTypes, function (&$value) {
            $value = (string)$value;
        });
        $this->interiorType = array_combine($interiorTypes, $interiorTypes);

        return $this;
    }

    public function clearInteriorTypes(): self
    {
        $this->interiorType = [];

        return $this;
    }

    public function setEquipments(array $equipments): self
    {
        // Convert all values to strings
        array_walk($equipments, function (&$value) {
            $value = (string)$value;
        });
        $this->equipment = array_combine($equipments, $equipments);

        return $this;
    }

    public function addEquipment(string $equipment): self
    {
        $this->equipment[$equipment] = $equipment;

        return $this;
    }

    public function removeEquipment(string $equipment): self
    {
        unset($this->equipment[$equipment]);

        return $this;
    }

    public function clearEquipments(): self
    {
        $this->equipment = [];

        return $this;
    }

    public function setPriceType(string $priceType): self
    {
        $validValues = [
            Fields::BASE_PRICE_BRUTTO,
            Fields::BASE_PRICE_NETTO,
            Fields::FINAL_PRICE_BRUTTO,
            Fields::FINAL_PRICE_NETTO
        ];
        if (in_array($priceType, $validValues)) {
            $this->priceType = $priceType;
        } else {
            throw new ApiException('Invalid priceType value!');
        }

        return $this;
    }

    public function clearPriceType(): self
    {
        $this->priceType = null;

        return $this;
    }

    public function setLongitude(float $longitude): self
    {
        $this->longitude = $longitude;

        return $this;
    }

    public function clearLongitude(): self
    {
        $this->longitude = null;

        return $this;
    }

    public function setLatitude(float $latitude): self
    {
        $this->latitude = $latitude;

        return $this;
    }

    public function clearLatitude(): self
    {
        $this->latitude = null;

        return $this;
    }

    public function setMaxDistance(int $distance): self
    {
        $this->maxDistance = $distance;

        return $this;
    }

    public function clearMaxDistance(): self
    {
        $this->maxDistance = null;

        return $this;
    }
}
