export interface WeixinInboundMessageLike {
  from_user_id?: unknown;
  to_user_id?: unknown;
  room_id?: unknown;
  chat_room_id?: unknown;
  item_list?: unknown;
  at_user_list?: unknown;
  mention_list?: unknown;
  [key: string]: unknown;
}

export interface ChatScope {
  kind: 'dm' | 'group';
  chatId: string;
  senderId: string;
}

export function decideChatScope(
  message: WeixinInboundMessageLike,
  botAccountId: string,
): ChatScope | null {
  const senderId = String(message.from_user_id || '').trim();
  if (!senderId) return null;
  if (senderId === botAccountId) return null;

  const roomId = String(message.room_id || message.chat_room_id || '').trim();
  if (roomId) {
    return { kind: 'group', chatId: roomId, senderId };
  }
  return { kind: 'dm', chatId: senderId, senderId };
}

export function buildDmSessionId(channelId: number, senderId: string): string {
  return `channel:${channelId}:weixin:${senderId}`;
}

export function buildGroupSessionId(channelId: number, roomId: string): string {
  return `channel:${channelId}:weixin:group:${roomId}`;
}

export function buildDmDispatchKey(channelId: number, senderId: string): string {
  return `channel:${channelId}:weixin:${senderId}`;
}

export function buildGroupDispatchKey(channelId: number, roomId: string): string {
  return `channel:${channelId}:weixin:group:${roomId}`;
}

export function buildDmClarifyKey(channelId: number, senderId: string): string {
  return `channel:${channelId}:weixin:${senderId}`;
}

export function buildGroupClarifyKey(channelId: number, roomId: string, senderId: string): string {
  return `channel:${channelId}:weixin:group:${roomId}:${senderId}`;
}

function escapeRegExp(value: string): string {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function collectMentionIds(message: WeixinInboundMessageLike): string[] {
  const ids: string[] = [];
  const pushFrom = (value: unknown) => {
    if (!Array.isArray(value)) return;
    for (const entry of value) {
      if (typeof entry === 'string' && entry.trim()) {
        ids.push(entry.trim());
        continue;
      }
      if (entry && typeof entry === 'object') {
        const record = entry as Record<string, unknown>;
        const id = record.user_id ?? record.userId ?? record.id ?? record.openid;
        if (typeof id === 'string' && id.trim()) ids.push(id.trim());
      }
    }
  };
  pushFrom(message.at_user_list);
  pushFrom(message.mention_list);
  const items = Array.isArray(message.item_list) ? message.item_list : [];
  for (const item of items) {
    if (!item || typeof item !== 'object') continue;
    pushFrom((item as Record<string, unknown>).at_info);
    pushFrom((item as Record<string, unknown>).at_user_list);
    pushFrom((item as Record<string, unknown>).mention_list);
  }
  return ids;
}

export function detectAtMention(
  message: WeixinInboundMessageLike,
  text: string,
  aliases: string[],
  accountIds: string[],
): boolean {
  const mentionedIds = collectMentionIds(message);
  if (mentionedIds.length > 0) {
    for (const account of accountIds) {
      if (account && mentionedIds.includes(account)) return true;
    }
  }

  const nonEmptyAliases = aliases.filter(a => typeof a === 'string' && a.length > 0);
  if (nonEmptyAliases.length === 0) return false;
  if (!text) return false;

  const joined = nonEmptyAliases.map(escapeRegExp).join('|');
  // P2-6: @ must be preceded by start-of-string or whitespace (block email@alias);
  // alias must be followed by end / whitespace / punctuation (block @alias后接字母汉字)
  const regex = new RegExp(
    `(?<=^|[\\s\\u2005\\u3000])@(?:${joined})(?=$|[\\s\\u2005\\u3000\\p{P}])`,
    'u',
  );
  return regex.test(text);
}

export function stripLeadingMention(text: string, aliases: string[]): string {
  const nonEmptyAliases = aliases.filter(a => typeof a === 'string' && a.length > 0);
  if (nonEmptyAliases.length === 0) return text;
  const joined = nonEmptyAliases.map(escapeRegExp).join('|');
  const leading = new RegExp(`^@(?:${joined})[\\s\\u2005\\u3000]*`, 'u');
  return text.replace(leading, '');
}

export function stripLeadingPrefix(text: string, prefix: string): string {
  if (!prefix) return text;
  if (!text.startsWith(prefix)) return text;
  return text.slice(prefix.length).replace(/^[\s 　]*/u, '');
}

export interface GroupEntityLike {
  id: number;
  channelId: number;
  roomId: string;
  status: number;
  triggerMode: string;
  triggerPrefix: string | null;
}

export interface GroupAcceptanceResult {
  accept: boolean;
  cleanedText?: string;
  reason?: string;
}

export function decideGroupAcceptance(
  group: GroupEntityLike,
  message: WeixinInboundMessageLike,
  text: string,
  aliases: string[],
  accountIds: string[],
): GroupAcceptanceResult {
  if (group.status !== 1) {
    return { accept: false, reason: 'group_disabled' };
  }

  switch (group.triggerMode) {
    case 'all':
      return { accept: true, cleanedText: text };
    case 'prefix': {
      const prefix = (group.triggerPrefix || '').trim();
      if (!prefix) return { accept: false, reason: 'prefix_not_configured' };
      if (!text.startsWith(prefix)) return { accept: false, reason: 'prefix_mismatch' };
      return { accept: true, cleanedText: stripLeadingPrefix(text, prefix) };
    }
    case 'at_mention':
    default: {
      if (!detectAtMention(message, text, aliases, accountIds)) {
        return { accept: false, reason: 'not_mentioned' };
      }
      return { accept: true, cleanedText: stripLeadingMention(text, aliases) };
    }
  }
}
