import { lazy, Suspense } from 'react';
import { UnitConfigEditorSettings } from '@ablogcms/unit-editor/components/config-editor/types';
import { defaultConfigEditorSettings } from '@ablogcms/unit-editor/config';
import { UnitConfigList } from '@ablogcms/unit-editor/core/types';
import type { UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';
import { SlotFillProvider } from '@ablogcms/components/slot-fill';
import { getGlobalFillBridge } from '@ablogcms/plugins/core';
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 UnitConfigEditor = lazy(
  () => import(/* webpackChunkName: "unit-config-editor" */ '@ablogcms/unit-editor/components/config-editor')
);

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

export interface UnitConfigEditorMountCallbacks {
  /** unitDefs のレジストリ解決を差し替えたいテスト向け。省略時は getUnitDefinitions() を都度呼ぶ。 */
  unitDefs?: Record<string, UnitDefInterface>;
  /** host（`<acms-unit-config-editor>`）の class 属性から転送される。UnitConfigEditor のルート要素へ合成する。 */
  className?: string;
}

/**
 * `<acms-unit-config-editor>` の settings 属性（JSON、data-* ではない独自属性）から
 * UnitConfigEditorSettings を組み立てる。`dispatch-unit-config-editor.tsx`（旧
 * `#js-unit-config-editor` マーク経路、削除済み）から移設。
 */
export function getSettings(element: HTMLElement): UnitConfigEditorSettings {
  const json = element.getAttribute('settings') ?? '{}';
  const settings: Partial<UnitConfigEditorSettings> = JSON.parse(json);
  return { ...defaultConfigEditorSettings, ...settings };
}

/**
 * `<acms-unit-config-editor>` の default-value 属性（JSON、data-* ではない独自属性）から
 * UnitConfigList を組み立てる。
 */
export function getDefaultValue(element: HTMLElement): UnitConfigList {
  const json = element.getAttribute('default-value') ?? '[]';
  const defaultValue: UnitConfigList = JSON.parse(json);
  return defaultValue;
}

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

  const root = render(
    <SlotFillProvider store={getGlobalFillBridge()}>
      <BlockEditorExtensionsProvider>
        {(editorExtensions) => (
          <BlockEditorSettingsProvider blockDefinitions={editorExtensions?.blockDefinitions}>
            <Suspense fallback={null}>
              <UnitConfigEditor
                id={id}
                label={label}
                defaultValue={defaultValue}
                settings={settings}
                unitDefs={unitDefs}
                className={className}
              />
            </Suspense>
          </BlockEditorSettingsProvider>
        )}
      </BlockEditorExtensionsProvider>
    </SlotFillProvider>,
    target
  );

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

/**
 * `<acms-unit-config-editor>` の data-id / label / default-value / settings 属性から
 * UnitConfigEditor をマウントする。element 自身がマウント先コンテナになる。`data-id` のみ
 * HTML 標準のグローバル属性 `id` と衝突するため data-* のまま維持している。
 */
export function mountUnitConfigEditorFromAttributes(
  element: HTMLElement,
  callbacks: UnitConfigEditorMountCallbacks = {}
): UnitConfigEditorFieldController {
  const id = element.getAttribute('data-id');
  if (!id) {
    throw new Error('data-id is required');
  }
  const label = element.getAttribute('label') ?? '';
  const settings = getSettings(element);
  const defaultValue = getDefaultValue(element);
  return mountUnitConfigEditorField(element, id, label, defaultValue, settings, callbacks);
}
