import type { ControlDef, SectionDef, WidgetManifest } from './types';

/**
 * Reusable control groups (`widgets/_common/<group>.json`, e.g. the loop query controls). Mirrors
 * `Zadex\Loop\ControlGroups::expand()` in PHP: a manifest lists `"controlGroups": ["query"]` or
 * `[{ "group": "query", "defaults": { "query_post_type": "product" }, "exclude": ["query_source"] }]`; the group's
 * sections go before the first style-tab section, its controls are appended, ids the manifest defines win.
 * Idempotent (manifests WordPress already expanded pass through unchanged).
 */
interface GroupFile {
  sections?: SectionDef[];
  controls?: ControlDef[];
}
export type ControlGroupRef = string | { group: string; defaults?: Record<string, unknown>; exclude?: string[] };

const files = import.meta.glob<{ default: GroupFile }>('/widgets/_common/*.json', { eager: true });
const groups = new Map<string, GroupFile>();
for (const [path, mod] of Object.entries(files)) {
  const name = path.split('/').pop()!.replace(/\.json$/, '');
  if (name !== 'controls') groups.set(name, mod.default);
}

export function controlGroup(name: string): GroupFile | undefined {
  return groups.get(name);
}

export function expandControlGroups<M extends Pick<WidgetManifest, 'sections' | 'controls'> & { controlGroups?: ControlGroupRef[] }>(m: M): M {
  const refs = Array.isArray(m.controlGroups) ? m.controlGroups : [];
  if (!refs.length) return m;
  const sections = [...(m.sections ?? [])];
  const controls = [...(m.controls ?? [])];
  const sectionIds = new Set(sections.map((s) => s.id));
  const controlIds = new Set(controls.map((c) => c.id));
  for (const ref of refs) {
    const spec = typeof ref === 'string' ? { group: ref } : ref;
    const group = spec && groups.get(spec.group);
    if (!group) continue;
    const defaults = spec.defaults ?? {};
    const exclude = new Set(spec.exclude ?? []);
    const add = (group.sections ?? []).filter((s) => !sectionIds.has(s.id));
    add.forEach((s) => sectionIds.add(s.id));
    if (add.length) {
      let at = sections.findIndex((s) => s.tab === 'style');
      if (at === -1) at = sections.length;
      sections.splice(at, 0, ...add);
    }
    for (const c of group.controls ?? []) {
      if (controlIds.has(c.id) || exclude.has(c.id)) continue;
      controls.push(Object.prototype.hasOwnProperty.call(defaults, c.id) ? { ...c, default: defaults[c.id] } : c);
      controlIds.add(c.id);
    }
  }
  return { ...m, sections, controls };
}
