"use strict";

import { Controller } from "@hotwired/stimulus";
import {
    ClassicEditor,
    Essentials,
    Bold,
    Italic,
    Underline,
    Strikethrough,
    Subscript,
    Superscript,
    Heading,
    Link,
    List,
    ListProperties,
    Paragraph,
    BlockQuote,
    Indent,
    IndentBlock,
    Alignment,
    HorizontalLine,
    RemoveFormat,
    SourceEditing,
    Style,
    GeneralHtmlSupport,
    Fullscreen,
    Undo,
    Image,
    ImageBlock,
    ImageInline,
    ImageInsert,
    ImageUpload,
    ImageResize,
    ImageToolbar,
    ImageCaption,
    ImageStyle,
    SimpleUploadAdapter,
    Plugin,
    ButtonView,
    Command,
} from "ckeditor5";
import "ckeditor5/ckeditor5.css";

// ── Plugin Section : wrapper <div class="editor-section"> ────────────
// Manipule le HTML via getData/setData pour insérer les wrappers.
// Le GHS préserve les div au round-trip. Aucun schema custom,
// donc Enter, ShiftEnter et SourceEditing fonctionnent normalement.

function isInSection(editor) {
    const viewSelection = editor.editing.view.document.selection;
    const pos = viewSelection.getFirstPosition();
    if (!pos) return false;
    return pos.getAncestors().some((a) => a.is?.("containerElement") && a.hasClass?.("editor-section"));
}

class SectionCommand extends Command {
    refresh() {
        this.isEnabled = true;
        this.value = isInSection(this.editor);
    }

    execute() {
        const editor = this.editor;

        // Insère une section via le clipboard (pas de setData = pas de reset)
        const sectionHtml = '<div class="editor-section"><p>&nbsp;</p></div>';

        const viewFragment = editor.data.processor.toView(sectionHtml);
        const modelFragment = editor.data.toModel(viewFragment);

        editor.model.change((writer) => {
            const root = editor.model.document.getRoot();
            const selection = editor.model.document.selection;
            const position = selection.getLastPosition();

            // Trouver le bloc parent au premier niveau (enfant direct de root)
            let topBlock = position?.parent;
            while (topBlock && topBlock.parent !== root) {
                topBlock = topBlock.parent;
            }

            if (!topBlock) return;

            // Insère la section après le bloc courant de premier niveau
            writer.insert(modelFragment, writer.createPositionAfter(topBlock));

            // Supprimer les paragraphes vides orphelins au premier niveau
            const toRemove = [];
            for (const child of root.getChildren()) {
                if (child.name === "paragraph" && child.isEmpty) {
                    toRemove.push(child);
                }
            }
            toRemove.forEach((p) => writer.remove(p));

            // Placer le curseur dans le dernier paragraphe de la nouvelle section
            const lastRootChild = root.getChild(root.childCount - 1);
            if (lastRootChild) {
                let target = lastRootChild;
                // Descendre dans le dernier enfant s'il a des children (cas GHS htmlDiv)
                while (target.childCount > 0) {
                    const last = target.getChild(target.childCount - 1);
                    if (last.name === "paragraph" || last.childCount > 0) {
                        target = last;
                    } else {
                        break;
                    }
                }
                writer.setSelection(target, "in");
            }
        });

        editor.editing.view.focus();
    }
}

class SectionPlugin extends Plugin {
    static get pluginName() {
        return "SectionPlugin";
    }

    static get requires() {
        return [GeneralHtmlSupport];
    }

    init() {
        const editor = this.editor;

        editor.commands.add("insertSection", new SectionCommand(editor));

        editor.ui.componentFactory.add("insertSection", (locale) => {
            const button = new ButtonView(locale);
            const command = editor.commands.get("insertSection");

            button.set({
                label: "Section",
                withText: true,
                tooltip: true,
            });

            button.bind("isOn").to(command, "value");
            button.bind("isEnabled").to(command, "isEnabled");

            button.on("execute", () => {
                editor.execute("insertSection");
            });

            return button;
        });

        // Rafraîchir l'état du bouton quand la sélection change
        editor.model.document.selection.on("change:range", () => {
            editor.commands.get("insertSection")?.refresh();
        });
    }
}

/*
 * Styles disponibles dans le dropdown "Style" de CKEditor.
 * Chaque entrée correspond à une classe définie dans editor.scss.
 */
const STYLE_DEFINITIONS = [
    // ── Block : Titres & paragraphes ──
    { name: "Titre section", element: "h2", classes: ["editor-title"] },
    { name: "Sous-titre", element: "h3", classes: ["editor-subtitle"] },
    { name: "Accroche", element: "p", classes: ["editor-lead"] },
    { name: "Paragraphe", element: "p", classes: ["editor-body"] },

    // ── Inline : Styles sémantiques ──
    { name: "Accent (prix)", element: "span", classes: ["editor-accent"] },
    { name: "Label souligné", element: "span", classes: ["editor-underline"] },
    { name: "Emphase italique", element: "span", classes: ["editor-emphasis"] },

    // ── Inline : Graisse ──
    { name: "Gras", element: "span", classes: ["editor-bold"] },
    { name: "Semi-gras", element: "span", classes: ["editor-semibold"] },
    { name: "Medium", element: "span", classes: ["editor-medium"] },
    { name: "Italique", element: "span", classes: ["editor-italic"] },

    // ── Inline : Tailles de texte ──
    { name: "Texte SM", element: "span", classes: ["editor-text-sm"] },
    { name: "Texte Base", element: "span", classes: ["editor-text-base"] },
    { name: "Texte LG", element: "span", classes: ["editor-text-lg"] },
    { name: "Texte XL", element: "span", classes: ["editor-text-xl"] },
    { name: "Texte 2XL", element: "span", classes: ["editor-text-2xl"] },

    // ── Inline : Couleurs ──
    { name: "Couleur accent", element: "span", classes: ["editor-text-accent"] },
    { name: "Couleur accent secondaire", element: "span", classes: ["editor-text-accent-secondary"] },
    { name: "Couleur primaire", element: "span", classes: ["editor-text-primary"] },
];

// ── Plugins communs à toutes les configs ─────────────────────────────
const BASE_PLUGINS = [
    Essentials,
    Bold,
    Italic,
    Underline,
    Strikethrough,
    Subscript,
    Superscript,
    Heading,
    Link,
    List,
    ListProperties,
    Paragraph,
    BlockQuote,
    Indent,
    IndentBlock,
    Alignment,
    HorizontalLine,
    RemoveFormat,
    Undo,
    GeneralHtmlSupport,
    Fullscreen,
    Image,
    ImageBlock,
    ImageInline,
    ImageInsert,
    ImageUpload,
    ImageResize,
    ImageToolbar,
    ImageCaption,
    ImageStyle,
    SimpleUploadAdapter,
    SectionPlugin,
];

export default class extends Controller {
    static values = {
        config: { type: String, default: "minimal" },
        maxlength: { type: Number, default: 0 },
        language: { type: String, default: "fr" },
    };

    connect() {
        ClassicEditor.create(this.element, this.#getConfig())
            .then((editor) => {
                this.editor = editor;
                editor.ui.view.editable.element.classList.add("actualite-content");
                this.#setupEnterKey(editor);
                this.#setupCharCount(editor);
                this.#setupInvalidState(editor);
                this.#setupSync(editor);
            })
            .catch((error) => {
                console.error("Erreur CKEditor:", error);
            });
    }

    disconnect() {
        if (this.editor) {
            this.editor.destroy().catch((error) => console.error("Erreur destroy:", error));
        }
    }

    // ── Enter / Shift+Enter ─────────────────────────────────────────
    #setupEnterKey(editor) {
        editor.editing.view.document.on(
            "keydown",
            (evt, data) => {
                if (data.keyCode === 13) {
                    if (data.shiftKey) {
                        editor.execute("shiftEnter");
                    } else {
                        editor.execute("enter");
                    }
                    data.preventDefault();
                    evt.stop();
                }
            },
            { priority: "highest" },
        );
    }

    // ── Compteur de caractères ───────────────────────────────────────
    #setupCharCount(editor) {
        const charCountWrapper = this.element.parentElement?.querySelector(".ck-editor-char");
        const charCountEl = this.element.parentElement?.querySelector("[data-char-count]");
        if (!charCountEl) return;

        const updateCounts = () => {
            const tempDiv = document.createElement("div");
            tempDiv.innerHTML = editor.getData();
            const length = (tempDiv.textContent || "").length;
            charCountEl.textContent = length;
            return length;
        };

        updateCounts();
        editor.model.document.on("change:data", () => {
            if (charCountWrapper && this.maxlengthValue > 0) {
                charCountWrapper.classList.toggle("is-text-error", updateCounts() > this.maxlengthValue);
            } else {
                updateCounts();
            }
        });
    }

    // ── Style d'erreur ───────────────────────────────────────────────
    #setupInvalidState(editor) {
        const ckEditorWrapper = editor.ui.view.editable.element.closest(".ck-editor");
        if (!ckEditorWrapper) return;

        if (this.element.classList.contains("is-invalid")) {
            ckEditorWrapper.classList.add("is-invalid");
        }
        ckEditorWrapper.id = this.element.id + "_textarea";
    }

    // ── Sync vers le textarea (formulaires Symfony / LiveComponent) ──
    #setupSync(editor) {
        editor.editing.view.document.on("blur", () => {
            this.element.value = editor.getData();
            this.element.dispatchEvent(new Event("change", { bubbles: true }));
        });
    }

    // ── Configurations ───────────────────────────────────────────────
    #getConfig() {
        const lang = this.languageValue;

        const htmlSupport = {
            allow: [
                { name: "span", classes: true },
                { name: "div", classes: true },
                { name: "h2", classes: true },
                { name: "h3", classes: true },
                { name: "h4", classes: true },
                { name: "p", classes: true },
            ],
        };

        const heading = {
            options: [
                { model: "paragraph", title: "Paragraphe", class: "ck-heading_paragraph" },
                {
                    model: "heading2",
                    view: { name: "h2", classes: ["editor-title"] },
                    title: "Titre",
                    class: "ck-heading_heading2",
                },
                {
                    model: "heading3",
                    view: { name: "h3", classes: ["editor-subtitle"] },
                    title: "Sous-titre",
                    class: "ck-heading_heading3",
                },
                { model: "heading4", view: "h4", title: "Titre petit", class: "ck-heading_heading4" },
            ],
        };

        const style = {
            definitions: STYLE_DEFINITIONS,
        };

        const simpleUpload = {
            uploadUrl: "/dashboard/ckeditor/upload",
            withCredentials: true,
            headers: {
                "X-Window-Id": sessionStorage.getItem("window_id") || "",
            },
        };

        const image = {
            toolbar: [
                "imageStyle:inline",
                "imageStyle:block",
                "imageStyle:side",
                "|",
                "imageTextAlternative",
                "|",
                "toggleImageCaption",
                "|",
                "resizeImage",
            ],
            resizeOptions: [
                { name: "resizeImage:original", value: null, label: "Original" },
                { name: "resizeImage:25", value: "25", label: "25%" },
                { name: "resizeImage:50", value: "50", label: "50%" },
                { name: "resizeImage:75", value: "75", label: "75%" },
            ],
        };

        const indentBlock = {
            offset: 2,
            unit: "em",
        };

        const list = {
            properties: {
                styles: true,
                startIndex: true,
                reversed: true,
            },
        };

        const configs = {
            minimal: {
                licenseKey: "GPL",
                plugins: BASE_PLUGINS,
                toolbar: ["bold", "italic", "link", "|", "undo", "redo", "|", "fullscreen"],
                language: lang,
                htmlSupport,
                indentBlock,
                list,
                simpleUpload,
                image,
            },
            full: {
                licenseKey: "GPL",
                plugins: [...BASE_PLUGINS, Style, SourceEditing],
                toolbar: [
                    "insertSection",
                    "|",
                    "heading",
                    "|",
                    "style",
                    "|",
                    "bold",
                    "italic",
                    "underline",
                    "strikethrough",
                    "|",
                    "subscript",
                    "superscript",
                    "|",
                    "link",
                    "|",
                    "insertImage",
                    "|",
                    "bulletedList",
                    "numberedList",
                    "|",
                    "alignment",
                    "|",
                    "blockQuote",
                    "horizontalLine",
                    "|",
                    "indent",
                    "outdent",
                    "|",
                    "removeFormat",
                    "|",
                    "sourceEditing",
                    "|",
                    "undo",
                    "redo",
                    "|",
                    "fullscreen",
                ],
                language: lang,
                heading,
                style,
                htmlSupport,
                indentBlock,
                list,
                simpleUpload,
                image,
            },
            clean: {
                licenseKey: "GPL",
                plugins: BASE_PLUGINS,
                toolbar: [],
                language: lang,
                htmlSupport,
                indentBlock,
                list,
                simpleUpload,
                image,
            },
        };

        return configs[this.configValue] || configs.minimal;
    }
}
