import type { BlockMenuItem, BlockTypeMaster, CommandItem } from '../types';

/**
 * PHP SettingsJson から届く、ブロックメニュー 1 件分のコンフィグ値。
 * グループ階層を持たず、entry の配列として送られる。
 */
export interface RawBlockMenuEntry {
  type: string;
  label: string;
  class: string;
  group: string;
}

export type RawBlockMenu = RawBlockMenuEntry[];

/** 最初の entry が group を指定しなかった場合のフォールバックグループ名。 */
const DEFAULT_GROUP_TITLE = 'グループ';

/**
 * type と class から識別子を生成する。
 * - class なし: `type`
 * - class あり: `type.class`
 * allow / deny の判定や、同 type の class 違いを区別する用途で使う。
 */
export const buildIdentifier = (type: string, className: string | undefined): string =>
  className ? `${type}.${className}` : type;

interface PositionedCommand {
  group: string;
  command: CommandItem;
}

/**
 * API レスポンスの blockMenus を TS マスターと合体させて `BlockMenuItem[]` に展開する。
 *
 * - 入力は entry の配列。マスターに該当 type が無ければ除外。
 * - label が空文字なら master.label を採用、class が空文字なら undefined とする
 * - 空 group は直前の entry の group を引き継ぐ（=admin の挿入順の慣習を維持）
 * - identifier (`type` または `type.class`) を CommandItem に焼き込む
 * - 同 identifier が複数あれば後勝ち（最後の entry の位置・内容で上書き）
 * - isDisabled などのランタイム関数は useBlockMenus.resolveCommands が後段で差し込むため、ここではスタブ
 */
export default function createBlockMenu(raw: RawBlockMenu, masters: Record<string, BlockTypeMaster>): BlockMenuItem[] {
  const byIdentifier = new Map<string, PositionedCommand>();
  let currentGroup = DEFAULT_GROUP_TITLE;

  for (const entry of raw) {
    const master = masters[entry.type];
    if (!master) {
      continue;
    }

    if (entry.group !== '' && entry.group !== currentGroup) {
      currentGroup = entry.group;
    }

    const className = entry.class || undefined;
    const identifier = buildIdentifier(entry.type, className);
    const command: CommandItem = {
      name: entry.type,
      identifier,
      label: entry.label || master.label,
      description: master.description,
      iconName: master.icon,
      aliases: master.aliases,
      isTextMenu: master.isTextMenu,
      class: className,
      isDisabled: () => false,
    };

    // 後勝ち: Map に identifier をキーにして set し直すと、挿入順は最後の位置に揃う
    byIdentifier.delete(identifier);
    byIdentifier.set(identifier, { group: currentGroup, command });
  }

  const commandsByGroup = new Map<string, CommandItem[]>();
  const groupOrder: string[] = [];

  for (const { group, command } of byIdentifier.values()) {
    if (!commandsByGroup.has(group)) {
      commandsByGroup.set(group, []);
      groupOrder.push(group);
    }
    commandsByGroup.get(group)!.push(command);
  }

  return groupOrder.map((title, index) => ({
    name: `group-${index + 1}`,
    title,
    commands: commandsByGroup.get(title) ?? [],
  }));
}
