import React, { useState } from 'react'
import { BaseBoxShapeUtil, TLBaseShape, createShapeId, IndexKey, TLParentId, HTMLContainer } from '@tldraw/tldraw'
import type { JSX } from 'react'
import { StandardizedToolWrapper } from '../components/StandardizedToolWrapper'
import { usePinnedToView } from '../hooks/usePinnedToView'
import { useMaximize } from '../hooks/useMaximize'

export type IFathomNoteShape = TLBaseShape<
  'FathomNote',
  {
    w: number
    h: number
    title: string
    content: string
    tags: string[]
    noteId: string
    pinnedToView: boolean
    primaryColor: string // Blue shade for the header
  }
>

export class FathomNoteShape extends BaseBoxShapeUtil<IFathomNoteShape> {
  static override type = 'FathomNote' as const

  // Default blue color (can be overridden per shape)
  static readonly PRIMARY_COLOR = "#3b82f6"

  getDefaultProps(): IFathomNoteShape['props'] {
    return {
      w: 500,
      h: 600,
      title: 'Fathom Note',
      content: '',
      tags: [],
      noteId: '',
      pinnedToView: false,
      primaryColor: FathomNoteShape.PRIMARY_COLOR,
    }
  }

  component(shape: IFathomNoteShape) {
    const isSelected = this.editor.getSelectedShapeIds().includes(shape.id)
    const [isMinimized, setIsMinimized] = useState(false)
    const [isCopied, setIsCopied] = useState(false)

    // Use the pinning hook
    usePinnedToView(this.editor, shape.id, shape.props.pinnedToView)

    // Use the maximize hook for fullscreen functionality
    const { isMaximized, toggleMaximize } = useMaximize({
      editor: this.editor,
      shapeId: shape.id,
      currentW: shape.props.w,
      currentH: shape.props.h,
      shapeType: 'FathomNote',
    })

    const handleClose = () => {
      this.editor.deleteShape(shape.id)
    }

    const handleMinimize = () => {
      setIsMinimized(!isMinimized)
    }

    const handlePinToggle = () => {
      this.editor.updateShape<IFathomNoteShape>({
        id: shape.id,
        type: 'FathomNote',
        props: {
          ...shape.props,
          pinnedToView: !shape.props.pinnedToView,
        },
      })
    }

    const handleCopy = async () => {
      try {
        // Extract plain text from content (remove HTML tags and markdown formatting)
        let textToCopy = shape.props.content || ''
        
        // Remove HTML tags if present
        const tempDiv = document.createElement('div')
        tempDiv.innerHTML = textToCopy
        textToCopy = tempDiv.textContent || tempDiv.innerText || textToCopy
        
        // Clean up markdown formatting for better plain text output
        // Remove markdown headers
        textToCopy = textToCopy.replace(/^#+\s+/gm, '')
        // Remove markdown bold/italic
        textToCopy = textToCopy.replace(/\*\*([^*]+)\*\*/g, '$1')
        textToCopy = textToCopy.replace(/\*([^*]+)\*/g, '$1')
        // Remove markdown links but keep text
        textToCopy = textToCopy.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
        // Remove markdown code blocks
        textToCopy = textToCopy.replace(/```[\s\S]*?```/g, '')
        // Remove inline code
        textToCopy = textToCopy.replace(/`([^`]+)`/g, '$1')
        
        // Clean up extra whitespace
        textToCopy = textToCopy.trim().replace(/\n{3,}/g, '\n\n')
        
        if (!textToCopy.trim()) {
          console.warn('No content to copy')
          return
        }
        
        await navigator.clipboard.writeText(textToCopy)
        setIsCopied(true)
        setTimeout(() => {
          setIsCopied(false)
        }, 2000)
      } catch (error) {
        console.error('Failed to copy text:', error)
      }
    }

    const contentStyle: React.CSSProperties = {
      padding: '16px',
      overflow: 'auto',
      flex: 1,
      backgroundColor: '#ffffff',
      color: '#000000',
      fontSize: '13px',
      lineHeight: '1.6',
      fontFamily: 'Inter, sans-serif',
      userSelect: 'text', // Enable text selection
      cursor: 'text', // Show text cursor
      WebkitUserSelect: 'text', // Safari support
      MozUserSelect: 'text', // Firefox support
      msUserSelect: 'text', // IE/Edge support
    }

    // Format markdown content for display
    const formatContent = (content: string) => {
      if (!content) return null
      
      // Check if content starts with HTML (for the header with date)
      if (content.trim().startsWith('<div')) {
        // Find where the HTML div ends
        const divEndIndex = content.indexOf('</div>')
        if (divEndIndex !== -1) {
          const htmlHeader = content.substring(0, divEndIndex + 6) // Include </div>
          const markdownContent = content.substring(divEndIndex + 6).trim()
          
          return (
            <>
              <div dangerouslySetInnerHTML={{ __html: htmlHeader }} />
              {markdownContent ? formatMarkdownContent(markdownContent) : null}
            </>
          )
        }
      }
      
      return formatMarkdownContent(content)
    }
    
    // Format markdown content (extracted to separate function)
    const formatMarkdownContent = (content: string) => {
      const lines = content.split('\n')
      const elements: JSX.Element[] = []
      let i = 0
      let inCodeBlock = false
      let codeBlockLines: string[] = []
      let listItems: string[] = []
      let listType: 'ul' | 'ol' | null = null
      
      const processInlineMarkdown = (text: string): (string | JSX.Element)[] => {
        const parts: (string | JSX.Element)[] = []
        let lastIndex = 0
        let keyCounter = 0
        
        // Process inline code, links, bold, italic in order of precedence
        // We need to process them in a way that handles overlapping patterns correctly
        // Process bold first, then italic (to avoid conflicts)
        const patterns: Array<{
          regex: RegExp
          render: (...args: any[]) => JSX.Element
          groupCount: number
        }> = [
          { 
            regex: /`([^`]+)`/g,
            groupCount: 1,
            render: (code: string, key: number) => (
              <code key={key} style={{ 
                backgroundColor: '#f4f4f4', 
                padding: '2px 4px', 
                borderRadius: '3px',
                fontFamily: 'monospace',
                fontSize: '12px'
              }}>{code}</code>
            )
          },
          { 
            regex: /\[([^\]]+)\]\(([^)]+)\)/g,
            groupCount: 2,
            render: (linkText: string, url: string, key: number) => (
              <a key={key} href={url} target="_blank" rel="noopener noreferrer" style={{ color: '#2563eb', textDecoration: 'underline' }}>{linkText}</a>
            )
          },
          { 
            regex: /\*\*([^*]+)\*\*/g,
            groupCount: 1,
            render: (boldText: string, key: number) => (
              <strong key={key} style={{ fontWeight: 'bold' }}>{boldText}</strong>
            )
          },
        ]
        
        // Process italic separately after bold to avoid conflicts
        // Match single asterisks that aren't part of double asterisks
        // Use a simpler approach: match *text* where text doesn't contain *
        const italicPattern = /\*([^*\n]+?)\*/g
        
        // Find all matches and sort by position
        const matches: Array<{ index: number; length: number; render: () => JSX.Element }> = []
        
        patterns.forEach(({ regex, render, groupCount }) => {
          regex.lastIndex = 0
          let match
          while ((match = regex.exec(text)) !== null) {
            const matchKey = keyCounter++
            // Store the match data to avoid closure issues
            const matchIndex = match.index
            const matchLength = match[0].length
            // Extract captured groups immediately and store them
            const matchGroups: string[] = []
            for (let i = 1; i <= groupCount; i++) {
              if (match[i] !== undefined) {
                matchGroups.push(match[i])
              }
            }
            
            matches.push({
              index: matchIndex,
              length: matchLength,
              render: () => {
                // Call render with the stored groups and key
                // Safety check: ensure we have the required groups
                if (matchGroups.length < groupCount) {
                  return <span key={matchKey}>{text.substring(matchIndex, matchIndex + matchLength)}</span>
                }
                if (groupCount === 1) {
                  return render(matchGroups[0], matchKey)
                } else if (groupCount === 2) {
                  return render(matchGroups[0], matchGroups[1], matchKey)
                } else {
                  return render(...matchGroups, matchKey)
                }
              }
            })
          }
        })
        
        // Process italic separately (after bold to avoid conflicts)
        // First, create a set of positions that are already covered by bold
        const boldPositions = new Set<number>()
        matches.forEach(m => {
          for (let pos = m.index; pos < m.index + m.length; pos++) {
            boldPositions.add(pos)
          }
        })
        
        italicPattern.lastIndex = 0
        let italicMatch
        while ((italicMatch = italicPattern.exec(text)) !== null) {
          // Safety check: ensure we have a captured group
          if (!italicMatch[1]) continue
          
          // Check if this italic match overlaps with any bold match
          let overlapsBold = false
          for (let pos = italicMatch.index; pos < italicMatch.index + italicMatch[0].length; pos++) {
            if (boldPositions.has(pos)) {
              overlapsBold = true
              break
            }
          }
          
          if (!overlapsBold) {
            const matchKey = keyCounter++
            // Store the italic text to avoid closure issues
            const italicText = italicMatch[1]
            const italicIndex = italicMatch.index
            const italicLength = italicMatch[0].length
            matches.push({
              index: italicIndex,
              length: italicLength,
              render: () => (
                <em key={matchKey} style={{ fontStyle: 'italic' }}>{italicText}</em>
              )
            })
          }
        }
        
        // Sort matches by position, and remove overlapping matches (keep the first one)
        matches.sort((a, b) => a.index - b.index)
        
        // Remove overlapping matches - if two matches overlap, keep the one that starts first
        const nonOverlapping: typeof matches = []
        for (const match of matches) {
          const overlaps = nonOverlapping.some(existing => {
            const existingEnd = existing.index + existing.length
            const matchEnd = match.index + match.length
            return (match.index < existingEnd && matchEnd > existing.index)
          })
          if (!overlaps) {
            nonOverlapping.push(match)
          }
        }
        
        // Build parts array
        nonOverlapping.forEach((match) => {
          if (match.index > lastIndex) {
            parts.push(text.substring(lastIndex, match.index))
          }
          parts.push(match.render())
          lastIndex = match.index + match.length
        })
        
        if (lastIndex < text.length) {
          parts.push(text.substring(lastIndex))
        }
        
        return parts.length > 0 ? parts : [text]
      }
      
      const flushList = () => {
        if (listItems.length > 0) {
          const ListTag = listType === 'ol' ? 'ol' : 'ul'
          elements.push(
            <ListTag key={`list-${i}`} style={{ margin: '8px 0', paddingLeft: '24px' }}>
              {listItems.map((item, idx) => (
                <li key={idx} style={{ margin: '4px 0' }}>
                  {processInlineMarkdown(item)}
                </li>
              ))}
            </ListTag>
          )
          listItems = []
          listType = null
        }
      }
      
      const flushCodeBlock = () => {
        if (codeBlockLines.length > 0) {
          elements.push(
            <pre key={`codeblock-${i}`} style={{
              backgroundColor: '#f4f4f4',
              padding: '12px',
              borderRadius: '4px',
              overflow: 'auto',
              margin: '12px 0',
              fontSize: '12px',
              fontFamily: 'monospace',
              lineHeight: '1.5'
            }}>
              <code>{codeBlockLines.join('\n')}</code>
            </pre>
          )
          codeBlockLines = []
        }
      }
      
      while (i < lines.length) {
        const line = lines[i]
        const trimmed = line.trim()
        
        // Code blocks
        if (trimmed.startsWith('```')) {
          if (inCodeBlock) {
            flushCodeBlock()
            inCodeBlock = false
          } else {
            flushList()
            inCodeBlock = true
          }
          i++
          continue
        }
        
        if (inCodeBlock) {
          codeBlockLines.push(line)
          i++
          continue
        }
        
        // Headers
        if (trimmed.startsWith('# ')) {
          flushList()
          flushCodeBlock()
          elements.push(
            <h1 key={i} style={{ fontSize: '20px', fontWeight: 'bold', margin: '16px 0 8px 0' }}>
              {processInlineMarkdown(trimmed.substring(2))}
            </h1>
          )
          i++
          continue
        }
        if (trimmed.startsWith('## ')) {
          flushList()
          flushCodeBlock()
          elements.push(
            <h2 key={i} style={{ fontSize: '18px', fontWeight: 'bold', margin: '12px 0 6px 0' }}>
              {processInlineMarkdown(trimmed.substring(3))}
            </h2>
          )
          i++
          continue
        }
        if (trimmed.startsWith('### ')) {
          flushList()
          flushCodeBlock()
          elements.push(
            <h3 key={i} style={{ fontSize: '16px', fontWeight: 'bold', margin: '10px 0 4px 0' }}>
              {processInlineMarkdown(trimmed.substring(4))}
            </h3>
          )
          i++
          continue
        }
        if (trimmed.startsWith('#### ')) {
          flushList()
          flushCodeBlock()
          elements.push(
            <h4 key={i} style={{ fontSize: '15px', fontWeight: 'bold', margin: '10px 0 4px 0' }}>
              {processInlineMarkdown(trimmed.substring(5))}
            </h4>
          )
          i++
          continue
        }
        
        // Horizontal rule
        if (trimmed === '---' || trimmed === '***' || trimmed === '___') {
          flushList()
          flushCodeBlock()
          elements.push(
            <hr key={i} style={{ margin: '16px 0', border: 'none', borderTop: '1px solid #e0e0e0' }} />
          )
          i++
          continue
        }
        
        // Blockquote
        if (trimmed.startsWith('> ')) {
          flushList()
          flushCodeBlock()
          elements.push(
            <blockquote key={i} style={{
              margin: '8px 0',
              paddingLeft: '16px',
              borderLeft: '3px solid #e0e0e0',
              color: '#666',
              fontStyle: 'italic'
            }}>
              {processInlineMarkdown(trimmed.substring(2))}
            </blockquote>
          )
          i++
          continue
        }
        
        // Unordered list
        if (trimmed.match(/^[-*+]\s/)) {
          flushCodeBlock()
          if (listType !== 'ul') {
            flushList()
            listType = 'ul'
          }
          listItems.push(trimmed.substring(2))
          i++
          continue
        }
        
        // Ordered list
        if (trimmed.match(/^\d+\.\s/)) {
          flushCodeBlock()
          if (listType !== 'ol') {
            flushList()
            listType = 'ol'
          }
          listItems.push(trimmed.replace(/^\d+\.\s/, ''))
          i++
          continue
        }
        
        // Empty line
        if (trimmed === '') {
          flushList()
          flushCodeBlock()
          elements.push(<br key={i} />)
          i++
          continue
        }
        
        // Regular paragraph
        flushList()
        flushCodeBlock()
        const processed = processInlineMarkdown(trimmed)
        elements.push(
          <p key={i} style={{ margin: '8px 0' }}>
            {processed}
          </p>
        )
        i++
      }
      
      // Flush any remaining lists or code blocks
      flushList()
      flushCodeBlock()
      
      return elements
    }

    return (
      <HTMLContainer style={{ width: shape.props.w, height: shape.props.h }}>
        <StandardizedToolWrapper
          title={shape.props.title}
          primaryColor={shape.props.primaryColor}
          isSelected={isSelected}
          width={shape.props.w}
          height={shape.props.h}
          onClose={handleClose}
          onMinimize={handleMinimize}
          isMinimized={isMinimized}
          onMaximize={toggleMaximize}
          isMaximized={isMaximized}
          editor={this.editor}
          shapeId={shape.id}
          isPinnedToView={shape.props.pinnedToView}
          onPinToggle={handlePinToggle}
          tags={shape.props.tags}
          onTagsChange={(newTags) => {
            this.editor.updateShape<IFathomNoteShape>({
              id: shape.id,
              type: 'FathomNote',
              props: {
                ...shape.props,
                tags: newTags,
              }
            })
          }}
          tagsEditable={true}
        >
          <div 
            style={contentStyle}
            onPointerDown={(e) => {
              // Allow text selection - don't stop propagation for text selection
              // Only stop if clicking on interactive elements (links, etc.)
              const target = e.target as HTMLElement
              if (target.tagName === 'A' || target.closest('a')) {
                // Let links work normally
                return
              }
              // For text selection, allow the event to bubble but don't prevent default
              // This allows text selection while still allowing shape selection
            }}
            onMouseDown={(e) => {
              // Allow text selection on mouse down
              // Don't prevent default to allow text selection
              const target = e.target as HTMLElement
              if (target.tagName === 'A' || target.closest('a')) {
                return
              }
            }}
          >
            {formatContent(shape.props.content)}
          </div>
          {/* Copy button at bottom right */}
          <button
            onClick={(e) => {
              e.stopPropagation()
              handleCopy()
            }}
            onPointerDown={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
            style={{
              position: 'absolute',
              bottom: '8px',
              right: '8px',
              backgroundColor: isCopied ? '#10b981' : 'rgba(0, 0, 0, 0.05)',
              border: '1px solid rgba(0, 0, 0, 0.1)',
              borderRadius: '4px',
              padding: '6px',
              cursor: 'pointer',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              width: '28px',
              height: '28px',
              transition: 'background-color 0.2s ease',
              zIndex: 10,
              opacity: 0.8,
            }}
            onMouseEnter={(e) => {
              if (!isCopied) {
                e.currentTarget.style.opacity = '1'
                e.currentTarget.style.backgroundColor = 'rgba(0, 0, 0, 0.1)'
              }
            }}
            onMouseLeave={(e) => {
              if (!isCopied) {
                e.currentTarget.style.opacity = '0.8'
                e.currentTarget.style.backgroundColor = 'rgba(0, 0, 0, 0.05)'
              }
            }}
            title={isCopied ? 'Copied!' : 'Copy content to clipboard'}
          >
            {isCopied ? (
              <svg width="14" height="14" viewBox="0 0 24 24" fill="white">
                <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
              </svg>
            ) : (
              <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                <path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z"/>
              </svg>
            )}
          </button>
        </StandardizedToolWrapper>
      </HTMLContainer>
    )
  }

  indicator(shape: IFathomNoteShape) {
    return <rect width={shape.props.w} height={shape.props.h} />
  }

  /**
   * Create a Fathom note shape from data
   */
  static createFromData(
    data: {
      id: string
      title: string
      content: string
      tags: string[]
      primaryColor?: string
    },
    x: number = 0,
    y: number = 0
  ): IFathomNoteShape {
    return {
      id: createShapeId(),
      type: 'FathomNote',
      x,
      y,
      rotation: 0,
      index: 'a1' as IndexKey,
      parentId: 'page:page' as TLParentId,
      isLocked: false,
      opacity: 1,
      meta: {},
      typeName: 'shape',
      props: {
        w: 500,
        h: 600,
        title: data.title,
        content: data.content,
        tags: data.tags,
        noteId: data.id,
        pinnedToView: false,
        primaryColor: data.primaryColor || FathomNoteShape.PRIMARY_COLOR,
      }
    }
  }
}

