<?php

namespace Opentalent\OtTemplating\ViewHelpers\Members;

use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
use Opentalent\OtCore\Domain\Repository\MemberRepository;
use Opentalent\OtCore\Exception\ApiRequestException;
use Opentalent\OtCore\Logging\OtLogger;
use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;

/**
 *   This view helper give access to an array named according to the 'as' variable
 *   and which contains the members of the structure, eventually grouped by instruments
 *
 *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
 *
 *     <ot:members.getAll as="members"
 *                        organizationId="{settings.organizationId}">
 *          <f:debug>{members}</f:debug>
 *     </ot:members.getAll>
 *
 * @package Opentalent\OtTemplating\ViewHelpers
 */
class GetAllViewHelper extends OtAbstractViewHelper {

    use TemplateVariableViewHelperTrait;

    /**
     * >> Required to prevent typo3 to escape the html output
     * @var boolean
     */
    protected $escapeOutput = false;

    /**
     * @var MemberRepository
     *
     */
    protected MemberRepository $memberRepository;

    /**
     * -- This method is expected by Fluid --
     * Declares the viewhelper's parameters
     */
    public function initializeArguments()
    {
        $this->registerArgument(
            'as',
            'string',
            'Name of the returned array',
            true
        );
        $this->registerArgument(
            'organizationId',
            'integer',
            'Id of the current structure',
            true
        );
        $this->registerArgument(
            'groupByInstruments',
            'bool',
            'If true, returns an array of the form [instrument=>[members]], 
                       else the returned array is simply [members]',
            false,
            false
        );
    }

    /**
     * -- This method is expected by Fluid --
     * Renders the content as html
     *
     * @return string
     */
    public function render(): string
    {
        // Get current settings
        $as = $this->arguments['as'];
        $organizationId = $this->arguments['organizationId'];
        $groupByInstruments = $this->arguments['groupByInstruments'];

        if ($groupByInstruments) {
            $members = $this->getMembersAsGroups($organizationId);
        } else {
            $members = $this->getMembersAsSimpleList($organizationId);
        }

        $variables = [$as => $members];
        return $this->renderChildrenWithVariables($variables);
    }

    /**
     * Return the organization members as a simple array, sorted by name
     *
     * @param int $organizationId
     * @return array
     */
    private function getMembersAsSimpleList(int $organizationId): array
    {
        $members = $this->getMembers($organizationId);
        usort(
            $members,
            static function ($m1, $m2) { return strcmp($m1->getName(), $m2->getName()); }
        );
        return $members;
    }

    /**
     * Return the organization members as an array of the form :
     *   [
     *      'Instrument 1' => [Member1, Member2, ...],
     *      'Instrument 2' => [Member3, ...],
     *   ]
     *
     * Instruments keys are translated and sorted alphabetically, except for CONDUCTOR which shall appear first
     * Group members are sorted by name
     *
     * @param int $organizationId
     * @return array
     */
    private function getMembersAsGroups(int $organizationId): array
    {
        $members = $this->getMembers($organizationId);

        $stack = ['CONDUCTOR' => []];
        $conductors = [];

        foreach ($members as $member) {
            if ($member->getInstrument() === 'CONDUCTOR') {
                $conductors[] = $member;
                continue;
            }

            $instrument = ucfirst($this->translate($member->getInstrument()));
            if (!array_key_exists($instrument, $stack)) {
                $stack[$instrument] = [$member];
                continue;
            }

            $stack[$instrument][] = $member;
        }

        // remove empty instruments in stack 1
        $stack = array_filter($stack);

        // sort by instrument stack2
        ksort($stack);

        // sort groups by name
        foreach (array_values($stack) as $group) {
            usort(
                $group,
                static function ($m1, $m2) { return strcmp($m1->getName(), $m2->getName()); }
            );
        }

        // Add the conductors in first pos
        if ($conductors) {
            $stack = [ucfirst($this->translate('CONDUCTOR')) => $conductors] + $stack;
        }

        return $stack;
    }

    private function getMembers(int $organizationId): array
    {
        try {
            // Get members of the structure
            return $this->memberRepository->findByOrganizationId($organizationId)->getMembers();
        } catch (ApiRequestException $e) {
            OtLogger::error(sprintf('API Error: %s', $e->getMessage()));
            return [];
        }
    }

    /**
     * @param MemberRepository $memberRepository
     */
    public function injectMemberRepository(MemberRepository $memberRepository): void
    {
        $this->memberRepository = $memberRepository;
    }
}
