import { emitAsukaEvent } from './events';

export type AsukaListenerName = string;

export interface AsukaListenerEntry {
  name: AsukaListenerName;
  enabled: boolean;
  /** Optional: human description for UIs / debugging. */
  description?: string;
  /** Optional: whether this listener was explicitly set by user options. */
  userDefined?: boolean;
}

const listeners = new Map<AsukaListenerName, AsukaListenerEntry>();

/**
 * Declare a listener (id + default enabled state). Calling multiple times is safe.
 * F1: This is only a registry; actual wiring comes in F2.
 */
export function defineListener(
  name: AsukaListenerName,
  defaultEnabled: boolean = true,
  description?: string,
): AsukaListenerEntry {
  const existing = listeners.get(name);
  if (existing) {
    // Keep existing enabled state; allow description update if provided.
    if (description && !existing.description) existing.description = description;
    return existing;
  }
  const entry: AsukaListenerEntry = { name, enabled: defaultEnabled, description };
  listeners.set(name, entry);
  return entry;
}

export function isListenerEnabled(name: AsukaListenerName): boolean {
  return listeners.get(name)?.enabled ?? false;
}

export function setListenerEnabled(
  name: AsukaListenerName,
  enabled: boolean,
  opts?: { userDefined?: boolean },
): AsukaListenerEntry {
  const entry = defineListener(name, false);
  if (entry.enabled === enabled && (opts?.userDefined ? entry.userDefined === true : true)) {
    return entry;
  }
  entry.enabled = enabled;
  if (typeof opts?.userDefined === 'boolean') entry.userDefined = opts.userDefined;

  emitAsukaEvent('listeners.changed', { name, enabled });
  return entry;
}

export function toggleListener(name: AsukaListenerName): AsukaListenerEntry {
  const entry = defineListener(name, false);
  return setListenerEnabled(name, !entry.enabled);
}

export function getListenersSnapshot(): Record<string, AsukaListenerEntry> {
  const out: Record<string, AsukaListenerEntry> = {};
  listeners.forEach((v, k) => {
    out[k] = { ...v };
  });
  return out;
}

export function applyListenersFromOptions(options: unknown): void {
  if (!options || typeof options !== 'object') return;
  const obj = options as Record<string, unknown>;
  Object.keys(obj).forEach((name) => {
    const val = obj[name];
    if (typeof val === 'boolean') {
      setListenerEnabled(name, val, { userDefined: true });
    }
  });
}

/**
 * Convenience: define the canonical listeners we plan to wire in F2.
 * This keeps the registry stable even if some are still no-ops.
 */
export function defineDefaultListeners(): void {
  // Only keep listeners that have (or will have) real runtime implementations.
  defineListener('constraints', true, 'Applies movement constraints / drawing zones.');
  defineListener('preventBoundingBoxExit', true, 'Reverts objects only when fully outside the drawing zone; partial overflow remains allowed.');
  defineListener('preventBoundingBoxOvertaking', false, 'Clamps objects so they never partially exit the drawing zone. Opt-in only.');
  defineListener('snapObjects', true, 'Snaps objects to guides/edges.');
  defineListener('snapCenter', true, 'Enables center snapping (object center + canvas/zone centers).');
  defineListener('snapRotation', true, 'Snaps rotation to fixed steps.');
  defineListener('clickElement', true, 'Enables element overlay on click.');
  defineListener('adjustBoundingBoxColors', true, 'Updates stroke colors based on drawing zone overlap.');
  defineListener('boundingBoxesBorders', true, 'Shows bounding box borders.');
}
