<?php
namespace Helpers\Form;

use Helpers\Outil\ArrayHelper;
use Helpers\Parametres\Constante;
use Helpers\Trad\ITrad;

class FormBuilder extends AbsFormService
{
    /** @var array<mixed> */
    private array $listInput;

    public function __construct()
    {
        $this->listInput = [];
    }

    /**
     * @param array<mixed> $option
     */
    public function addSwitch(string $name, array $option = []) :self
    {
        $this->listInput[] = $this->switch($name, $option);
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addText(string $name, int $max, array $option = []) :self
    {
        $this->listInput[] = $this->simpleText($name, $max, $option);
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addTextarea(string $name, int $max = 3000, array $option = []) :self
    {
        $this->listInput[] = $this->simpleText(
            $name,
            $max,
            ['type' => 'TEXTAREA'] + $option
        );
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addDate(string $name, array $option = []) :self
    {
        $this->listInput[] = $this->simpleDate($name, $option);
        return $this;
    }

    /**
     * @param array<string> $listChoices
     * @param array<mixed> $option
     */
    public function addSelect(string $name, $listChoices, array $option = []) :self
    {
        // TODO : handle listChoices callback
        $this->listInput[] = $this->simpleSelect($name, $listChoices, $option);
        return $this;
    }

    /**
     * @param $fnChoiceLabel function (ITrad $trad) => map of choice label
     * @param $fnChoiceValue function () => array of choice
     * @param array<mixed> $option
     */
    public function addCustomSelect(
        string $name,
        callable $fnChoiceLabel,
        callable $fnChoiceValue,
        array $option = []
    ) :self {
        $this->listInput[] = (object) ($option + [
            'name' => $name, // Active directory
            'type' => 'SELECT',
            'choices' => $fnChoiceLabel,
            'choices_value' => $fnChoiceValue,
        ]);
        return $this;
    }

    /**
     * @param array<mixed> $option TODO : document zoom_filter
     */
    public function addZoom(string $name, string $zoomPath, array $option = []) :self
    {
        $this->listInput[] = $this->simpleZoom($name, $zoomPath, $option);
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addMultipick(string $name, string $zoomPath, array $option = []) :self
    {
        $this->listInput[] = (object) ([
            'name' => $name,
            'type' => 'MULTIPICK',
            'zoom_path' => $zoomPath,
        ] + $option);
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addFloat(string $name, string $format, array $option = []) :self
    {
        $this->listInput[] = $this->simpleFloat($name, $format, $option);
        return $this;
    }

    /**
     * TODO : add devise
     * @param array<mixed> $option
     */
    public function addAmount(string $name, array $option = []) :self
    {
        return $this->addFloat($name, Constante::formatMontant, $option);
    }

    /**
     * @param array<mixed> $option
     */
    public function addInteger(string $name, array $option = []) :self
    {
        // TODO : improve with type 'INT'
        $this->listInput[] = $this->simpleInteger($name, $option);
        return $this;
    }

    /**
     * @param array<mixed> $option
     */
    public function addFile(string $name, array $option = []) :self
    {
        $this->listInput[] = (object) ([
            'name' => $name,
            'type' => 'FILE',
            'template' => '@GlobalApplication/Input/inputFile', // This template prevent the input name to be suffixed with 'import'
        ] + $option);
        return $this;
    }
    
    /**
     * @param array<mixed> $option
     */
    public function addCustomInput(string $name, string $type, array $option = []) :self
    {
        $this->listInput[] = (object) ($option + [
            'name' => $name,
            'type' => $type,
        ]);
        return $this;
    }

    /**
     * @param array<\stdClass> $subform
     * @param array<mixed> $option
     */
    public function addTable(string $name, array $subform, array $option = []) :self
    {
        $this->listInput[] = (object) ([
            'type' => 'TABLE',
            'name' => $name,
            'listChild' => $subform,
        ] + $option);

        return $this;
    }

    /**
     * @param array<\stdClass> $subform
     * @param array<mixed> $option
     */
    public function addGroup(string $name, array $subform, array $option = []) :self
    {
        $this->listInput[] = $this->createGroup($name, $subform, $option);
        return $this;
    }

    /**
     * @param array<\stdClass>[] $mapSubform
     * @param array<mixed> $option
     */
    public function addBranch(string $name, string $refInputChoice, array $mapSubform, array $option = []) :self
    {
        $this->listInput[] = (object) ([
            'type' => 'BRANCH',
            'name' => $name,
            'mapSubform' => $mapSubform,
            'branch_dependency' => $refInputChoice,
        ] + $option);
            
        return $this;
    }

    /**
     * @return array<\stdClass>
     */
    public function build()
    {
        return array_column($this->listInput, null, 'name');
    }

    /**
     * @return \stdClass|null
     */
    protected function get(string $name) :?object
    {
        return ArrayHelper::findFirst($this->listInput, function ($input) use ($name) {
            return $input->name == $name;
        });
    }

    protected function remove(string $name) :self
    {
        $this->listInput = array_filter($this->listInput, function ($input) use ($name) {
            return $input->name != $name;
        });
        return $this;
    }

    /**
     * @return array<\stdClass>
     */
    protected function list() :array
    {
        return $this->listInput;
    }
}
