<?php

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\Model\Document;

use MonsieurBiz\SyliusSearchPlugin\Exception\MissingLocaleException;
use MonsieurBiz\SyliusSearchPlugin\Exception\MissingParamException;
use MonsieurBiz\SyliusSearchPlugin\Exception\MissingPriceException;
use MonsieurBiz\SyliusSearchPlugin\Exception\NotSupportedTypeException;
use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Attributes;
use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Document;
use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Price;
use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon;
use MonsieurBiz\SyliusSearchPlugin\Provider\UrlParamsProvider;
use MonsieurBiz\SyliusSearchPlugin\Provider\UrlParamsProviderInterface;

class Result extends Document implements ResultInterface
{
    public function getUniqId(): string
    {
        if (null === $this->getType()) {
            throw new MissingParamException('Missing "type" for document');
        }
        if (null === $this->getId()) {
            throw new MissingParamException('Missing "ID" for document');
        }

        return sprintf('%s-%d', $this->getType(), $this->getId());
    }

    public function getAttribute(string $code): ?Attributes
    {
        foreach ($this->getAttributes() as $attribute) {
            if ($attribute->getCode() === $code) {
                return $attribute;
            }
        }

        return null;
    }

    public function getPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price
    {
        if (null === $this->getPrice()) {
            return null;
        }
        foreach ($this->getPrice() as $price) {
            if ($price->getChannel() === $channelCode && $price->getCurrency() === $currencyCode) {
                return $price;
            }
        }

        throw new MissingPriceException(sprintf('Price not found for channel "%s" and currency "%s"', $channelCode, $currencyCode));
    }

    public function getOriginalPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price
    {
        if (null === $this->getOriginalPrice()) {
            return null;
        }

        foreach ($this->getOriginalPrice() as $price) {
            if ($price->getChannel() === $channelCode && $price->getCurrency() === $currencyCode) {
                return $price;
            }
        }

        return null;
    }

    public function getLocale(): string
    {
        foreach ($this->getAttributes() as $attribute) {
            if (null !== $attribute->getLocale()) {
                return $attribute->getLocale();
            }
        }

        throw new MissingLocaleException('Locale not found in document');
    }

    public function getUrlParams(): UrlParamsProviderInterface
    {
        if ($this->getType() === 'product') {
            return new UrlParamsProvider('sylius_shop_product_show', ['slug' => $this->getSlug(), '_locale' => $this->getLocale()]);
        }

        throw new NotSupportedTypeException(sprintf('Object type "%s" not supported to get URL', $this->getType()));
    }

    public function addChannel(string $channel): self
    {
        if (null !== $this->getChannel()) {
            $this->setChannel(array_unique(array_merge($this->getChannel(), [$channel])));
        } else {
            $this->setChannel([$channel]);
        }

        return $this;
    }

    public function addTaxon(string $code, string $name, int $position, int $level, int $productPosition): ResultInterface
    {
        $taxon = new Taxon();
        $taxon->setCode($code)->setPosition($position)->setName($name)->setLevel($level)->setProductPosition($productPosition);

        if (is_array($this->getTaxon()) && count($this->getTaxon()) > 0) {
            $this->setTaxon(array_merge($this->getTaxon(), [$taxon]));
        } else {
            $this->setTaxon([$taxon]);
        }

        return $this;
    }

    public function addPrice(string $channel, string $currency, int $value): ResultInterface
    {
        $price = new Price();
        $price->setChannel($channel)->setCurrency($currency)->setValue($value);

        if (is_array($this->getPrice()) && count($this->getPrice()) > 0) {
            $this->setPrice(array_merge($this->getPrice(), [$price]));
        } else {
            $this->setPrice([$price]);
        }

        return $this;
    }

    public function addOriginalPrice(string $channel, string $currency, int $value): ResultInterface
    {
        $price = new Price();
        $price->setChannel($channel)->setCurrency($currency)->setValue($value);

        if (is_array($this->getOriginalPrice()) && count($this->getOriginalPrice()) > 0) {
            $this->setOriginalPrice(array_merge($this->getOriginalPrice(), [$price]));
        } else {
            $this->setOriginalPrice([$price]);
        }

        return $this;
    }

    public function addAttribute(string $code, string $name, array $value, string $locale, int $score): ResultInterface
    {
        $attribute = new Attributes();
        $attribute->setCode($code)->setName($name)->setValue($value)->setLocale($locale)->setScore($score);

        if (is_array($this->getAttributes()) && count($this->getAttributes()) > 0) {
            $this->setAttributes(array_merge($this->getAttributes(), [$attribute]));
        } else {
            $this->setAttributes([$attribute]);
        }

        return $this;
    }
}
