type PermissionKey<M extends string, C extends string> = `${M}:${C}`;

type PermissionsRecord<M extends string, T extends readonly string[]> = {
  readonly [K in T[number]]: PermissionKey<M, K>;
};

interface PermissionResultBase<M extends string, T extends readonly string[]> {
  permissions: PermissionsRecord<M, T>;
  own: readonly string[];
  all: readonly string[];
}

interface PermissionResultWithAliases<
  M extends string,
  T extends readonly string[],
  A extends Record<string, readonly T[number][]>,
> extends PermissionResultBase<M, T> {
  aliases: { readonly [K in keyof A]: readonly PermissionKey<M, T[number]>[] };
}

interface DefinePermissionsOptions<T extends readonly string[]> {
  deps?: readonly string[];
  aliases?: Record<string, readonly T[number][]>;
}

// Overload: no deps, no aliases
export function definePermissions<const M extends string, const T extends readonly string[]>(
  moduleName: M,
  commands: T,
): PermissionResultBase<M, T>;

// Overload: deps array shorthand
export function definePermissions<const M extends string, const T extends readonly string[]>(
  moduleName: M,
  commands: T,
  deps: readonly string[],
): PermissionResultBase<M, T>;

// Overload: options object with aliases
export function definePermissions<
  const M extends string,
  const T extends readonly string[],
  const A extends Record<string, readonly T[number][]>,
>(
  moduleName: M,
  commands: T,
  options: { deps?: readonly string[]; aliases: A },
): PermissionResultWithAliases<M, T, A>;

// Overload: options object without aliases
export function definePermissions<const M extends string, const T extends readonly string[]>(
  moduleName: M,
  commands: T,
  options: { deps?: readonly string[] },
): PermissionResultBase<M, T>;

// Implementation
export function definePermissions(
  moduleName: string,
  commands: readonly string[],
  depsOrOptions?: readonly string[] | DefinePermissionsOptions<readonly string[]>,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
): any {
  const isArray = Array.isArray(depsOrOptions);
  const deps: readonly string[] = isArray
    ? depsOrOptions
    : ((depsOrOptions as DefinePermissionsOptions<readonly string[]> | undefined)?.deps ?? []);
  const aliasDefs = !isArray
    ? (depsOrOptions as DefinePermissionsOptions<readonly string[]> | undefined)?.aliases
    : undefined;

  const permissions: Record<string, string> = {};
  const own: string[] = [];

  for (const cmd of commands) {
    const key = `${moduleName}:${cmd}`;
    permissions[cmd] = key;
    own.push(key);
  }

  const all = [...new Set([...own, ...deps])];

  const result: Record<string, unknown> = { permissions, own, all };

  if (aliasDefs) {
    const aliases: Record<string, string[]> = {};
    for (const [alias, cmds] of Object.entries(aliasDefs)) {
      aliases[alias] = cmds.map((cmd) => `${moduleName}:${cmd}`);
    }
    result.aliases = aliases;
  }

  return result;
}
