import {
  CodeOutlined,
  DownloadOutlined,
  EyeOutlined,
  FileMarkdownOutlined,
  FilePdfOutlined,
  FileTextOutlined,
  LinkOutlined,
  PictureOutlined,
  VideoCameraOutlined,
} from '@ant-design/icons';
import { Alert, Button, Empty, Space, Spin, Typography, message } from 'antd';
import { InlineImage } from '../../../components/common/InlineImage';
import { MarkdownPreviewContent } from '../../../components/markdownPreview/MarkdownPreviewContent';
import { CodexDiffBlock, ZoomablePreviewSurface } from '../../../components/previewer';
import { inferCodeLanguage, renderEditorBlock } from '../../../components/previewer/renderers';
import { ChatDataTablePreview, resolveTabularPreviewModel } from './ChatDataTablePreview';
import { triggerResourceDownload } from './downloadUtils';
import type { PreviewKind } from './previewKind';
import '../../../components/previewer/PreviewerUI.css';

const { Paragraph, Text } = Typography;

export type ChatPreviewKind = PreviewKind;

export type ChatPreviewTarget = {
  label: string;
  url: string;
  kind: ChatPreviewKind;
};

export function resolveChatPreviewGlyph(kind: ChatPreviewKind) {
  switch (kind) {
    case 'image':
      return <PictureOutlined />;
    case 'video':
      return <VideoCameraOutlined />;
    case 'markdown':
      return <FileMarkdownOutlined />;
    case 'code':
    case 'diff':
      return <CodeOutlined />;
    case 'document':
      return <FileTextOutlined />;
    case 'pdf':
      return <FilePdfOutlined />;
    case 'file':
      return <DownloadOutlined />;
    default:
      return <LinkOutlined />;
  }
}

export function resolveChatPreviewKindLabel(kind: ChatPreviewKind) {
  switch (kind) {
    case 'image':
      return 'image preview';
    case 'video':
      return 'video preview';
    case 'markdown':
      return 'markdown preview';
    case 'code':
      return 'code preview';
    case 'diff':
      return 'diff preview';
    case 'document':
      return 'document preview';
    case 'pdf':
      return 'pdf preview';
    case 'file':
      return 'file download';
    default:
      return 'resource preview';
  }
}

function resolvePreviewErrorMessage(previewError: string) {
  const normalized = previewError.trim();

  if (!normalized) {
    return '';
  }

  if (/^\s*403\b/.test(normalized) || normalized.includes('권한으로 열 수 없습니다')) {
    return '권한이 없거나 허용되지 않은 경로입니다. 세션 리소스 경로와 접근 권한을 확인해 주세요.';
  }

  if (/^\s*404\b/.test(normalized) || normalized.includes('찾을 수 없습니다')) {
    return '파일이 이동되었거나 아직 세션 리소스 경로에 생성되지 않았습니다. 경로를 다시 확인해 주세요.';
  }

  if (/^\s*401\b/.test(normalized) || normalized.includes('인증이 필요합니다')) {
    return '인증 정보가 없어서 문서를 열 수 없습니다.';
  }

  return normalized;
}

function resolvePreviewExtension(target: ChatPreviewTarget) {
  const raw = target.url || target.label;
  const normalized = raw.toLowerCase().split('?')[0] ?? '';
  const match = normalized.match(/\.([a-z0-9]+)$/i);
  return match?.[1] ?? '';
}

function resolveCodeLanguage(target: ChatPreviewTarget, previewText: string) {
  const extension = resolvePreviewExtension(target);

  if (extension === 'tsx' || extension === 'ts') {
    return 'typescript';
  }

  if (extension === 'jsx' || extension === 'js' || extension === 'mjs' || extension === 'cjs') {
    return 'javascript';
  }

  if (extension === 'json') {
    return 'json';
  }

  if (extension === 'css') {
    return 'css';
  }

  if (extension === 'scss') {
    return 'scss';
  }

  if (extension === 'html' || extension === 'htm') {
    return 'html';
  }

  if (extension === 'md' || extension === 'markdown') {
    return 'markdown';
  }

  if (extension === 'java') {
    return 'java';
  }

  if (extension === 'kt') {
    return 'kotlin';
  }

  if (extension === 'py') {
    return 'python';
  }

  if (extension === 'go') {
    return 'go';
  }

  if (extension === 'rs') {
    return 'rust';
  }

  if (extension === 'sql') {
    return 'sql';
  }

  if (extension === 'sh' || extension === 'bash' || extension === 'zsh') {
    return 'bash';
  }

  if (extension === 'yml' || extension === 'yaml') {
    return 'yaml';
  }

  if (extension === 'xml') {
    return 'xml';
  }

  if (extension === 'diff' || extension === 'patch') {
    return 'diff';
  }

  if (/^(diff --git|@@\s|--- a\/|\+\+\+ b\/)/m.test(previewText)) {
    return 'diff';
  }

  return inferCodeLanguage(extension || 'text');
}

function isAppRouteUrl(url: string) {
  if (typeof window === 'undefined') {
    return false;
  }

  try {
    const parsed = new URL(url, window.location.href);
    const pathname = parsed.pathname.toLowerCase();
    const hasKnownFileExtension = /\.[a-z0-9]+$/i.test(pathname);
    return parsed.origin === window.location.origin && !hasKnownFileExtension;
  } catch {
    return false;
  }
}

function canRenderFramePreview(url: string) {
  if (typeof window === 'undefined') {
    return false;
  }

  try {
    const parsed = new URL(url, window.location.href);
    return parsed.origin === window.location.origin;
  } catch {
    return false;
  }
}

function buildHtmlFrameDocument(html: string, sourceUrl: string) {
  const trimmed = html.trim();

  if (!trimmed) {
    return '<!doctype html><html><body></body></html>';
  }

  const baseHref = (() => {
    try {
      return new URL('.', sourceUrl).toString();
    } catch {
      return sourceUrl;
    }
  })();
  const baseTag = `<base href="${baseHref}">`;

  if (/<head(\s|>)/i.test(trimmed)) {
    return trimmed.replace(/<head(\s*[^>]*)>/i, (match) => `${match}${baseTag}`);
  }

  if (/<html(\s|>)/i.test(trimmed)) {
    return trimmed.replace(/<html(\s*[^>]*)>/i, (match) => `${match}<head>${baseTag}</head>`);
  }

  return `<!doctype html><html><head>${baseTag}</head><body>${trimmed}</body></html>`;
}

type ChatPreviewBodyProps = {
  target: ChatPreviewTarget | null;
  previewText: string;
  isPreviewLoading: boolean;
  previewError: string;
  previewContentType?: string;
  maxMarkdownBlocks?: number;
  renderHtmlAsFrame?: boolean;
  fullscreen?: boolean;
};

function isHtmlFallbackPreview(target: ChatPreviewTarget, previewText: string, previewContentType: string | undefined) {
  const extension = resolvePreviewExtension(target);
  const normalizedContentType = previewContentType?.toLowerCase() ?? '';
  const normalizedPreview = previewText.trimStart().toLowerCase();

  if (extension === 'html' || extension === 'htm' || target.kind === 'markdown') {
    return false;
  }

  const looksLikeHtml =
    normalizedContentType.includes('text/html') ||
    normalizedPreview.startsWith('<!doctype html') ||
    normalizedPreview.startsWith('<html') ||
    normalizedPreview.includes('<head') ||
    normalizedPreview.includes('<body');

  if (!looksLikeHtml) {
    return false;
  }

  return normalizedPreview.includes('<div id="root"');
}

export function ChatPreviewBody({
  target,
  previewText,
  isPreviewLoading,
  previewError,
  previewContentType,
  maxMarkdownBlocks,
  renderHtmlAsFrame = false,
  fullscreen = false,
}: ChatPreviewBodyProps) {
  if (!target) {
    return <Empty description="preview 가능한 링크가 아직 없습니다." />;
  }

  if (isPreviewLoading) {
    return (
      <div className="app-chat-panel__preview-loading">
        <Spin size="small" />
        <Text type="secondary">preview를 불러오는 중입니다.</Text>
      </div>
    );
  }

  if (previewError) {
    return (
      <Alert
        showIcon
        type="warning"
        message="preview를 불러오지 못했습니다."
        description={resolvePreviewErrorMessage(previewError)}
      />
    );
  }

  if (isHtmlFallbackPreview(target, previewText, previewContentType)) {
    return (
      <Alert
        showIcon
        type="info"
        message="실제 소스 파일 대신 앱 HTML 화면이 반환되었습니다."
        description={`${target.label} 경로가 raw 파일이 아니라 현재 앱의 fallback HTML을 돌려주고 있습니다. 정적 파일 경로 또는 실제 다운로드 경로를 사용해야 코드 preview가 정확하게 표시됩니다.`}
      />
    );
  }

  if (target.kind === 'image') {
    const imageNode = (
      <InlineImage
        src={target.url}
        alt={target.label}
        className="app-chat-panel__preview-image"
        fallbackText="이미지 preview를 불러오지 못했습니다."
      />
    );

    if (fullscreen) {
      return (
        <ZoomablePreviewSurface stageClassName="app-chat-panel__preview-zoom-stage" contentClassName="app-chat-panel__preview-zoom-content">
          {imageNode}
        </ZoomablePreviewSurface>
      );
    }

    return (
      imageNode
    );
  }

  if (target.kind === 'video') {
    return <video src={target.url} className="app-chat-panel__preview-video" controls playsInline />;
  }

  if (target.kind === 'pdf') {
    return <iframe title={target.label} src={target.url} className="app-chat-panel__preview-frame" />;
  }

  const handleDownloadResource = () => {
    const fileName = target.url.split('/').pop()?.trim() || target.label;
    void triggerResourceDownload(target.url, fileName).catch((error: unknown) => {
      message.error(error instanceof Error ? error.message : '다운로드에 실패했습니다.');
    });
  };

  if (target.kind === 'file') {
    return (
      <div className="app-chat-panel__preview-file">
        <Paragraph>
          브라우저에서 바로 미리보기하지 않는 파일입니다. 아래 버튼으로 새 탭에서 열거나 다운로드할 수 있습니다.
        </Paragraph>
        <Space wrap>
          <Button type="text" href={target.url} target="_blank" rel="noreferrer" aria-label="새 탭 열기" icon={<EyeOutlined />} />
          <Button type="text" aria-label="다운로드" icon={<DownloadOutlined />} onClick={handleDownloadResource} />
        </Space>
      </div>
    );
  }

  if (target.kind === 'markdown') {
    return (
      <div className="app-chat-panel__preview-rich app-chat-panel__preview-rich--markdown">
        <MarkdownPreviewContent
          content={previewText || '# Preview\n\n표시할 preview 본문이 없습니다.'}
          maxBlocks={maxMarkdownBlocks}
        />
      </div>
    );
  }

  if (target.kind === 'code' || target.kind === 'diff' || target.kind === 'document') {
    const tabularModel = resolveTabularPreviewModel(target, previewText);

    if (tabularModel) {
      return <ChatDataTablePreview model={tabularModel} />;
    }

    const resolvedLanguage = resolveCodeLanguage(target, previewText);

    if (renderHtmlAsFrame && resolvedLanguage === 'html' && canRenderFramePreview(target.url)) {
      const frameNode = (
        <iframe
          title={target.label}
          srcDoc={buildHtmlFrameDocument(previewText, target.url)}
          className="app-chat-panel__preview-frame"
        />
      );

      if (fullscreen) {
        return (
          <ZoomablePreviewSurface stageClassName="app-chat-panel__preview-zoom-stage" contentClassName="app-chat-panel__preview-zoom-content">
            {frameNode}
          </ZoomablePreviewSurface>
        );
      }

      return (
        frameNode
      );
    }

    if (target.kind === 'diff' || resolvedLanguage === 'diff') {
      return (
        <div className="app-chat-panel__preview-rich app-chat-panel__preview-rich--code">
          <CodexDiffBlock
            diffText={previewText || ''}
            summary={`${target.label} 기준 raw diff preview입니다.`}
            showToolbar={false}
            className="app-chat-panel__preview-diff"
          />
        </div>
      );
    }

    return (
      <div className="app-chat-panel__preview-rich app-chat-panel__preview-rich--code">
        {renderEditorBlock(previewText || '표시할 preview 본문이 없습니다.', resolvedLanguage, 'code')}
      </div>
    );
  }

  if (isAppRouteUrl(target.url)) {
    return (
      <Alert
        showIcon
        type="info"
        message="앱 화면 경로는 preview iframe으로 열지 않습니다."
        description="현재 화면 문맥은 이미 WebSocket으로 서버에 전달됩니다. 이 경로를 다시 열면 앱만 새로 렌더링되어 preview처럼 보이지 않을 수 있습니다."
      />
    );
  }

  if (canRenderFramePreview(target.url)) {
    return <iframe title={target.label} src={target.url} className="app-chat-panel__preview-frame" />;
  }

  return (
    <div className="app-chat-panel__preview-file">
      <Paragraph>
        브라우저에서 직접 렌더링하지 않는 형식입니다. 아래 버튼으로 새 탭에서 열거나 바로 다운로드할 수 있습니다.
      </Paragraph>
      <Space wrap>
        <Button type="text" href={target.url} target="_blank" rel="noreferrer" aria-label="새 탭 열기" icon={<EyeOutlined />} />
        <Button type="text" aria-label="다운로드" icon={<DownloadOutlined />} onClick={handleDownloadResource} />
      </Space>
    </div>
  );
}
