import type { ProjectRepositoryItem, RoomMemberResponse } from '@/client';
import type { RoomAiConfig } from '@/components/room/MentionPopover';
import { memo, useCallback, useMemo } from 'react';
import { cn } from '@/lib/utils';
import { parse, type Node, type MentionMentionType } from '@/lib/mention-ast';

type MentionType = 'repository' | 'user' | 'ai' | 'notify';

interface LegacyMentionToken {
  full: string;
  type: MentionType;
  name: string;
}

function isMentionNameChar(ch: string): boolean {
  return /[A-Za-z0-9._:\/-]/.test(ch);
}

/**
 * Parses legacy mention formats for backward compatibility with old messages:
 * - `<user>name</user>` (old backend format)
 * - `@type:name`         (old colon format)
 * - `<type>name</type>`  (old XML format)
 */
function extractLegacyMentionTokens(text: string): LegacyMentionToken[] {
  const tokens: LegacyMentionToken[] = [];
  let cursor = 0;

  while (cursor < text.length) {
    const angleOpen = text.indexOf('<', cursor);
    const atOpen = text.indexOf('@', cursor);

    let next: number;
    let style: 'angle' | 'at';
    if (angleOpen >= 0 && (atOpen < 0 || angleOpen <= atOpen)) {
      next = angleOpen;
      style = 'angle';
    } else if (atOpen >= 0) {
      next = atOpen;
      style = 'at';
    } else {
      break;
    }

    const typeStart = next + 1;
    const colon = text.indexOf(':', typeStart);
    const validTypes: MentionType[] = ['repository', 'user', 'ai', 'notify'];

    if (colon >= 0 && colon - typeStart <= 20) {
      const typeRaw = text.slice(typeStart, colon).toLowerCase();
      if (!validTypes.includes(typeRaw as MentionType)) {
        cursor = next + 1;
        continue;
      }

      let end = colon + 1;
      while (end < text.length && isMentionNameChar(text[end])) {
        end++;
      }

      const closeBracket = style === 'angle' && text[end] === '>' ? end + 1 : end;
      const name = text.slice(colon + 1, style === 'angle' ? end : closeBracket);

      if (name) {
        tokens.push({ full: text.slice(next, closeBracket), type: typeRaw as MentionType, name });
        cursor = closeBracket;
        continue;
      }
    }

    if (style === 'angle') {
      let typeEnd = typeStart;
      while (typeEnd < text.length && /[A-Za-z]/.test(text[typeEnd])) {
        typeEnd++;
      }
      const typeCandidate = text.slice(typeStart, typeEnd);

      if (validTypes.includes(typeCandidate as MentionType)) {
        const closeTag = `</${typeCandidate}>`;
        const contentStart = typeEnd;
        const tagClose = text.indexOf(closeTag, cursor);
        if (tagClose >= 0) {
          const name = text.slice(contentStart, tagClose);
          const fullMatch = text.slice(next, tagClose + closeTag.length);
          tokens.push({ full: fullMatch, type: typeCandidate as MentionType, name });
          cursor = tagClose + closeTag.length;
          continue;
        }
      }
    }

    cursor = next + 1;
  }

  return tokens;
}

function extractFirstMentionName(text: string, type: MentionType): string | null {
  const token = extractLegacyMentionTokens(text).find((item) => item.type === type);
  return token?.name ?? null;
}

interface MessageContentWithMentionsProps {
  content: string;
  /** Members list for resolving user mention IDs to display names */
  members?: RoomMemberResponse[];
  /** Repository list for resolving repository mention IDs to display names */
  repos?: ProjectRepositoryItem[];
  /** AI configs for resolving AI mention IDs to display names */
  aiConfigs?: RoomAiConfig[];
}

const mentionStyles: Record<string, string> = {
  user: 'inline-flex items-center rounded bg-blue-100/80 px-1.5 py-0.5 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 font-medium cursor-pointer hover:bg-blue-200 dark:hover:bg-blue-900/60 transition-colors text-sm leading-5',
  repository: 'inline-flex items-center rounded bg-purple-100/80 px-1.5 py-0.5 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300 font-medium cursor-pointer hover:bg-purple-200 dark:hover:bg-purple-900/60 transition-colors text-sm leading-5',
  ai: 'inline-flex items-center rounded bg-green-100/80 px-1.5 py-0.5 text-green-700 dark:bg-green-900/40 dark:text-green-300 font-medium cursor-pointer hover:bg-green-200 dark:hover:bg-green-900/60 transition-colors text-sm leading-5',
  notify: 'inline-flex items-center rounded bg-yellow-100/80 px-1.5 py-0.5 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300 font-medium cursor-pointer hover:bg-yellow-200 dark:hover:bg-yellow-900/60 transition-colors text-sm leading-5',
};

function renderNode(
  node: Node,
  index: number,
  resolveName: (type: string, id: string, label: string) => string,
): React.ReactNode {
  if (node.type === 'text') {
    return <span key={index}>{node.text}</span>;
  }
  if (node.type === 'mention') {
    const displayName = resolveName(node.mentionType, node.id, node.label);
    return (
      <span key={index} className={mentionStyles[node.mentionType] ?? mentionStyles.user}>
        @{displayName}
      </span>
    );
  }
  if (node.type === 'ai_action') {
    return (
      <span
        key={index}
        className="inline-flex items-center rounded bg-green-100/80 px-1.5 py-0.5 text-green-700 dark:bg-green-900/40 dark:text-green-300 font-mono text-xs"
      >
        /{node.action}
        {node.args ? ` ${node.args}` : ''}
      </span>
    );
  }
  return null;
}

/** Renders message content with @mention highlighting using styled spans.
 *  Supports the new `<mention type="..." id="...">label</mention>` format
 *  and legacy formats for backward compatibility with old messages.
 */
export const MessageContentWithMentions = memo(function MessageContentWithMentions({
  content,
  members = [],
  repos = [],
  aiConfigs = [],
}: MessageContentWithMentionsProps) {
  const nodes = useMemo(() => {
    // Try the new AST parser first (handles <mention> and <ai> tags)
    const ast = parse(content);
    if (ast.length > 0) return ast;

    // Fall back to legacy parser for old-format messages
    const legacy = extractLegacyMentionTokens(content);
    if (legacy.length === 0) return [{ type: 'text' as const, text: content }];

    const parts: Node[] = [];
    let cursor = 0;
    for (const token of legacy) {
      const idx = content.indexOf(token.full, cursor);
      if (idx === -1) continue;
      if (idx > cursor) {
        parts.push({ type: 'text', text: content.slice(cursor, idx) });
      }
      parts.push({
        type: 'mention',
        mentionType: token.type as MentionMentionType,
        id: '',
        label: token.name,
      });
      cursor = idx + token.full.length;
    }
    if (cursor < content.length) {
      parts.push({ type: 'text', text: content.slice(cursor) });
    }
    return parts;
  }, [content]);

  // Resolve ID → display name for each mention type
  const resolveName = useCallback(
    (type: string, id: string, label: string): string => {
      if (type === 'user') {
        const member = members.find((m) => m.user === id);
        return member?.user_info?.username ?? member?.user ?? label;
      }
      if (type === 'repository') {
        const repo = repos.find((r) => r.uid === id);
        return repo?.repo_name ?? label;
      }
      if (type === 'ai') {
        const cfg = aiConfigs.find((c) => c.model === id);
        return cfg?.modelName ?? cfg?.model ?? label;
      }
      return label;
    },
    [members, repos, aiConfigs],
  );

  return (
    <div
      className={cn(
        'text-sm text-foreground',
        'max-w-full min-w-0 break-words whitespace-pre-wrap',
        '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs',
        '[&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:overflow-x-auto',
      )}
    >
      {nodes.map((node, i) => renderNode(node, i, resolveName))}
    </div>
  );
});

/** Extract first mentioned user name from text */
export function extractMentionedUserUid(
  text: string,
  participants: Array<{ uid: string; name: string; is_ai: boolean }>,
): string | null {
  const userName = extractFirstMentionName(text, 'user');
  if (!userName) return null;
  const user = participants.find((p) => !p.is_ai && p.name === userName);
  return user ? user.uid : null;
}

/** Extract first mentioned AI name from text */
export function extractMentionedAiUid(
  text: string,
  participants: Array<{ uid: string; name: string; is_ai: boolean }>,
): string | null {
  const aiName = extractFirstMentionName(text, 'ai');
  if (!aiName) return null;
  const ai = participants.find((p) => p.is_ai && p.name === aiName);
  return ai ? ai.uid : null;
}
