<?php
/*********************************************************************************
 * The contents of this file are subject to the EspoCRM Advanced Pack
 * Agreement ("License") which can be viewed at
 * https://www.espocrm.com/advanced-pack-agreement.
 * By installing or using this file, You have unconditionally agreed to the
 * terms and conditions of the License, and You may not use this file except in
 * compliance with the License.  Under the terms of the license, You shall not,
 * sublicense, resell, rent, lease, distribute, or otherwise  transfer rights
 * or usage to the software.
 *
 * Copyright (C) 2015-2023 Letrium Ltd.
 *
 * License ID: 7f33790bc71ff8ca9dfbc9066dbc323a
 ***********************************************************************************/

namespace Espo\Modules\Advanced\Tools\ReportFilter;

use Espo\Core\DataManager;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Language;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Advanced\Classes\Select\Common\PrimaryFilters\ReportFilter as ReportPrimaryFilter;
use Espo\Modules\Advanced\Core\ReportFilter as ReportFilterUtil;
use Espo\Modules\Advanced\Entities\ReportFilter;
use Espo\ORM\EntityManager;

class Service
{
    private EntityManager $entityManager;
    private Metadata $metadata;
    private InjectableFactory $injectableFactory;
    private DataManager $dataManager;
    private Config $config;

    public function __construct(
        EntityManager $entityManager,
        Metadata $metadata,
        InjectableFactory $injectableFactory,
        DataManager $dataManager,
        Config $config
    ) {
        $this->entityManager = $entityManager;
        $this->metadata = $metadata;
        $this->injectableFactory = $injectableFactory;
        $this->dataManager = $dataManager;
        $this->config = $config;
    }

    public function rebuild(?string $specificEntityType = null): void
    {
        $scopeData = $this->metadata->get(['scopes'], []);

        $entityTypeList = [];

        $language = $this->injectableFactory->createWith(Language::class, ['language' => 'en_US']);

        $isAnythingChanged = false;

        if ($specificEntityType) {
            $entityTypeList[] = $specificEntityType;
        } else {
            foreach ($scopeData as $scope => $item) {
                if (empty($item['entity'])) {
                    continue;
                }

                if (empty($item['object'])) {
                    continue;
                }

                if (!empty($item['disabled'])) {
                    continue;
                }

                $entityTypeList[] = $scope;
            }
        }

        foreach ($entityTypeList as $entityType) {
            $removedHash = [];
            $isChanged = false;

            $clientDefs = $this->metadata->getCustom('clientDefs', $entityType, (object) []);
            $filterList = [];
            $toAppend = true;

            if (isset($clientDefs->filterList)) {
                $toAppend = false;
                $filterList = $clientDefs->filterList;
            }

            foreach ($filterList as $i => $item) {
                if (is_string($item)) {
                    if ($item === '__APPEND__') {
                        unset($filterList[$i]);
                        $toAppend = true;
                    }

                    continue;
                }

                if (!empty($item->isReportFilter)) {
                    unset($filterList[$i]);
                    $isChanged = true;
                }
            }

            $filterList = array_values($filterList);

            $entityDefs = $this->metadata->getCustom('entityDefs', $entityType, (object) []);

            $filtersData = (object) [];

            if (isset($entityDefs->collection) && isset($entityDefs->collection->filters)) {
                $filtersData = $entityDefs->collection->filters;

                if (is_array($filtersData)) {
                    $filtersData = (object) [];
                }
            }

            foreach ($filtersData as $filter => $item) {
                if (!empty($item->isReportFilter)) {
                    unset($filtersData->$filter);

                    $removedHash[$filter] = true;
                    $isChanged = true;
                }
            }

            $reportFilterList = $this->entityManager
                ->getRDBRepository(ReportFilter::ENTITY_TYPE)
                ->where([
                    'isActive' => true,
                    'entityType' => $entityType,
                ])
                ->order('order')
                ->find();

            $supportsFilterNames = $this->supportsFilterNames();

            foreach ($reportFilterList as $reportFilter) {
                $isChanged = true;
                $name = 'reportFilter' . $reportFilter->getId();

                $o = (object) [
                    'isReportFilter' => true,
                    'name' => $name,
                ];

                if (count($reportFilter->getLinkMultipleIdList('teams'))) {
                    $o->accessDataList = [
                        (object) ['teamIdList' => $reportFilter->getLinkMultipleIdList('teams')]
                    ];
                }

                $filterList[] = $o;

                unset($removedHash[$name]);

                $filtersData->$name = (object) [
                    'isReportFilter' => true,
                    'className' => ReportFilterUtil::class,
                    'id' => $reportFilter->getId(),
                ];

                if ($supportsFilterNames) {
                    unset($filtersData->$name->className);
                }

                $language->set($entityType, 'presetFilters', $name, $reportFilter->get('name'));
            }

            if ($isChanged) {
                $isAnythingChanged = true;

                $clientDefs = $this->metadata->getCustom('clientDefs', $entityType, (object) []);

                if (!empty($filterList)) {
                    if ($toAppend) {
                        array_unshift($filterList, '__APPEND__');
                    }

                    $clientDefs->filterList = $filterList;
                } else {
                    unset($clientDefs->filterList);
                }

                $this->metadata->saveCustom('clientDefs', $entityType, $clientDefs);

                if ($supportsFilterNames) {
                    $selectDefs = $this->metadata->getCustom('selectDefs', $entityType, (object) []);

                    if (!isset($selectDefs->primaryFilterClassNameMap)) {
                        $selectDefs->primaryFilterClassNameMap = (object) [];
                    }
                }

                $entityDefs = $this->metadata->getCustom('entityDefs', $entityType, (object) []);

                if (!isset($entityDefs->collection)) {
                    $entityDefs->collection = (object) [];
                }

                $entityDefs->collection->filters = $filtersData;

                $this->metadata->saveCustom('entityDefs', $entityType, $entityDefs);

                if ($supportsFilterNames) {
                    foreach (get_object_vars($filtersData) as $name => $ignored) {
                        $selectDefs->primaryFilterClassNameMap->$name = ReportPrimaryFilter::class;
                    }
                }

                foreach ($removedHash as $name => $item) {
                    $language->delete($entityType, 'presetFilters', $name);

                    if ($supportsFilterNames) {
                        unset($selectDefs->primaryFilterClassNameMap->$name);
                    }
                }

                if ($supportsFilterNames) {
                    $this->metadata->saveCustom('selectDefs', $entityType, $selectDefs);
                }
            }
        }

        if ($isAnythingChanged) {
            $language->save();

            $this->dataManager->clearCache();
        }
    }

    private function supportsFilterNames(): bool
    {
        $version = $this->config->get('version');

        return version_compare($version, '7.5.0') >= 0;
    }
}
