
import { djbase, djclass } from "../declare";
import { attach, createElement } from "../tsx";
import { DjxWidgetBase } from "../tsx/DjxWidgetBase";
import _FormMixin from "./_FormMixin";


/** This widget represents a document section containing interactive controls
 * for submitting information.
 */
@djclass
export default class Form<T extends object> extends djbase(DjxWidgetBase, _FormMixin) {

    /** Name of form for scripting. */
    name?: string;

    /** The URL that processes the form submission.  */
    action?: string;

    /** The HTTP method to submit the form with. */
    method?: "POST" | "GET" | "DIALOG";

    /** If the value of the method attribute is post, enctype is the MIME type
     * of the form submission.
     */
    enctype?: string;

    /** Comma-separated content types the server accepts.
     * 
     * @deprecated This attribute has been deprecated and should not be used.
     *   Instead, use the accept attribute on <input type=file> elements.
    */
    accept?: string;

    /** Space-separated character encodings the server accepts. The browser
     * uses them in the order in which they are listed. 
     */
    "accept-charset"?: string;

    /** Indicates where to display the response after submitting the form. It
     * is a name/keyword for a browsing context (for example, tab, window, or
     * iframe).
     */
    target?: string;

    /** Indicates whether input elements can by default have their values
     * automatically completed by the browser. `autocomplete` attributes on form
     * elements override it on `<form>`.
     */
    autocomplete?: "off" | "on";


    /** This Boolean attribute indicates that the form shouldn't be validated
     * when submitted. If this attribute is not set the form is validated, it
     * can be overridden by a `formnovalidate` attribute on a `<button>`,
     * `<input type="submit">`, or `<input type="image">` element belonging to
     * the form.
     */
    novalidate?: boolean;

    value?: T;

    containerNode?: HTMLFormElement;

    _resetting = false;

    constructor(opts?: Partial<Form<T>>, srcRef?: string | Node) {
        super(opts, srcRef);
    }

    render() {
        return <form onreset={this._onReset} onsubmit={this._onSubmit} ref={attach(this, "containerNode")}>
        </form>;
    }

    postCreate() {
        super.postCreate();

        this.watch("value", (_prop, _oldValue, newValue) => this.onValueChange(newValue));
    }

    private readonly _onReset = (evt: Event) => {
        if (!this._resetting) {
            // re-dispatch event to replace default reset behavior
            try {
                this._resetting = true;
                evt.preventDefault();
                if(this.containerNode?.dispatchEvent(new Event(evt.type, evt)))
                    super.reset();
            } finally {
                this._resetting = false;
            }
        } else {
            this.onReset(evt);
        }
    };

    private readonly _onSubmit = (evt: SubmitEvent) => {
        if(!this.novalidate && !this.validate()) {
            evt.preventDefault();
            evt.stopPropagation();
        } else {
            this.onSubmit(evt);
        }
    };

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onSubmit(evt: Event) {
    }

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onReset(evt: Event) {
    }

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onValueChange(value?: T) {
    }

    /** Resets the form. */
    reset() {
        if (!this.containerNode)
            throw new Error("Can't reset the destroyed form");
        this.containerNode.reset();
    }

    /** Programmatically submits form, no additional events emitted
     * or checks made.
     */
    submit() {
        if (!this.containerNode)
            throw new Error("Can't submit the destroyed form");
        this.containerNode.submit();
    }
}
