<?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\ZimbraBundle\Calendar\Connector\ZimbraConnector;

use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\CalendarBundle\Entity\Invite;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Templating\Entity\AddressRender;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
use Zimbra\Common\Enum\FreeBusyStatus;
use Zimbra\Common\Enum\InviteClass;
use Zimbra\Common\Enum\InviteStatus;
use Zimbra\Common\Enum\Transparency;
use Zimbra\Mail\Struct\InviteComponent;

/**
 * Class responsible for creating Zimbra invite components based on calendar data.
 */
final readonly class CreateZimbraComponent
{
    public function __construct(
        private PersonRenderInterface $personRender,
        private AddressRender $addressRender,
        private DateConverter $dateConverter,
        private TranslatorInterface $translator,
        private Environment $twig,
    ) {}

    /**
     * Creates a Zimbra invite component from the provided calendar object.
     *
     * The method initializes a new InviteComponent object, sets its properties
     * including name, free/busy status, status, classification, transparency,
     * all-day and draft status, as well as start and end times. If the calendar
     * contains a location, it also sets the location for the invite component.
     *
     * @param Calendar|CalendarRange|Invite $calendar a calendar object containing event data
     *
     * @return InviteComponent the configured Zimbra invite component
     */
    public function createZimbraInviteComponentFromCalendar(Calendar|CalendarRange|Invite $calendar): InviteComponent
    {
        if ($calendar instanceof Calendar) {
            $subject = '[Chill] '.
                implode(
                    ', ',
                    $calendar->getPersons()->map(fn (Person $p) => $this->personRender->renderString($p, ['addAge' => false]))->toArray()
                );
            $content = $this->twig->render('@ChillZimbra/ZimbraComponent/calendar_content.txt.twig', ['calendar' => $calendar]);
        } elseif ($calendar instanceof Invite) {
            $subject = '[Chill] '.
                '('.$this->translator->trans('remote_calendar.calendar_invite_statement_in_calendar').') '.
                implode(
                    ', ',
                    $calendar->getCalendar()->getPersons()->map(fn (Person $p) => $this->personRender->renderString($p, ['addAge' => false]))->toArray()
                );
            $content = $this->twig->render('@ChillZimbra/ZimbraComponent/invitation_content.txt.twig', ['calendar' => $calendar->getCalendar()]);
        } else {
            // $calendar is an instanceof CalendarRange
            $subject = $this->translator->trans('remote_calendar.calendar_range_title');
            $content = '';
        }

        if ($calendar instanceof Invite) {
            $startDate = $calendar->getCalendar()->getStartDate();
            $endDate = $calendar->getCalendar()->getEndDate();
            $location = $calendar->getCalendar()->getLocation();
            $hasLocation = $calendar->getCalendar()->hasLocation();
            $isPrivate = $calendar->getCalendar()->getAccompanyingPeriod()?->isConfidential() ?? false;
        } else {
            $startDate = $calendar->getStartDate();
            $endDate = $calendar->getEndDate();
            $location = $calendar->getLocation();
            $hasLocation = $calendar->hasLocation();
            $isPrivate = $calendar->getAccompanyingPeriod()?->isConfidential() ?? false;
        }

        $comp = new InviteComponent();
        $comp->setName($subject);
        $comp->setDescription($content);
        $comp->setFreeBusy(FreeBusyStatus::BUSY);
        $comp->setStatus(InviteStatus::CONFIRMED);
        $comp->setCalClass($isPrivate ? InviteClass::PRI : InviteClass::PUB);
        $comp->setTransparency(Transparency::OPAQUE);
        $comp->setIsAllDay(false);
        $comp->setIsDraft(false);
        $comp->setDtStart($this->dateConverter->phpToZimbraDateTime($startDate));
        $comp->setDtEnd($this->dateConverter->phpToZimbraDateTime($endDate));

        if ($hasLocation) {
            $comp
                ->setLocation($this->createLocationString($location));
        }

        return $comp;
    }

    private function createLocationString(Location $location): string
    {
        $str = '';

        if ('' !== ($loc = (string) $location->getName())) {
            $str .= $loc;
        }

        if ($location->hasAddress()) {
            if ('' !== $str) {
                $str .= ', ';
            }

            $str .= $this->addressRender->renderString($location->getAddress(), ['separator' => ', ']);
        }

        return $str;
    }
}
