import React, { useCallback, useEffect, useRef } from 'react';
import { Node, mergeAttributes } from '@tiptap/core';
import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Placeholder from '@tiptap/extension-placeholder';
import Link from '@tiptap/extension-link';
import {
  Bold,
  Heading2,
  Italic,
  Link as LinkIcon,
  List,
  PanelsTopLeft,
  Quote,
} from 'lucide-react';
import type { NoteEmbedInput } from '../../../types/research';

interface ResearchEditorProps {
  content: string;
  embeds?: NoteEmbedInput[];
  onChange: (html: string, text: string, embeds: NoteEmbedInput[]) => void;
  onPreviewLink?: (url: string) => Promise<NoteEmbedInput>;
  placeholder?: string;
  minRows?: number;
  compact?: boolean;
  className?: string;
}

const urlPattern = /^https?:\/\/\S+$/i;

const escapeHtml = (value: string) =>
  value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');

export const embedToHtml = (embed: NoteEmbedInput) => {
  const attributes = [
    'data-note-embed-card="true"',
    `data-url="${escapeHtml(embed.url)}"`,
    `url="${escapeHtml(embed.url)}"`,
    `title="${escapeHtml(embed.title)}"`,
    `kind="${escapeHtml(embed.kind)}"`,
    embed.canonicalUrl ? `canonicalUrl="${escapeHtml(embed.canonicalUrl)}"` : '',
    embed.publisher ? `publisher="${escapeHtml(embed.publisher)}"` : '',
    embed.excerpt ? `excerpt="${escapeHtml(embed.excerpt)}"` : '',
    embed.imageUrl ? `imageUrl="${escapeHtml(embed.imageUrl)}"` : '',
  ]
    .filter(Boolean)
    .join(' ');

  return `<div ${attributes}></div>`;
};

const extractEmbeds = (html: string, fallback: NoteEmbedInput[]): NoteEmbedInput[] => {
  if (typeof document === 'undefined') return fallback;

  const container = document.createElement('div');
  container.innerHTML = html;
  const fallbackByUrl = new Map(fallback.map((embed) => [embed.url, embed]));

  return Array.from(container.querySelectorAll('[data-note-embed-card]')).reduce<NoteEmbedInput[]>(
    (result, element) => {
      const url = element.getAttribute('url') ?? element.getAttribute('data-url') ?? '';
      if (!url) return result;

      const fallbackEmbed = fallbackByUrl.get(url);
      result.push({
        url,
        canonicalUrl: element.getAttribute('canonicalUrl') ?? fallbackEmbed?.canonicalUrl,
        title: element.getAttribute('title') ?? fallbackEmbed?.title ?? url,
        publisher: element.getAttribute('publisher') ?? fallbackEmbed?.publisher,
        excerpt: element.getAttribute('excerpt') ?? fallbackEmbed?.excerpt,
        kind: (element.getAttribute('kind') ?? fallbackEmbed?.kind ?? 'article') as NoteEmbedInput['kind'],
        imageUrl: element.getAttribute('imageUrl') ?? fallbackEmbed?.imageUrl,
      });
      return result;
    },
    [],
  );
};

const EmbeddedLinkCard = Node.create({
  name: 'embeddedLinkCard',
  group: 'block',
  atom: true,

  addAttributes() {
    return {
      url: { default: null },
      title: { default: null },
      publisher: { default: null },
      excerpt: { default: null },
      kind: { default: 'article' },
      canonicalUrl: { default: null },
      imageUrl: { default: null },
    };
  },

  parseHTML() {
    return [{ tag: 'div[data-note-embed-card]' }];
  },

  renderHTML({ HTMLAttributes }) {
    const title = HTMLAttributes.title || HTMLAttributes.url || 'Embedded link';
    const publisher = HTMLAttributes.publisher || 'Link';
    const excerpt = HTMLAttributes.excerpt;
    const imageUrl = HTMLAttributes.imageUrl;

    const children: (string | object)[] = [
      ['div', { class: 'text-xs uppercase tracking-[0.16em] text-term-text-tertiary' }, publisher],
      ['div', { class: 'mt-1 text-sm font-semibold text-term-text' }, title],
      excerpt
        ? ['div', { class: 'mt-1 text-xs leading-5 text-term-text-muted' }, excerpt]
        : ['div', { class: 'hidden' }, ''],
        [
          'a',
          {
            href: HTMLAttributes.url,
            target: '_blank',
            rel: 'noopener noreferrer',
            class: 'mt-2 block truncate text-xs text-info underline decoration-info/40 underline-offset-2',
          },
          HTMLAttributes.url,
        ],
    ];

    if (imageUrl) {
      return [
        'div',
        mergeAttributes(HTMLAttributes, {
          'data-note-embed-card': 'true',
          contenteditable: 'false',
          class:
            'note-embed-card my-3 flex overflow-hidden rounded-lg border border-term-border bg-term-surface text-left',
        }),
        [
          'img',
          {
            src: imageUrl,
            alt: title,
            class: 'h-auto w-28 flex-shrink-0 object-cover',
          },
        ],
        ['div', { class: 'flex min-w-0 flex-col p-3' }, ...children],
      ];
    }

    return [
      'div',
      mergeAttributes(HTMLAttributes, {
        'data-note-embed-card': 'true',
        contenteditable: 'false',
        class:
          'note-embed-card my-3 rounded-lg border border-term-border bg-term-surface p-3 text-left',
      }),
      ...children,
    ];
  },
});

export const ResearchEditor: React.FC<ResearchEditorProps> = ({
  content,
  embeds = [],
  onChange,
  onPreviewLink,
  placeholder = 'Write your research note...',
  minRows = 6,
  compact = false,
  className = '',
}) => {
  const isSettingContent = useRef(false);
  const lastExternalContent = useRef(content);
  const embedsRef = useRef(embeds);
  embedsRef.current = embeds;

  const editor = useEditor({
    extensions: [
      StarterKit.configure({
        heading: { levels: [2, 3] },
        codeBlock: false,
        horizontalRule: false,
        strike: false,
      }),
      Placeholder.configure({
        placeholder,
      }),
      Link.configure({
        openOnClick: false,
        autolink: true,
        HTMLAttributes: { class: 'text-info underline decoration-info/40 underline-offset-2', target: '_blank', rel: 'noopener noreferrer' },
      }),
      EmbeddedLinkCard,
    ],
    content,
    editorProps: {
      attributes: {
        class: `research-editor-content outline-none text-sm leading-7 text-term-text ${compact ? 'min-h-0' : ''}`,
      },
      handlePaste: (view, event) => {
        const text = event.clipboardData?.getData('text/plain').trim();
        if (!text || !urlPattern.test(text) || !onPreviewLink) {
          return false;
        }

        event.preventDefault();
        void onPreviewLink(text).then((embed) => {
          const { state, dispatch } = view;
          const node = state.schema.nodes.embeddedLinkCard.create({
            url: embed.url,
            canonicalUrl: embed.canonicalUrl,
            title: embed.title,
            publisher: embed.publisher,
            excerpt: embed.excerpt,
            kind: embed.kind,
            imageUrl: embed.imageUrl,
          });
          dispatch(state.tr.replaceSelectionWith(node).scrollIntoView());
        });
        return true;
      },
    },
    onUpdate: ({ editor: ed }) => {
      if (isSettingContent.current) return;
      const html = ed.getHTML();
      const text = ed.getText();
      const nextEmbeds = extractEmbeds(html, embedsRef.current);
      lastExternalContent.current = html;
      onChange(html, text, nextEmbeds);
    },
  });

  useEffect(() => {
    if (!editor || editor.isDestroyed) return;
    if (content === lastExternalContent.current) return;
    isSettingContent.current = true;
    const { from, to } = editor.state.selection;
    editor.commands.setContent(content);
    lastExternalContent.current = content;
    try {
      editor.commands.setTextSelection({
        from: Math.min(from, editor.state.doc.content.size),
        to: Math.min(to, editor.state.doc.content.size),
      });
    } catch {
      // Selection restoration is best effort after external content replacement.
    }
    isSettingContent.current = false;
  }, [content, editor]);

  const setLink = useCallback(() => {
    if (!editor) return;
    const previous = editor.getAttributes('link').href as string | undefined;
    const url = window.prompt('URL', previous ?? 'https://');
    if (url === null) return;
    if (url === '') {
      editor.chain().focus().extendMarkRange('link').unsetLink().run();
      return;
    }
    editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();
  }, [editor]);

  const embedLink = useCallback(() => {
    if (!editor || !onPreviewLink) return;
    const url = window.prompt('URL', 'https://');
    if (!url) return;

    void onPreviewLink(url).then((embed) => {
      editor.chain().focus().insertContent(embedToHtml(embed)).run();
    });
  }, [editor, onPreviewLink]);

  const minHeight = compact ? 'auto' : `${Math.max(minRows * 28, 168)}px`;

  if (!editor) return null;

  return (
    <div className={`research-editor border border-term-border bg-term-bg transition-colors focus-within:border-info ${className}`}>
      {!compact && (
        <div className="flex items-center gap-1 border-b border-term-border px-2 py-1.5">
          <ToolbarButton
            onClick={() => editor.chain().focus().toggleBold().run()}
            active={editor.isActive('bold')}
            title="Bold"
          >
            <Bold className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor.chain().focus().toggleItalic().run()}
            active={editor.isActive('italic')}
            title="Italic"
          >
            <Italic className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
            active={editor.isActive('heading', { level: 2 })}
            title="Heading"
          >
            <Heading2 className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor.chain().focus().toggleBulletList().run()}
            active={editor.isActive('bulletList')}
            title="Bullet list"
          >
            <List className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor.chain().focus().toggleBlockquote().run()}
            active={editor.isActive('blockquote')}
            title="Quote"
          >
            <Quote className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={setLink}
            active={editor.isActive('link')}
            title="Link"
          >
            <LinkIcon className="h-3.5 w-3.5" />
          </ToolbarButton>
          <ToolbarButton
            onClick={embedLink}
            active={false}
            title="Embed link"
            disabled={!onPreviewLink}
          >
            <PanelsTopLeft className="h-3.5 w-3.5" />
          </ToolbarButton>
        </div>
      )}

      <div style={{ minHeight }} className="px-4 py-3">
        <EditorContent editor={editor} />
      </div>
    </div>
  );
};

const ToolbarButton: React.FC<{
  children: React.ReactNode;
  onClick: () => void;
  active: boolean;
  title: string;
  disabled?: boolean;
}> = ({ children, onClick, active, title, disabled = false }) => (
  <button
    type="button"
    onClick={onClick}
    title={title}
    disabled={disabled}
    className={`p-1.5 transition-colors ${
      active
        ? 'bg-info/20 text-info'
        : 'text-term-text-tertiary hover:bg-term-surface hover:text-term-text disabled:cursor-not-allowed disabled:opacity-40'
    }`}
  >
    {children}
  </button>
);
