import { lazy, Suspense } from 'react';
import type { UnitTree, UnitTreeNode, UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';
import { type Editor } from '@ablogcms/unit-editor/core';
import type { UnitEditorSettings } from '@ablogcms/unit-editor/types';
import { defaultEditorSettings } from '@ablogcms/unit-editor/config';
import { SlotFillProvider } from '@ablogcms/components/slot-fill';
import { getGlobalFillBridge } from '@ablogcms/plugins/core';
import Spinner from '@ablogcms/components/spinner';
import { render } from '@ablogcms/react-utils';
import { getUnitDefinitions } from './unit-definitions';
import BlockEditorSettingsProvider from '../block-editor/components/block-editor-settings-provider';
import BlockEditorExtensionsProvider from '../block-editor/components/block-editor-extensions-provider';

const UnitEditor = lazy(
  () => import(/* webpackChunkName: "unit-editor" */ '@ablogcms/unit-editor/components/unit-editor')
);

function getDefaultSummaryRange(): number | null {
  const input = document.querySelector<HTMLInputElement>('[name="summary_range"]');
  if (input == null) {
    return null;
  }
  if (input.value === '') {
    return null;
  }
  return parseInt(input.value, 10);
}

function applySummaryRange(value: string) {
  const input = document.querySelector<HTMLInputElement>('[name="summary_range"]');
  if (input) {
    input.value = value;
  }
}

function setDefaultHtml(tree: UnitTree): UnitTree {
  const template = document.querySelector<HTMLTemplateElement>('#unit-html-repository');
  if (template) {
    const clone = template.content.cloneNode(true) as HTMLElement;
    const nodes = clone.querySelectorAll(`[data-unit-id]`);
    if (nodes.length === 0) {
      return tree;
    }
    nodes.forEach((node) => {
      const html = node.outerHTML;
      const findUnit = (units: UnitTree, id: UnitTreeNode['id']): UnitTreeNode | undefined => {
        for (const unit of units) {
          if (unit.id === id) {
            return unit;
          }
          if (unit.children.length > 0) {
            const found = findUnit(unit.children, id);
            if (found) {
              return found;
            }
          }
        }
        return undefined;
      };
      const id = node.getAttribute('data-unit-id')!;
      const unit = findUnit(tree, id);
      if (unit) {
        unit.defaultHtml = html;
      }
    });
  }
  return tree;
}

/**
 * `<acms-unit-editor>` の default-value 属性（JSON、data-* ではない独自属性）から初期の
 * ユニットツリーを組み立てる。`#unit-html-repository`（テンプレート要素）にサーバーレンダリング
 * 済みのカスタムユニット HTML（`[data-unit-id]` マーク。こちらはテンプレート内の別要素の属性で
 * 対象外）があれば defaultHtml として差し込む。dispatch-unit-editor.tsx（旧マーク経路、削除済み）
 * から移設。mount-unit-inplace-editor.tsx が引き続きこの関数を利用する。
 */
export function getDefaultValue(element: HTMLElement): UnitTree {
  const json = element.getAttribute('default-value') ?? '[]';
  const tree: UnitTree = JSON.parse(json);

  const data = setDefaultHtml(tree);

  return data;
}

/**
 * `<acms-unit-editor>` の settings 属性（JSON、data-* ではない独自属性）から UnitEditorSettings を
 * 組み立てる。mount-unit-inplace-editor.tsx が引き続きこの関数を利用する。
 */
export function getSettings(element: HTMLElement): UnitEditorSettings {
  const json = element.getAttribute('settings') ?? '{}';
  const settings: Partial<UnitEditorSettings> = JSON.parse(json);
  return { ...defaultEditorSettings, ...settings };
}

export interface UnitEditorFieldController {
  unmount: () => void;
}

export interface UnitEditorMountCallbacks {
  /** unitDefs のレジストリ解決を差し替えたいテスト向け。省略時は getUnitDefinitions() を都度呼ぶ。 */
  unitDefs?: Record<string, UnitDefInterface>;
  /** editor が生成されたときに一度だけ呼ばれる（`<acms-unit-editor>` の `el.editor` 用）。 */
  onEditorReady?: (editor: Editor) => void;
  /** ユニットツリーが変わる（transaction が起きる）たびに呼ばれる（`change` イベント発火用）。 */
  onChange?: (data: UnitTree) => void;
  /** host（`<acms-unit-editor>`）の class 属性から転送される。UnitEditor 自身のルート要素へ合成する。 */
  className?: string;
}

/**
 * target に UnitEditor をマウントする。`dispatch-unit-editor.tsx`（旧 `#js-unit-editor` マーク経路、
 * 削除済み）にあった処理を移設したもので、`<acms-unit-editor>`（elements/unit-editor-element.ts）から
 * 呼ばれる。
 */
export function mountUnitEditorField(
  target: HTMLElement,
  defaultValue: UnitTree,
  settings: UnitEditorSettings,
  callbacks: UnitEditorMountCallbacks = {}
): UnitEditorFieldController {
  const { unitDefs = getUnitDefinitions(), onEditorReady, onChange, className } = callbacks;

  const handleContentChange = (data: UnitTree) => {
    const index = data.findIndex((unit) => unit.type === 'more');
    applySummaryRange(index === -1 ? '' : index.toString());
    onChange?.(data);
  };

  const handleCreate = (editor: Editor) => {
    onEditorReady?.(editor);

    const summaryRange = getDefaultSummaryRange();
    if (summaryRange !== null) {
      const unit = editor.createUnit('more');
      editor.commands.insertUnit(unit, { index: summaryRange });
    }
  };

  const root = render(
    <SlotFillProvider store={getGlobalFillBridge()}>
      <BlockEditorExtensionsProvider>
        {(editorExtensions) => (
          <BlockEditorSettingsProvider blockDefinitions={editorExtensions?.blockDefinitions}>
            <Suspense
              fallback={
                <div className="acms-admin-d-flex acms-admin-justify-content-center acms-admin-align-items-center">
                  <Spinner size={20} />
                </div>
              }
            >
              <UnitEditor
                defaultValue={defaultValue}
                settings={settings}
                unitDefs={unitDefs}
                onCreate={handleCreate}
                onContentChange={handleContentChange}
                className={className}
              />
            </Suspense>
          </BlockEditorSettingsProvider>
        )}
      </BlockEditorExtensionsProvider>
    </SlotFillProvider>,
    target
  );

  return {
    unmount() {
      root.unmount();
    },
  };
}

/**
 * data-default-value / data-settings 属性から UnitEditor をマウントする。element 自身がマウント先
 * コンテナになる（`<acms-unit-editor>` は Light DOM で自身に直接 React をマウントする）。
 */
export function mountUnitEditorFromAttributes(
  element: HTMLElement,
  callbacks: UnitEditorMountCallbacks = {}
): UnitEditorFieldController {
  const defaultValue = getDefaultValue(element);
  const settings = getSettings(element);
  return mountUnitEditorField(element, defaultValue, settings, callbacks);
}
