import { Component, onMounted, onWillDestroy, useRef, useState, useSubEnv } from "@odoo/owl";
import { Editor } from "./editor";
import { Toolbar } from "./main/toolbar/toolbar";
import { useChildRef } from "@web/core/utils/hooks";
import { LocalOverlayContainer } from "./local_overlay_container";
import { uniqueId } from "@web/core/utils/functions";

/**
 * @typedef { import("./editor").EditorConfig } EditorConfig
 **/

function copyCssRules(sourceDoc, targetDoc) {
    for (const sheet of sourceDoc.styleSheets) {
        const rules = [];
        for (const r of sheet.cssRules) {
            rules.push(r.cssText);
        }
        const cssRules = rules.join(" ");
        const styleTag = targetDoc.createElement("style");
        styleTag.appendChild(targetDoc.createTextNode(cssRules));
        targetDoc.head.appendChild(styleTag);
    }
}

export class Wysiwyg extends Component {
    static template = "html_editor.Wysiwyg";
    static components = { Toolbar, LocalOverlayContainer };
    static props = {
        config: { type: Object, optional: true },
        class: { type: String, optional: true },
        contentClass: { type: String, optional: true }, // on editable element
        style: { type: String, optional: true },
        toolbar: { type: Boolean, optional: true },
        iframe: { type: Boolean, optional: true },
        copyCss: { type: Boolean, optional: true },
        onLoad: { type: Function, optional: true },
        onBlur: { type: Function, optional: true },
        dynamicPlaceholder: { type: Boolean, optional: true },
    };

    static defaultProps = {
        onLoad: () => {},
        onBlur: () => {},
    };

    setup() {
        this.state = useState({
            showToolbar: false,
        });
        this.overlayRef = useChildRef();
        useSubEnv({
            localOverlayContainerKey: uniqueId("wysiwyg"),
        });
        const contentRef = useRef("content");
        this.editor = this.props.editor;
        const config = this.getEditorConfig();
        this.editor = new Editor(config, this.env.services);
        this.props.onLoad(this.editor);

        onMounted(() => {
            // now that component is mounted, editor is attached to el, and
            // plugins are started, so we can allow the toolbar to be displayed
            this.state.showToolbar = true;
            /** @type { any } **/
            const el = contentRef.el;

            if (el.tagName === "IFRAME") {
                // grab the inner body instead
                const attachEditor = () => {
                    if (!this.editor.isDestroyed) {
                        if (this.props.copyCss) {
                            copyCssRules(document, el.contentDocument);
                        }
                        const additionalClasses = el.dataset.class?.trim().split(" ");
                        if (additionalClasses) {
                            for (const c of additionalClasses) {
                                el.contentDocument.body.classList.add(c);
                            }
                        }
                        this.editor.attachTo(el.contentDocument.body);
                    }
                };
                if (el.contentDocument.readyState === "complete") {
                    attachEditor();
                } else {
                    // in firefox, iframe is not immediately available. we need to wait
                    // for it to be ready before mounting editor
                    el.addEventListener(
                        "load",
                        () => {
                            attachEditor();
                            this.render();
                        },
                        { once: true }
                    );
                }
            } else {
                this.editor.attachTo(el);
            }
        });
        onWillDestroy(() => this.editor.destroy(true));
    }

    getEditorConfig() {
        return {
            ...this.props.config,
            // TODO ABD TODO @phoenix: check if there is too much info in the wysiwyg env.
            // i.e.: env has X because of parent component,
            // embedded component descendant sometimes uses X from env which is set conditionally:
            // -> it will override the one one from the parent => OK.
            // -> it will not => the embedded component still has X in env because of its ancestors => Issue.
            embeddedComponentInfo: { app: this.__owl__.app, env: this.env },
            localOverlayContainers: {
                key: this.env.localOverlayContainerKey,
                ref: this.overlayRef,
            },
            disableFloatingToolbar: this.props.toolbar,
        };
    }
}
