export type AnnouncementPoliteness = 'polite' | 'assertive';

export interface AnnouncementPayload {
  message: string;
  politeness?: AnnouncementPoliteness;
  dedupeKey?: string;
  source?: 'app' | 'toast';
}

type AnnouncementHandler = (payload: Required<AnnouncementPayload>) => void;

const DEDUPE_WINDOW_MS = 1600;

let handler: AnnouncementHandler | null = null;
let recent = new Map<string, { at: number; source: 'app' | 'toast' }>();

function normalizeKey(payload: Required<AnnouncementPayload>): string {
  return `${payload.politeness}:${payload.dedupeKey || payload.message.trim()}`;
}

function prune(ts: number): void {
  recent = new Map(
    [...recent.entries()].filter(([, value]) => ts - value.at < DEDUPE_WINDOW_MS),
  );
}

export function mountAnnouncementBridge(nextHandler: AnnouncementHandler | null): void {
  handler = nextHandler;
}

export function announce(payload: AnnouncementPayload | string): boolean {
  const normalized: Required<AnnouncementPayload> =
    typeof payload === 'string'
      ? { message: payload, politeness: 'polite', dedupeKey: '', source: 'app' }
      : {
          message: payload.message,
          politeness: payload.politeness ?? 'polite',
          dedupeKey: payload.dedupeKey ?? '',
          source: payload.source ?? 'app',
        };

  const message = normalized.message.trim();
  if (!message || !handler) return false;

  const ts = Date.now();
  prune(ts);

  const key = normalizeKey({ ...normalized, message });
  const previous = recent.get(key);
  if (previous && (previous.source === 'toast' || normalized.source === 'toast')) {
    recent.set(key, { at: ts, source: normalized.source });
    return false;
  }
  if (previous && ts - previous.at < DEDUPE_WINDOW_MS) {
    recent.set(key, { at: ts, source: normalized.source });
    return false;
  }

  recent.set(key, { at: ts, source: normalized.source });
  handler({ ...normalized, message });
  return true;
}
