<template>
  <div :id="'editor_' + uid"></div>
</template>

<script>
  /* global CKEDITOR */

  import mixinPage from '../../mixins/page';
  import util from '../../libs/util';
  import Setting from '../../setting';

  let ckScriptPromise = null;

  function debounce(fn, wait = 80) {
    let timer = null;
    return (...args) => {
      clearTimeout(timer);
      timer = setTimeout(() => fn(...args), wait);
    };
  }

  function loadCKEditorScript(scriptUrl) {
    if (window.CKEDITOR) {
      return Promise.resolve(window.CKEDITOR);
    }
    if (ckScriptPromise) {
      return ckScriptPromise;
    }
    ckScriptPromise = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = scriptUrl;
      script.async = true;
      script.onload = () => resolve(window.CKEDITOR);
      script.onerror = () => reject(new Error(`Failed to load CKEditor script: ${scriptUrl}`));
      document.head.appendChild(script);
    });
    return ckScriptPromise;
  }

  function withBaseUrl(path) {
    if (!path) return path;
    if (/^https?:\/\//i.test(path)) return path;
    const base = (import.meta.env.BASE_URL || '/').replace(/\/?$/, '/');
    return path.startsWith('/') ? `${base}${path.replace(/^\/+/, '')}` : `${base}${path}`;
  }

  export default {
    mixins: [mixinPage],
    props: {
      modelValue: {
        type: String,
        default: ''
      },
      // 只读
      readonly: {
        type: Boolean,
        default: false
      },
      width: {
        type: Number
      },
      height: {
        type: Number
      },
      editorUrl: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        uid: Math.random().toString().replace('.', ''),
        instance: null, // 当前实例
        $_destroyed: false // 是否销毁
      };
    },
    computed: {
      /** 仅一处配置：prop 优先，否则 Setting.editorUrl（CDN 或本地同一字段） */
      resolvedEditorUrl() {
        const raw = (this.editorUrl || '').trim() || (Setting.editorUrl || '').trim();
        return withBaseUrl(raw);
      },
      config() {
        const token = util.cookies.get('token');
        return {
          // 禁用 public/ckeditor/config.js，避免其 toolbarGroups/removeButtons 覆盖组件配置
          customConfig: '',
          versionCheck: false,
          height: this.height == null ? 500 : this.height,
          fileTools_requestHeaders: {
            Authorization: 'Bearer ' + token
          },
          // 显式工具栏（按你截图的经典三行布局）
          toolbar: [
            {
              name: 'document',
              items: ['Source', '-', 'Save', 'NewPage', 'DocProps', 'Preview', 'Print', '-', 'Templates']
            },
            '/',
            {
              name: 'clipboard',
              items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']
            },
            {
              name: 'editing',
              items: ['Find', 'Replace', '-', 'SelectAll', '-', 'Scayt', 'RemoveFormat']
            },
            {
              name: 'basicstyles',
              items: ['Bold', 'Italic', 'Underline', 'Strike', '-', 'Subscript', 'Superscript', '-', 'CopyFormatting']
            },
            {
              name: 'paragraph',
              items: [
                'NumberedList',
                'BulletedList',
                '-',
                'Outdent',
                'Indent',
                '-',
                'Blockquote',
                'CreateDiv',
                '-',
                'JustifyLeft',
                'JustifyCenter',
                'JustifyRight',
                'JustifyBlock',
                '-',
                'BidiLtr',
                'BidiRtl',
                '-',
                'Language'
              ]
            },
            { name: 'links', items: ['Link', 'Unlink'] },
            '/',
            {
              name: 'insert',
              items: ['Image', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']
            },
            { name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
            { name: 'colors', items: ['TextColor', 'BGColor'] },
            { name: 'tools', items: ['Maximize', 'ShowBlocks'] }
          ],
          // 显式拉起这些按钮对应插件，防止发行包默认集合差异导致按钮不显示
          extraPlugins: 'font,colorbutton,justify,copyformatting,showblocks,find,templates,div,bidi,language,save,iframe,pagebreak,docprops,preview',
          removeButtons: '',
          toolbarCanCollapse: false,
          toolbarStartupExpanded: true,
          filebrowserUploadUrl: this.apiBaseURL + 'api/CKEditor?r=1',
          removePlugins: 'easyimage,cloudservices,exportpdf',
          clipboard_handleImages: false
        };
      }
    },
    mounted() {
      loadCKEditorScript(this.resolvedEditorUrl)
        .then(namespace => {
          this.$emit('namespaceloaded', namespace);

        if (this.$_destroyed) {
          return;
        }

        const config = this.prepareConfig();

        CKEDITOR.replace('editor_' + this.uid, config);
        })
        .catch(err => {
          console.error(err);
        });
    },
    beforeUnmount() {
      if (this.instance) {
        this.instance.destroy();
      }

      this.$_destroyed = true;
    },
    watch: {
      modelValue(val) {
        if (this.instance && this.instance.getData() !== val) {
          this.instance.setData(val);
        }
      },

      readonly(val) {
        if (this.instance) {
          this.instance.setReadOnly(val);
        }
      }
    },
    methods: {
      prepareConfig() {
        const config = this.config || {};
        config.on = config.on || {};

        if (config.delayIfDetached === undefined) {
          config.delayIfDetached = true;
        }
        if (this.readonly !== null) {
          config.readOnly = this.readonly;
        }

        const userInstanceReadyCallback = config.on.instanceReady;

        config.on.instanceReady = evt => {
          this.instance = evt.editor;

          this.$nextTick().then(() => {
            this.prepareComponentData();

            if (userInstanceReadyCallback) {
              userInstanceReadyCallback(evt);
            }
          });
        };

        return config;
      },
      prepareComponentData() {
        const data = this.modelValue;

        this.instance.fire('lockSnapshot');

        this.instance.setData(data, {
          callback: () => {
            this.$_setUpEditorEvents();

            const newData = this.instance.getData();

            // Locking the snapshot prevents the 'change' event.
            // Trigger it manually to update the bound data.
            if (data !== newData) {
              // this.$once('input', () => {
              //   this.$emit('ready', this.instance);
              // });

              this.$emit('update:modelValue', newData);
            }

            this.instance.fire('unlockSnapshot');
          }
        });
      },
      $_setUpEditorEvents() {
        const editor = this.instance;

        editor.on(
          'change',
          debounce(evt => {
            const data = editor.getData();

            // Editor#change event might be fired without an actual data change.
            if (this.modelValue !== data) {
              // The compatibility with the v-model and general Vue.js concept of input–like components.
              this.$emit('update:modelValue', data, evt, editor);
            }
          }, 80)
        );

        editor.on('fileUploadResponse', evt => {
          setTimeout(() => {
            const data = editor.getData();

            // Editor#change event might be fired without an actual data change.
            if (this.modelValue !== data) {
              // The compatibility with the v-model and general Vue.js concept of input–like components.
              this.$emit('update:modelValue', data, evt, editor);
            }
          });
        });
      }
    }
  };
</script>
