<?php
/**
 * 2007-2023 PrestaShop
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to http://www.prestashop.com for more information.
 *
 *  @author    PrestaShop SA <contact@prestashop.com>
 *  @copyright 2007-2023 PrestaShop SA
 *  @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
 *  International Registered Trademark & Property of PrestaShop SA
 */
declare(strict_types=1);

namespace PrestaShop\Module\AgileMultisellerPro\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use PrestaShop\Module\AgileMultisellerPro\Adapter\SellerInfoAdapter;
use PrestaShop\Module\AgileMultisellerPro\Entity\Sellerinfo;
use PrestaShop\Module\AgileMultisellerPro\Entity\SellerinfoLang;

if (!defined('_PS_VERSION_')) {
    exit;
}

class SellerInfoRepository extends EntityRepository
{
    private $translator;

    /**
     * @param int $langId
     *
     * @return array
     */
    public function getSellerInfos(int $langId = 0, bool $active = false, string $nameq = '', $sellerLocation = '', ?int $sellerType = null, ?string $filter = null): array
    {
        /** @var QueryBuilder $qb */
        $qb = $this->createQueryBuilder('si')
            ->addSelect('sil')
            ->leftJoin('si.sellerLangs', 'sil');

        if (0 !== $langId) {
            $qb->andWhere('sil.lang = :langId')
                ->setParameter('langId', $langId);
        }

        if (true === $active) {
            $qb->andWhere('si.active = :active')
                ->setParameter('active', $active);
        }

        if (!empty($nameq)) {
            $qb->leftJoin(\PrestaShopBundle\Entity\Shop::class, 'shop', 'WITH', 'shop.id = si.idShop')
                ->andWhere('shop.name LIKE :filter')
                ->setParameter('filter', '%' . $nameq . '%');
        }

        if ($sellerLocation) {
            $locationLevel = \Configuration::get('AGILE_MS_LOCATION_BLOCK_LEVEL');

            switch ($locationLevel) {
                default:
                case 'country':
                    $qb->andWhere('si.idCountry = :location')
                        ->setParameter('location', $sellerLocation);

                    break;

                case 'state':
                    $qb->andWhere('si.idState = :location')
                        ->setParameter('location', $sellerLocation);

                    break;
                case 'city':
                    $qb->andWhere('sil.city = :location')
                        ->setParameter('location', $sellerLocation);

                    break;
            }
        }

        if ($sellerType) {
            $qb->andWhere('si.idSellertype1 = :sellerType OR si.idSellertype2 = :sellerType')
                ->setParameter('sellerType', $sellerType);
        }

        if (!empty($filter)) {
            $qb->leftJoin(\PrestaShopBundle\Entity\Shop::class, 'shop', 'WITH', 'shop.id = si.idShop')
                ->andWhere('shop.name LIKE :filter')
                ->setParameter('filter', $filter . '%');
        }

        $qb->orderBy('sil.company', 'ASC');

        return $qb->getQuery()->getResult();
    }

    public function getSellerInfosByVirtualUri($virtualUri, $langId)
    {
        $conn = $this->getEntityManager()->getConnection();

        $sql = 'SELECT si.*,sil.*,st.name AS name_state, cl.name AS name_country
                    FROM `' . _DB_PREFIX_ . 'sellerinfo` si
                        INNER JOIN `' . _DB_PREFIX_ . 'shop_url` su
                            ON si.id_shop = su.id_shop
                        LEFT JOIN `' . _DB_PREFIX_ . 'sellerinfo_lang` sil
                            ON si.id_sellerinfo = sil.id_sellerinfo AND sil.id_lang = :langId
                        LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl
                            ON cl.id_country = si.id_country AND cl.id_lang = :langId
                        LEFT JOIN `' . _DB_PREFIX_ . 'state` st
                            ON st.id_state = si.id_state AND st.id_country = si.id_country
                        WHERE su.virtual_uri = :virtualUri
                ';

        return $conn->fetchAssociative(
            $sql,
            [
                'langId' => $langId,
                'virtualUri' => $virtualUri,
            ]
        );
    }

    public function shopNameDuplicated($name, $id)
    {
        if (empty($name)) {
            return false;
        }
        $conn = $this->getEntityManager()->getConnection();

        $sql = '
			SELECT id_shop
			FROM ' . _DB_PREFIX_ . 'shop
			WHERE name = :name';

        $result = $conn->fetchAssociative(
            $sql,
            [
                'name' => $name,
            ]
        );

        if (!$result || $result['id_shop'] == 0) {
            return false;
        }
        if ($result['id_shop'] > 0 && $result['id_shop'] != (int) $id) {
            return true;
        }

        return false;
    }

    public function getMainUrlId($id_shop)
    {
        if (empty($id_shop)) {
            return 0;
        }

        $conn = $this->getEntityManager()->getConnection();

        $sql = '
			SELECT id_shop_url
			FROM ' . _DB_PREFIX_ . 'shop_url
			WHERE main=1 AND id_shop = :id_shop';

        $result = $conn->fetchAssociative(
            $sql,
            [
                'id_shop' => $id_shop,
            ]
        );

        return $result['id_shop_url'];
    }

    public function getSellersLocationsByLang(int $langId, string $location_level): array
    {
        $conn = $this->getEntityManager()->getConnection();

        switch ($location_level) {
            default:
            case 'country':
                $sql = 'SELECT distinct c.id_country AS id,cl.name AS name
                    FROM `' . _DB_PREFIX_ . 'country` c
                        INNER JOIN `' . _DB_PREFIX_ . 'sellerinfo` si ON c.id_country = si.id_country
                        LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON c.id_country=cl.id_country AND cl.id_lang = :langId
                    ORDER BY cl.name ASC
                ';

                break;

            case 'state':
                $sql = 'SELECT distinct s.id_state AS id, s.name AS name
		            FROM `' . _DB_PREFIX_ . 'state` s
		                INNER JOIN `' . _DB_PREFIX_ . 'sellerinfo` si ON s.id_state = si.id_state
				    ORDER BY s.name ASC
	            ';

                break;
            case 'city':
                $sql = 'SELECT distinct TRIM(sil.city) AS id, TRIM(sil.city) AS name
		            FROM `' . _DB_PREFIX_ . 'sellerinfo_lang` sil
					WHERE sil.id_lang = :langId AND LENGTH(TRIM(sil.city)) > 0
				    ORDER BY sil.city ASC
	            ';
                break;
        }

        $query = $conn->executeQuery($sql, ['langId' => $langId]);

        $result = [];
        foreach ($query as $val) {
            $result[] = $val;
        }

        return $result;
    }

    public function getSellersTypesByLang(int $langId): array
    {
        $conn = $this->getEntityManager()->getConnection();

        $sql = 'SELECT distinct st.id_sellertype AS id, sl.name AS name
		            FROM `' . _DB_PREFIX_ . 'sellertype` st
		                INNER JOIN `' . _DB_PREFIX_ . 'sellerinfo` si ON (st.id_sellertype = si.id_sellertype1 OR st.id_sellertype = si.id_sellertype2)
			            LEFT JOIN `' . _DB_PREFIX_ . 'sellertype_lang` sl ON st.id_sellertype = sl.id_sellertype AND sl.id_lang = :langId
				    ORDER BY sl.name ASC
		';

        $query = $conn->executeQuery($sql, ['langId' => $langId]);

        $result = [];
        foreach ($query as $val) {
            $result[] = $val;
        }

        return $result;
    }

    public function getLinkedSellerID(int $idCustomer = 0)
    {
        /** @var QueryBuilder $qb */
        $qb = $this->createQueryBuilder('si')
            ->andWhere('si.idCustomer = :idCustomer')
            ->setParameter('idCustomer', $idCustomer)
            ->setMaxResults(1);

        return $qb->getQuery()->getOneOrNullResult();
    }

    public function validateBySellerInfo(Sellerinfo $sellerinfo, array $data = [])
    {
        $errors = [];

        $this->translator = \Context::getContext()->getTranslator();

        if (isset($data['id_country'])) {
            $id_country = $data['id_country'];
        } else {
            $id_country = $sellerinfo->getIdCountry();
        }
        if (!($country = new \Country($id_country)) || !\Validate::isLoadedObject($country)) {
            $errors[] = $this->translator->trans('Country cannot be loaded with id_country', [], 'Modules.Agilemultisellerpro.Sellerinfo');
        }

        if (isset($data['id_state'])) {
            $id_state = $data['id_state'];
        } else {
            $id_state = $sellerinfo->getIdState();
        }
        if ((int) $country->contains_states && !(int) $id_state) {
            $errors[] = $this->translator->trans('This country requires a state selection.', [], 'Modules.Agilemultisellerpro.Sellerinfo');
        }

        if ($country->need_zip_code) {
            if (isset($data['postcode'])) {
                $postcode = $data['postcode'];
            } else {
                $postcode = $sellerinfo->getPostcode();
            }

            if (false === $country->checkZipCode($postcode)) {
                $errors[] = '<b>' . $this->translator->trans('Zip / Postal code', [], 'Modules.Agilemultisellerpro.Sellerinfo') . '</b> '
                    . $this->translator->trans('is invalid.', [], 'Modules.Agilemultisellerpro.Sellerinfo') . ' '
                    . $this->translator->trans('Must be typed as follows:', [], 'Modules.Agilemultisellerpro.Sellerinfo') . ' '
                    . str_replace(
                        'C',
                        $country->iso_code,
                        str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format))
                    );
            }
        }

        if (isset($data['dni'])) {
            $dni = $data['dni'];
        } else {
            $dni = $sellerinfo->getDni();
        }
        if ($country->isNeedDni() && (!$dni || !\Validate::isDniLite($dni))) {
            $errors[] = $this->translator->trans('Identification number is incorrect or has already been used.', [], 'Modules.Agilemultisellerpro.Sellerinfo');
        }

        $errors = array_merge(
            $errors,
            $this->filedsMappingValidate($sellerinfo, $this->getClassMetadata()->fieldMappings, $data),
            $this->filedsMappingValidate($sellerinfo, $this->_em->getClassMetadata(SellerinfoLang::class)->fieldMappings, $data)
        );

        $errors = array_unique($errors);

        return $errors;
    }

    private function filedsMappingValidate(Sellerinfo $sellerinfo, array $fieldsMappings, array $data)
    {
        $errors = [];
        $languages = \Language::getLanguages(false);

        if ($fieldsMappings) {
            $fieldsInfo = SellerInfoAdapter::getSellerinfoFields();

            foreach ($fieldsMappings as $field) {
                $fieldInfo = [];

                $columnName = $field['columnName'];
                if (isset($fieldsInfo[$columnName])) {
                    $fieldInfo = $fieldsInfo[$columnName];
                } else {
                    continue;
                }

                if (isset($fieldInfo['lang']) && $fieldInfo['lang']) {
                    if (\Tools::getValue('signin')) {
                        $default_language = new \Language((int) \Context::getContext()->language->id);
                    } else {
                        $default_language = new \Language((int) \Configuration::get('PS_LANG_DEFAULT'));
                    }

                    $default_language_value = $this->getValueBySeller($sellerinfo, $columnName, $data, (int) $default_language->id);

                    if (
                        isset($fieldInfo['required'])
                        && $fieldInfo['required']
                        && !$default_language_value
                        && (string) $default_language_value != '0'
                    ) {
                        $errorLang = sprintf(
                            $this->translator->trans('The field %1$s is required at least in %2$s.', [], 'Modules.Agilemultisellerpro.Sellerinfo'),
                            '<b>' . $fieldInfo['label'] . '</b>',
                            $default_language->name
                        );
                        $errors = array_merge(
                            $errors,
                            [$errorLang]
                        );
                    }

                    foreach ($languages as $lang) {
                        $value = $this->getValueBySeller($sellerinfo, $columnName, $data, (int) $lang['id_lang']);
                        $errors = array_merge(
                            $errors,
                            $this->fieldValidate($field, $fieldInfo, $value, $lang)
                        );
                    }
                } else {
                    $value = $this->getValueBySeller($sellerinfo, $columnName, $data);
                    $errors = array_merge(
                        $errors,
                        $this->fieldValidate($field, $fieldInfo, $value)
                    );
                }
            }
        }

        return $errors;
    }

    private function getValueBySeller($sellerinfo, $columnName, $data, $id_lang = 0)
    {
        $value = null;

        if ($id_lang) {
            if (isset($data[$columnName . '_' . $id_lang])) {
                return $data[$columnName . '_' . $id_lang];
            }
            $object = $sellerinfo->getLangByLangId($id_lang);
        } else {
            if (isset($data[$columnName])) {
                return $data[$columnName];
            }
            $object = $sellerinfo;
        }

        $methodName = 'get' . ucfirst($columnName);
        if (is_object($object) && true === method_exists($object, $methodName)) {
            $value = $object->{$methodName}();
        }

        if ($value instanceof \DateTimeInterface) {
            $value = $value->format('Y-m-d');
        }

        return $value;
    }

    private function fieldValidate($field, $fieldInfo, $value, $lang = [])
    {
        $errors = [];

        // Check required
        if (
            isset($fieldInfo['required'])
            && $fieldInfo['required']
            && !$value
            && (string) $value != '0'
            && !$lang
        ) {
            $errors[] = "<b> {$fieldInfo['label']} </b> "
                . $this->translator->trans('is required.', [], 'Modules.Agilemultisellerpro.Sellerinfo');
        }

        // Check size
        if (
            $field['length'] > 0
            && $value
            && \Tools::strlen($value) > $field['length']
        ) {
            $errors[] = sprintf(
                $this->translator->trans('%1$s is too long. Maximum length: %2$d'),
                '<b>' . $fieldInfo['label'] . '</b>',
                $field['length']
            );
        }

        // Validate
        if (
            !\Validate::{$fieldInfo['validate']}($value)
            && (!empty($value) || (isset($fieldInfo['required']) && $fieldInfo['required']))
        ) {
            $errors[] = sprintf(
                $this->translator->trans('%s is invalid.'),
                '<b>' . $fieldInfo['label'] . '</b>'
            );
        }

        return $errors;
    }
}
