<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\ActivityBundle\Export\Export;

use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityPresenceRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\Helper\DateTimeHelper;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
use Chill\MainBundle\Export\Helper\UserHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class ListActivityHelper
{
    final public const MSG_KEY = 'export.list.activity.';

    public function __construct(
        private readonly ActivityPresenceRepositoryInterface $activityPresenceRepository,
        private readonly ActivityTypeRepositoryInterface $activityTypeRepository,
        private readonly DateTimeHelper $dateTimeHelper,
        private readonly LabelPersonHelper $labelPersonHelper,
        private readonly LabelThirdPartyHelper $labelThirdPartyHelper,
        private readonly TranslatorInterface $translator,
        private readonly TranslatableStringHelperInterface $translatableStringHelper,
        private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper,
        private readonly UserHelper $userHelper,
    ) {}

    public function addSelect(QueryBuilder $qb): void
    {
        $qb
            ->addSelect('activity.id AS id')
            ->addSelect('activity.date')
            ->addSelect('IDENTITY(activity.activityType) AS typeName')
            ->leftJoin('activity.reasons', 'reasons')
            ->addSelect('AGGREGATE(reasons.name) AS listReasons')
            ->leftJoin('activity.persons', 'actPerson')
            ->addSelect('AGGREGATE(actPerson.id) AS personsIds')
            ->addSelect('AGGREGATE(actPerson.id) AS personsNames')
            ->leftJoin('activity.users', 'users_u')
            ->addSelect('AGGREGATE(users_u.id) AS usersIds')
            ->addSelect('AGGREGATE(JSON_BUILD_OBJECT(\'uid\', users_u.id, \'d\', activity.date)) AS usersNames')
            ->leftJoin('activity.thirdParties', 'thirdparty')
            ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesIds')
            ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesNames')
            ->addSelect('IDENTITY(activity.attendee) AS attendeeName')
            ->addSelect('activity.durationTime')
            ->addSelect('activity.travelTime')
            ->addSelect('activity.emergency')
            ->leftJoin('activity.location', 'location')
            ->addSelect('location.name AS locationName')
            ->addSelect('activity.sentReceived')
            ->addSelect('activity.comment.comment AS commentText')
            ->addSelect('activity.comment.date AS commentDate')
            ->addSelect('JSON_BUILD_OBJECT(\'uid\', activity.comment.userId, \'d\', activity.comment.date) AS commentUser')
            ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
            ->addSelect('activity.createdAt')
            ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
            ->addSelect('activity.updatedAt')
            ->addGroupBy('activity.id')
            ->addGroupBy('location.id');
    }

    public function buildForm(FormBuilderInterface $builder) {}

    public function getAllowedFormattersTypes()
    {
        return [FormatterInterface::TYPE_LIST];
    }

    public function getLabels($key, array $values, $data)
    {
        return match ($key) {
            'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
            'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
            'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY.$key),
            'commentDate' => $this->dateTimeHelper->getLabel(self::MSG_KEY.'comment_date'),
            'commentUser' => $this->userHelper->getLabel($key, $values, self::MSG_KEY.'comment_user'),
            'attendeeName' => function ($value) {
                if ('_header' === $value) {
                    return 'Attendee';
                }

                if (null === $value || null === $presence = $this->activityPresenceRepository->find($value)) {
                    return '';
                }

                return $this->translatableStringHelper->localize($presence->getName());
            },
            'listReasons' => $this->translatableStringLabelHelper->getLabelMulti($key, $values, 'Activity Reasons'),
            'typeName' => function ($value) {
                if ('_header' === $value) {
                    return 'Activity type';
                }

                if (null === $value || null === $type = $this->activityTypeRepository->find($value)) {
                    return '';
                }

                return $this->translatableStringHelper->localize($type->getName());
            },
            'usersNames' => $this->userHelper->getLabelMulti($key, $values, self::MSG_KEY.'users name'),
            'usersIds', 'thirdPartiesIds', 'personsIds' => static function ($value) use ($key) {
                if ('_header' === $value) {
                    return match ($key) {
                        'usersIds' => self::MSG_KEY.'users ids',
                        'thirdPartiesIds' => self::MSG_KEY.'third parties ids',
                        'personsIds' => self::MSG_KEY.'persons ids',
                    };
                }

                $decoded = json_decode((string) $value, null, 512, JSON_THROW_ON_ERROR);

                return implode(
                    '|',
                    array_unique(
                        array_filter($decoded, static fn (?int $id) => null !== $id),
                        \SORT_NUMERIC
                    )
                );
            },
            'personsNames' => $this->labelPersonHelper->getLabelMulti($key, $values, self::MSG_KEY.'persons name'),
            'thirdPartiesNames' => $this->labelThirdPartyHelper->getLabelMulti($key, $values, self::MSG_KEY.'thirds parties'),
            'sentReceived' => function ($value) {
                if ('_header' === $value) {
                    return self::MSG_KEY.'sent received';
                }

                if (null === $value) {
                    return '';
                }

                return $this->translator->trans($value);
            },
            default => function ($value) use ($key) {
                if ('_header' === $value) {
                    return self::MSG_KEY.$key;
                }

                if (null === $value) {
                    return '';
                }

                return $this->translator->trans((string) $value);
            },
        };
    }

    public function getQueryKeys($data)
    {
        return [
            'id',
            'date',
            'typeName',
            'listReasons',
            'attendeeName',
            'durationTime',
            'travelTime',
            'emergency',
            'locationName',
            'sentReceived',
            'personsIds',
            'personsNames',
            'usersIds',
            'usersNames',
            'thirdPartiesIds',
            'thirdPartiesNames',
            'commentText',
            'commentDate',
            'commentUser',
            'createdBy',
            'createdAt',
            'updatedBy',
            'updatedAt',
        ];
    }

    public function getResult($query, $data)
    {
        return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
    }

    public function getType(): string
    {
        return Declarations::ACTIVITY;
    }

    public function supportsModifiers()
    {
        return [
            Declarations::ACTIVITY,
        ];
    }
}
