import type { AppserviceConfig } from './types.js';

export interface RegistrationOptions {
  /** Unique appservice id in Synapse. Default: "mosaic-as". */
  id?: string;
  /** URL where Synapse reaches the appservice, e.g. http://mosaic-as:8008 */
  url: string;
  /** Alias namespace regex prefix. Default: "#mosaic-". */
  aliasPrefix?: string;
}

const escapeRegex = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

/**
 * Build the Synapse appservice registration document (mosaic-as.yaml).
 * Deployment (infrastructure repo) serializes this to YAML and mounts it via
 * app_service_config_files.
 */
export function buildRegistration(cfg: AppserviceConfig, options: RegistrationOptions) {
  const prefix = cfg.userPrefix ?? 'agent-';
  return {
    id: options.id ?? 'mosaic-as',
    url: options.url,
    as_token: cfg.asToken,
    hs_token: cfg.hsToken,
    sender_localpart: cfg.senderLocalpart ?? 'mosaic-as',
    rate_limited: false,
    namespaces: {
      users: [
        {
          regex: `@${escapeRegex(prefix)}.*:${escapeRegex(cfg.domain)}`,
          exclusive: true,
        },
      ],
      aliases: [
        {
          regex: `${escapeRegex(options.aliasPrefix ?? '#mosaic-')}.*:${escapeRegex(cfg.domain)}`,
          exclusive: false,
        },
      ],
      rooms: [],
    },
  };
}

const assertYamlSafe = (field: string, value: string): string => {
  // Tokens/urls/ids are single-line opaque strings; control characters would
  // let a crafted value terminate the scalar and inject YAML keys.
  if (/[\r\n\x00-\x08\x0b-\x1f]/.test(value)) {
    throw new Error(`registration field ${field} contains control characters`);
  }
  return value.replace(/'/g, "''");
};

/** Minimal YAML serialization for the flat registration document. */
export function registrationToYaml(registration: ReturnType<typeof buildRegistration>): string {
  const ns = registration.namespaces;
  const nsBlock = (entries: Array<{ regex: string; exclusive: boolean }>): string =>
    entries.length === 0
      ? ' []'
      : '\n' +
        entries.map((e) => `    - regex: '${e.regex}'\n      exclusive: ${e.exclusive}`).join('\n');
  return [
    `id: '${assertYamlSafe('id', registration.id)}'`,
    `url: '${assertYamlSafe('url', registration.url)}'`,
    `as_token: '${assertYamlSafe('as_token', registration.as_token)}'`,
    `hs_token: '${assertYamlSafe('hs_token', registration.hs_token)}'`,
    `sender_localpart: '${assertYamlSafe('sender_localpart', registration.sender_localpart)}'`,
    `rate_limited: ${registration.rate_limited}`,
    'namespaces:',
    `  users:${nsBlock(ns.users)}`,
    `  aliases:${nsBlock(ns.aliases)}`,
    `  rooms:${nsBlock(ns.rooms)}`,
    '',
  ].join('\n');
}
