import Input from "../Input";
import InputFactory, {InputConfig, InputImplConfig} from "../InputFactory";
import EventHandler from "../../../Utility/EventHandler";
import {AddCustomOptionEvent} from "./MultiSelect";

export default class Radio extends Input<any>{
    public options: {label: string, value: any, description?: string}[] = [];
    public customInput: Input<any>|null = null;
    public onAddCustomOption: EventHandler<AddCustomOptionEvent> = new EventHandler();

    constructor(params: Partial<Radio>) {
        super(params);
        Object.assign(this, params);
    }

    protected getOptionElement(config: {label: string, value: any, description?: string}): HTMLElement{
        const dataNode = this.getDataNode();
        const label = document.createElement("label");
        const name = document.createElement("span");
        name.classList.add("pad-radio-option-name");
        label.append(name);
        if (config.description) {
            const description = document.createElement("p");
            description.classList.add("pad-radio-option-description");
            description.innerText = config.description;
            label.append(description);
        }
        name.innerText = config.label;
        label.classList.add("pad-radio-option");

        const radioElement = document.createElement("input");
        radioElement.value = String(config.value);
        radioElement.checked = dataNode.valueOrDefault === config.value;
        radioElement.type = "radio";
        radioElement.name = this.getFullPathName();
        label.prepend(radioElement);

        dataNode.onChange.add(event => {
            radioElement.checked = event.value === config.value;
        });

        dataNode.onReset.add(() => {
            this.value = undefined;
            radioElement.checked = dataNode.valueOrDefault === config.value;
        });

        radioElement.addEventListener("change", () => {
            if (radioElement.checked) {
                dataNode.value = config.value;
            }
        });

        return label;
    }

    public placeHtml(container: HTMLElement): void {
        const questionContainer = this.getQuestionContainer();
        questionContainer.classList.add("pad-radio-input");

        if (this.customInput) {
            const addCustomButton = document.createElement("button");
            addCustomButton.innerText = "+";
            addCustomButton.type = "button";
            addCustomButton.classList.add("pad-add-custom-option");
            addCustomButton.addEventListener("click", () => {
                const dataNode = this.customInput?.getDataNode();
                if (!dataNode) return;
                if (dataNode.value == null || dataNode.value === "") return;
                if (!dataNode.isValid) return;
                if (this.options.some(option => option.value === dataNode.value)) {
                    this.getDataNode().value = dataNode.value;
                    this.value = dataNode.value;
                    return;
                }

                const newOption: {label: string, value: any} = {
                    label: String(dataNode.value),
                    value: dataNode.value
                };

                this.options.push(newOption);
                dataNode.value = undefined;

                this.onAddCustomOption.emit(new AddCustomOptionEvent(newOption));
                this.getDataNode().value = newOption.value;
                this.value = newOption.value;
            });

            const customInputContainer = document.createElement("div");
            customInputContainer.classList.add("pad-custom-input-container");
            customInputContainer.append(addCustomButton);
            this.customInput.placeHtml(customInputContainer);
            questionContainer.append(customInputContainer);
        }

        container.append(questionContainer);
        this.placedHtml.push(questionContainer);
    }

    protected getInputElement(): HTMLElement {
        const optionsContainer = document.createElement("div");
        optionsContainer.classList.add("pad-radio-options");

        this.onAddCustomOption.add((event) => {
            optionsContainer.append(this.getOptionElement(event.newOption));
        });

        this.options.forEach(option => {
            optionsContainer.append(this.getOptionElement(option));
        });

        return optionsContainer;
    }

}

export function addRadioToFactory(factory: InputFactory): void {
    factory.registerConstructor("radio", (config, params) => {
        const impl = config.impl;
        if (!isRadioConfig(impl)) return null;

        const radioParam: Partial<Radio> = params as Partial<Radio>;
        radioParam.options = impl.options;

        const radio = new Radio(radioParam);
        if (!impl.allowCustom) return radio;

        const customInputParam: Partial<Input<any>> = {};
        customInputParam.formBuilder = radio.formBuilder;

        const customInput = factory.createInput(impl.customInput, customInputParam);
        if (!customInput) return radio;

        radio.customInput = customInput;

        return radio;
    });
}

function isRadioConfig(config: InputImplConfig): config is RadioConfig{
    return config.type === "radio";
}

interface RadioConfig extends InputImplConfig{
    type: "radio";
    options: {label: string, value: any, description?: string}[];
    allowCustom: boolean;
    customInput: InputConfig;
}