<?php

namespace IMATHUZH\Qfq\Core\Form\FormElement;

use IMATHUZH\Qfq\Core\Form\Form;
use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
use IMATHUZH\Qfq\Core\Helper\Support;
use function PHPUnit\Framework\throwException;

class CheckboxFormElement extends AbstractFormElement {
    public array $itemKeys = array();
    public array $itemValues = array();

    /**
     * Filter count for single checkboxes (from sql1 with 'count' column)
     */
    public ?int $filterCount = null;

    /**
     * @param $attributes
     * @param Form|null $form
     * @throws \CodeException
     * @throws \DbException
     * @throws \DownloadException
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     * @throws \UserFormException
     * @throws \UserReportException
     */
    public function __construct($attributes, ?Form $form = null) {
        parent::__construct($attributes, $form);

        $this->setKeysAndValues();
        $this->setDefaults();
        $this->setFilterCount();
    }

    /**
     * Fills $this->itemKeys and $this->itemValues
     *
     * @return void
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
     */
    private function setKeysAndValues(): void {
        HelperFormElement::getKeyValueListFromSqlEnumSpec($this->attributes, $this->itemKeys, $this->itemValues);
    }

    /**
     * Set meaningful default values
     *
     * @return void
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
     * @throws \UserReportException
     */
    private function setDefaults(): void {
        $this->attributes[FE_CHECKBOX_MODE] = $this->getCheckBoxMode();
        $this->htmlAttributes[HTML_ATTR_TYPE] = HTML_ATTR_TYPE_CHECKBOX;

        if ($this->attributes[FE_CHECKBOX_MODE] == FE_CHECKBOX_MODE_MULTI_STATE) {
            $this->htmlAttributes[HTML_ATTR_TYPE] = HTML_ATTR_TYPE_TEXT;
            $this->setMultiStateCheckBoxValues();

        } elseif ($this->attributes[FE_CHECKBOX_MODE] != FE_CHECKBOX_MODE_MULTI) {
            // Checked
            if (!isset($this->attributes[FE_CHECKBOX_CHECKED])) {
                if (isset($this->itemKeys[0])) {
                    // First element in $itemKey list
                    $this->attributes[FE_CHECKBOX_CHECKED] = $this->itemKeys[0];
                } else {
                    // Take column default value
                    $this->attributes[FE_CHECKBOX_CHECKED] = $this->store->getVar($this->attributes[FE_NAME], STORE_TABLE_DEFAULT);
                }
            }

            // Unchecked
            if (!isset($this->attributes[FE_CHECKBOX_UNCHECKED])) {
                if (isset($this->itemKeys[1])) {
                    $this->attributes[FE_CHECKBOX_UNCHECKED] = ($this->itemKeys[0] === $this->attributes[FE_CHECKBOX_CHECKED]) ? $this->itemKeys[1] : $this->itemKeys[0];
                } else {
                    $this->attributes[FE_CHECKBOX_UNCHECKED] = '';
                }
            }

            if ($this->attributes[FE_CHECKBOX_CHECKED] === $this->attributes[FE_CHECKBOX_UNCHECKED]) {
                throw new \UserFormException('FormElement: type=checkbox - checked and unchecked can\'t be the same: ' . $this->attributes[FE_CHECKBOX_CHECKED], ERROR_CHECKBOX_EQUAL);
            }
        }

        // Required Error
        if (isset($this->attributes[F_FE_DATA_REQUIRED_ERROR])) {
            $this->htmlAttributes[F_FE_DATA_REQUIRED_ERROR] = $this->attributes[F_FE_DATA_REQUIRED_ERROR];
        }

    }

    /**
     * Sets the options for a MultiState Checkbox by ether DB ENUM/SET or param checkBoxState
     *
     * example:
     * checkBoxState = VALUE:LABEL:FONT AWESOME ICON:ICON COLOR:BACKGROUND COLOR,no:No:fa-times:white:#2196f3,
     */
    private function setMultiStateCheckBoxValues() {
        $checkboxStates = empty($this->attributes[FE_MULTI_CHECKBOX_ITEMS]) ? false : $this->attributes[FE_MULTI_CHECKBOX_ITEMS];

        // emptyItemAtStart param is given and no value has been selected.
        $emptyItemAtStart = isset($this->attributes[FE_EMPTY_ITEM_AT_START]) && empty($this->value);
        $options = [];

        switch ($checkboxStates) {
            case false:
                // No explicit options provided from FE; use default values from ENUM/SET.
                $options[] = [
                    MULTI_STATE_CHECKBOX_VALUE => $this->itemValues[0],
                    MULTI_STATE_CHECKBOX_LABEL => $this->itemKeys[0],
                    MULTI_STATE_CHECKBOX_ICON => $emptyItemAtStart ? FONT_AWESOME_ICON_CHECKMARK : "",
                    MULTI_STATE_CHECKBOX_ICON_COLOR => $emptyItemAtStart ? MULTI_STATE_CHECKBOX_COLOR_WHITE : "",
                    MULTI_STATE_CHECKBOX_BACKGROUND_COLOR => $emptyItemAtStart ? MULTI_STATE_CHECKBOX_COLOR_BLUE : MULTI_STATE_CHECKBOX_COLOR_WHITE
                ];

                // Add remaining options from ENUM/SET.
                $totalItems = count($this->itemKeys);
                for ($i = 1; $i < $totalItems; $i++) {
                    $options[] = [
                        MULTI_STATE_CHECKBOX_VALUE => $this->itemValues[$i],
                        MULTI_STATE_CHECKBOX_LABEL => $this->itemKeys[$i],
                        MULTI_STATE_CHECKBOX_ICON => FONT_AWESOME_ICON_CHECKMARK,
                        MULTI_STATE_CHECKBOX_ICON_COLOR => MULTI_STATE_CHECKBOX_COLOR_WHITE,
                        MULTI_STATE_CHECKBOX_BACKGROUND_COLOR => MULTI_STATE_CHECKBOX_COLOR_BLUE
                    ];
                }

                break;

            default:
                // Options provided via FE; expected format: VALUE:LABEL:ICON:ICON_COLOR:BACKGROUND_COLOR.
                $rawOptions = explode(',', $checkboxStates);
                foreach ($rawOptions as $option) {
                    list($value, $label, $icon, $iconColor, $color) = explode(':', $option);
                    $options[] = [
                        MULTI_STATE_CHECKBOX_VALUE => $value,
                        MULTI_STATE_CHECKBOX_LABEL => $label,
                        MULTI_STATE_CHECKBOX_ICON => $icon,
                        MULTI_STATE_CHECKBOX_ICON_COLOR => $iconColor,
                        MULTI_STATE_CHECKBOX_BACKGROUND_COLOR => $color,
                    ];
                }
        }

        // Empty item is required at the start
        if ($emptyItemAtStart) {
            $emptyOption = [
                MULTI_STATE_CHECKBOX_VALUE => "",
                MULTI_STATE_CHECKBOX_LABEL => "",
                MULTI_STATE_CHECKBOX_ICON => "",
                MULTI_STATE_CHECKBOX_ICON_COLOR => "",
                MULTI_STATE_CHECKBOX_BACKGROUND_COLOR => MULTI_STATE_CHECKBOX_COLOR_WHITE,
            ];
            array_unshift($options, $emptyOption);
        }

        $this->attributes[FE_MULTI_CHECKBOX_ITEMS] = $options;
    }

    /** Get the state of the checkbox.
     * @return string
     */
    private function getCheckBoxMode(): string {
        // Checkbox mode is set
        if (isset($this->attributes[FE_CHECKBOX_MODE])) {
            return $this->attributes[FE_CHECKBOX_MODE];
        }
        // checkBoxState param is set expected 'multiState' checkbox
        if (isset($this->attributes[FE_MULTI_CHECKBOX_ITEMS])) {
            return FE_CHECKBOX_MODE_MULTI_STATE;
        }
        // more than 2 items expected 'multi' checkbox
        if (count($this->itemKeys) > 2) {
            return FE_CHECKBOX_MODE_MULTI;
        }
        // default single checkbox
        return FE_CHECKBOX_MODE_SINGLE;
    }

    /**
     * Set filter count from sql1 result (only for single checkboxes in inline filter mode)
     * sql1 should return one row with a 'count' column
     */
    private function setFilterCount(): void {
        // Only for single checkboxes in inline filter mode
        if ($this->attributes[FE_CHECKBOX_MODE] !== FE_CHECKBOX_MODE_SINGLE) {
            return;
        }
        if (!($this->attributes[FE_INLINE_FILTER] ?? false)) {
            return;
        }
        // sql1 should have one row with a 'count' column
        if (is_array($this->attributes[FE_SQL1]) && count($this->attributes[FE_SQL1]) > 0 && isset($this->attributes[FE_SQL1][0]['count'])) {
            $this->filterCount = (int)$this->attributes[FE_SQL1][0]['count'];
        }
    }
}