import {
  mountUnitConfigEditorFromAttributes,
  type UnitConfigEditorFieldController,
} from '@features/unit-editor/mount-unit-config-editor';
import { resetMountMarker, readForwardedHostAttributes } from './element-helpers';

/**
 * host が持つ、実際にレンダリングされる UnitConfigEditor のルート要素へ転送する独自属性。
 * host は display: contents（register.ts 参照）で自身のボックスを持たないため、host に標準の
 * class を当てても見た目には反映されない。テンプレート側は最初から `root-class` を独自属性として
 * 書く（詳細は README.md「共通の設計」参照）。
 */
const FORWARDED_HOST_ATTRIBUTES = ['root-class'] as const;

/**
 * `<acms-unit-config-editor>` カスタム要素。
 *
 * コンフィグ画面（新規エントリー作成 / ユニット追加）のユニット構成設定 UI
 * （UnitConfigEditor）をマウントする。Light DOM に直接 React をマウントし、自身は追加の DOM を
 * 持たない。マウント処理そのものは features/unit-editor/mount-unit-config-editor.tsx の
 * mountUnitConfigEditorFromAttributes を通じて行う。
 *
 * 外部 API: 無し（フォーム送信用の hidden input は UnitConfigEditor 自身が描画するため、
 * `<acms-unit-editor>` の `editor`/`change` のような公開プロパティ・イベントは持たない）。
 *
 * 属性は `admin/config/unit.html`（および deprecated/unit.html）が出力する data-id /
 * label / default-value（UnitConfigListのJSON） / settings（UnitConfigEditorSettingsのJSON）を
 * connectedCallback 時に一度だけ読む。label / default-value / settings は独自属性（data-* では
 * ない。カスタム要素自身の属性はその要素の公開 API であり HTML 標準要素向けの data-* 制約は
 * 適用されないため）。`data-id` のみ HTML 標準のグローバル属性 `id` と衝突するため data-* のまま
 * 維持している。旧 `#js-unit-config-editor` マーク経路は `mode:loop` により同一ページに同一IDの
 * 要素が複数レンダリングされていたが、カスタム要素化によりタグ名ベースになるため id 重複は問題に
 * ならない（各要素の connectedCallback が独立して発火する）。詳細は
 * ablogcms/js/src/elements/README.md を参照。
 */
export class UnitConfigEditorElement extends HTMLElement {
  private controller: UnitConfigEditorFieldController | null = null;

  connectedCallback(): void {
    if (this.controller) {
      return;
    }

    try {
      this.controller = mountUnitConfigEditorFromAttributes(
        this,
        readForwardedHostAttributes(this, FORWARDED_HOST_ATTRIBUTES)
      );
    } catch (error) {
      // 単一要素の初期化失敗が管理画面全体を止めないよう、ここで握りつぶして報告する。

      console.error(error);
    }
  }

  disconnectedCallback(): void {
    const controller = this.controller;
    // Why not: この要素がカスタムユニットの中に埋め込まれる等で物理的に DOM 移動されると、
    // disconnectedCallback → connectedCallback が同期的に再発火する。this.controller を
    // 同期的に null にすると、再発火した connectedCallback のガードが効かず、まだ unmount
    // されていない旧 root が残ったまま同じ host（React はここへ直接レンダリングしている）に
    // 新しい root を二重に作ってしまう。後片付け全体をマイクロタスクへ遅らせ、その時点で
    // this.isConnected を見て「本当に取り外されたのか、移動で戻ってきたのか」を判定する
    // （詳細は elements/block-editor-element.ts の同名コメント参照）。
    queueMicrotask(() => {
      if (this.isConnected) {
        return;
      }
      controller?.unmount();
      this.controller = null;
      resetMountMarker(this);
    });
  }
}
