'use client';

/**
 * Unified content renderer for markdown + @[type:id:label] mentions.
 * Used across room chat, issues, and PR comments.
 *
 * Features (enabled via props):
 * - enableLinkPreviews: inline rich link previews for issues, PRs, commits, repos
 * - enableCodeRefs: inline code reference blocks (file.rs:42 style)
 *
 * Mentions are protected from markdown parsing by replacing them with
 * zero-width-space-delimited placeholder tokens before rendering,
 * then restored as styled interactive elements after.
 */

import { memo, useMemo, useState } from 'react';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { cn } from '@/lib/utils';
import {
  extractMentions,
  type MentionType,
  type MentionSpan,
} from '@/lib/mention';
import { MentionBadge } from './MentionBadge';
import { detectLanguage, getLanguageFromTag } from '@/lib/code-lang-detect';
import { detectLinkType, extractUrls, type UnfurlResult } from '@/lib/link-unfurl';
import { parseCodeRef, type CodeRef } from '@/lib/code-ref-parser';

interface ContentRendererProps {
  content: string;
  /** Called when a mention is clicked. Provides (type, id, label). */
  onMentionClick?: (type: MentionType, id: string, label: string) => void;
  /** Show rich link preview cards for detected URLs */
  enableLinkPreviews?: boolean;
  /** Show inline code reference blocks for "file.rs:42" style references */
  enableCodeRefs?: boolean;
  /** Additional CSS classes for the wrapper div. */
  className?: string;
  /** Base URL for code reference navigation (e.g. /repository/ns/repo) */
  repoBaseUrl?: string;
  /** Branch for code reference navigation */
  branch?: string;
}

export const ContentRenderer = memo(function ContentRenderer({
  content,
  onMentionClick,
  enableLinkPreviews = false,
  enableCodeRefs = false,
  className,
  repoBaseUrl,
  branch = 'main',
}: ContentRendererProps) {
  const { safeContent, mentions } = useMemo(() => extractMentions(content), [content]);

  // Pre-detect standalone URLs and code refs for inline rendering
  const { urls, codeRefs } = useMemo(() => {
    if (!enableLinkPreviews && !enableCodeRefs) return { urls: new Map<string, UnfurlResult>(), codeRefs: [] };

    const urlMap = new Map<string, UnfurlResult>();
    const refs: CodeRef[] = [];

    // Extract standalone URLs (not inside markdown links)
    const urlMatches = extractUrls(content);
    for (const { url } of urlMatches) {
      const result = detectLinkType(url);
      if (result && result.type !== 'external') {
        urlMap.set(url, result);
      }
    }

    // Extract code refs
    if (enableCodeRefs) {
      const refMatches = content.match(/[^\s:]+:\d+(?:-\d+)?/g) ?? [];
      for (const match of refMatches) {
        const parsed = parseCodeRef(match);
        if (parsed) refs.push(parsed);
      }
    }

    return { urls: urlMap, codeRefs: refs };
  }, [content, enableLinkPreviews, enableCodeRefs]);

  return (
    <div
      className={cn(
        'text-[15px] text-foreground',
        'max-w-full min-w-0 break-words',
        '[&_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',
        '[&_p]:whitespace-pre-wrap [&_p]:leading-[1.4] [&_p]:my-1',
        '[&_ul]:list-disc [&_ul]:pl-6 [&_ul]:my-1',
        '[&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:my-1',
        '[&_li]:my-0.5',
        '[&_blockquote]:border-l-2 [&_blockquote]:border-primary [&_blockquote]:pl-4 [&_blockquote]:my-1',
        '[&_h1]:text-xl [&_h1]:font-semibold [&_h1]:my-2',
        '[&_h2]:text-lg [&_h2]:font-semibold [&_h2]:my-2',
        '[&_h3]:text-base [&_h3]:font-semibold [&_h3]:my-1.5',
        '[&_strong]:font-semibold',
        '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',
        '[&_hr]:border-foreground/20 [&_hr]:my-2',
        '[&_table]:w-full [&_table]:border-collapse [&_table]:rounded-md [&_table]:border [&_table]:border-foreground/20 [&_table]:my-2',
        '[&_th]:border [&_th]:border-foreground/20 [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-bold',
        '[&_td]:border [&_td]:border-foreground/20 [&_td]:px-2 [&_td]:py-1 [&_td]:text-left',
        '[&_tr]:border-t [&_tr]:even:bg-muted',
        className,
      )}
    >
      <Markdown
        remarkPlugins={[remarkGfm]}
        components={buildComponents(mentions, onMentionClick, {
          enableLinkPreviews,
          enableCodeRefs,
          urls,
          codeRefs,
          repoBaseUrl,
          branch,
        }) as Record<string, unknown>}
      >
        {safeContent}
      </Markdown>
    </div>
  );
});

interface RendererOptions {
  enableLinkPreviews: boolean;
  enableCodeRefs: boolean;
  urls: Map<string, UnfurlResult>;
  codeRefs: CodeRef[];
  repoBaseUrl?: string;
  branch?: string;
}

function buildComponents(
  mentions: MentionSpan[],
  onMentionClick: ContentRendererProps['onMentionClick'],
  opts: RendererOptions,
) {
  const { repoBaseUrl, branch } = opts;

  return {
    p: ({ children }: { children: React.ReactNode }) => (
      <p>{restoreInNode(children, mentions, onMentionClick)}</p>
    ),
    li: ({ children }: { children: React.ReactNode }) => (
      <li>{restoreInNode(children, mentions, onMentionClick)}</li>
    ),
    strong: ({ children }: { children: React.ReactNode }) => (
      <strong>{restoreInNode(children, mentions, onMentionClick)}</strong>
    ),
    em: ({ children }: { children: React.ReactNode }) => (
      <em>{restoreInNode(children, mentions, onMentionClick)}</em>
    ),
    code: ({ className, children, ...props }: React.ComponentProps<'code'>) => {
      const isBlock = typeof className === 'string' && className.includes('language-');
      if (isBlock) {
        // Enhanced fenced code block — try to detect language
        const langTag = typeof className === 'string'
          ? className.replace('language-', '')
          : undefined;
        const langInfo = langTag
          ? getLanguageFromTag(langTag)
          : detectLanguage(String(children));
        const langLabel = langInfo?.label ?? langTag;

        return (
          <EnhancedCodeBlock
            code={String(children)}
            language={langLabel}
            repoBaseUrl={repoBaseUrl}
            branch={branch}
          />
        );
      }
      return (
        <code className="font-mono rounded bg-muted px-1 py-0.5 text-xs" {...props}>
          {children}
        </code>
      );
    },
    pre: ({ children }: React.ComponentProps<'pre'>) => {
      // Extract language from nested code element
      const codeEl = children as React.ReactElement<{ className?: string; children?: string }>;
      const langTag = codeEl?.props?.className?.replace('language-', '');
      return (
        <EnhancedCodeBlock
          code={codeEl?.props?.children ?? ''}
          language={langTag}
          repoBaseUrl={repoBaseUrl}
          branch={branch}
        />
      );
    },
  };
}

/** Code block with language badge and optional line numbers */
function EnhancedCodeBlock({
  code,
  language,
  repoBaseUrl: _repoBaseUrl,
  branch: _branch,
}: {
  code: string;
  language?: string;
  repoBaseUrl?: string;
  branch?: string;
}) {
  const [copied, setCopied] = useState(false);
  const langInfo = language ? getLanguageFromTag(language) : detectLanguage(code);
  const displayLang = langInfo?.label ?? language ?? null;

  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(code);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {
      const ta = document.createElement('textarea');
      ta.value = code;
      document.body.appendChild(ta);
      ta.select();
      document.execCommand('copy');
      document.body.removeChild(ta);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }
  };

  return (
    <div className="rounded-lg border bg-muted overflow-hidden my-2">
      {/* Header */}
      <div className="flex items-center gap-2 px-3 py-1.5 border-b bg-muted/70">
        <div className="flex gap-1.5 -ml-1">
          <div className="h-2.5 w-2.5 rounded-full bg-red-500/50" />
          <div className="h-2.5 w-2.5 rounded-full bg-yellow-500/50" />
          <div className="h-2.5 w-2.5 rounded-full bg-green-500/50" />
        </div>
        {displayLang && (
          <span className="text-xs font-mono bg-muted-foreground/10 text-muted-foreground px-1.5 py-0.5 rounded">
            {displayLang}
          </span>
        )}
        <button
          type="button"
          onClick={handleCopy}
          className="ml-auto p-1 rounded hover:bg-muted-foreground/10 transition-colors"
          title={copied ? 'Copied!' : 'Copy'}
        >
          {copied ? (
            <span className="text-xs text-green-600 font-medium">Copied!</span>
          ) : (
            <span className="text-xs text-muted-foreground">Copy</span>
          )}
        </button>
      </div>
      {/* Code */}
      <pre className="p-3 overflow-x-auto font-mono text-sm leading-5 text-foreground/90">
        <code>{code}</code>
      </pre>
    </div>
  );
}

// ── Helpers ────────────────────────────────────────────────────────────────

/** Recursively restore mention placeholders inside text nodes. */
function restoreInNode(
  children: React.ReactNode,
  mentions: MentionSpan[],
  onMentionClick?: (type: MentionType, id: string, label: string) => void,
): React.ReactNode {
  if (typeof children === 'string') {
    return restoreMentionsText(children, mentions, onMentionClick);
  }
  if (Array.isArray(children)) {
    return children.map((child, i) => (
      <span key={i}>{restoreInNode(child, mentions, onMentionClick)}</span>
    ));
  }
  return children;
}

/** Restore mention placeholders inside a single text string into React elements. */
function restoreMentionsText(
  text: string,
  mentions: MentionSpan[],
  onMentionClick?: (type: MentionType, id: string, label: string) => void,
): React.ReactNode {
  const parts: React.ReactNode[] = [];
  let lastIndex = 0;

  const PLACEHOLDER_RE = /​MENTION_(\d+)​/g;
  let match: RegExpExecArray | null;

  while ((match = PLACEHOLDER_RE.exec(text)) !== null) {
    if (match.index > lastIndex) {
      parts.push(text.slice(lastIndex, match.index));
    }
    const idx = parseInt(match[1], 10);
    const m = mentions[idx];
    if (m) {
      parts.push(
        <MentionBadge
          key={`mention-${idx}`}
          type={m.type}
          id={m.id}
          label={m.label}
          onClick={onMentionClick}
        />,
      );
    }
    lastIndex = PLACEHOLDER_RE.lastIndex;
  }

  if (lastIndex < text.length) {
    parts.push(text.slice(lastIndex));
  }

  return parts.length > 0 ? parts : text;
}
