'use client'

import React, { useRef } from 'react'
import type { EditorView } from '@codemirror/view'
import {
  BoldIcon,
  ItalicIcon,
  CodeBracketIcon,
  H1Icon, H2Icon, H3Icon,
  ListBulletIcon,
  NumberedListIcon,
  QueueListIcon,
  ChatBubbleLeftRightIcon,
  LinkIcon,
  PhotoIcon,
  GlobeAltIcon,
  CodeBracketSquareIcon,
  TableCellsIcon,
  VariableIcon,
  ChartPieIcon,
  MusicalNoteIcon,
  FilmIcon,
  PresentationChartLineIcon,
  MinusIcon,
  ViewfinderCircleIcon,
  EyeSlashIcon,
  ArrowsRightLeftIcon,
  PencilSquareIcon,
  StrikethroughIcon
} from '@heroicons/react/24/outline'

import { MAX_IMAGE_BYTES, MAX_IMAGE_LABEL } from '~/lib/security-constants'
import { compressImage } from '~/lib/compress-image'

interface ToolbarProps {
  editor: EditorView | null
  viewMode?: 'split' | 'editor' | 'preview'
  onViewModeChange?: (mode: 'split' | 'editor' | 'preview') => void
  onImageError?: (message: string) => void
}

interface ToolbarAction {
  id: string
  label: string
  icon: React.ComponentType<{ className?: string }> | string
  action: () => void
  separator?: boolean
  shortcut?: string
}

const CodeMirrorToolbar: React.FC<ToolbarProps> = ({
  editor,
  viewMode = 'split',
  onViewModeChange,
  onImageError,
}) => {
  const imageInputRef = useRef<HTMLInputElement>(null)

  const insertText = (text: string) => {
    if (!editor) return
    
    const selection = editor.state.selection.main
    const transaction = editor.state.update({
      changes: { from: selection.from, to: selection.to, insert: text },
      selection: { anchor: selection.from + text.length }
    })
    editor.dispatch(transaction)
    editor.focus()
  } 

  const wrapText = (before: string, after: string = before) => {
    if (!editor) return
    
    const selection = editor.state.selection.main
    const selectedText = editor.state.doc.sliceString(selection.from, selection.to)
    const newText = before + selectedText + after
    
    const transaction = editor.state.update({
      changes: { from: selection.from, to: selection.to, insert: newText },
      selection: selectedText === '' 
        ? { anchor: selection.from + before.length }
        : { anchor: selection.from + newText.length }
    })
    editor.dispatch(transaction)
    editor.focus()
  }

  const insertAtLineStart = (prefix: string) => {
    if (!editor) return
    
    const selection = editor.state.selection.main
    const line = editor.state.doc.lineAt(selection.from)
    const newContent = prefix + line.text
    
    // Calculate cursor position after the prefix
    const cursorOffset = selection.from - line.from
    const newCursorPosition = line.from + prefix.length + cursorOffset
    
    const transaction = editor.state.update({
      changes: { from: line.from, to: line.to, insert: newContent },
      selection: { anchor: newCursorPosition }
    })
    editor.dispatch(transaction)
    editor.focus()
  }

  const insertNewLines = (text: string) => {
    if (!editor) return
    
    const selection = editor.state.selection.main
    const line = editor.state.doc.lineAt(selection.from)
    
    // Add newlines before and after if not at start/end of line
    let textToInsert = text
    if (line.text.trim() !== '' && selection.from > line.from) {
      textToInsert = '\n\n' + text + '\n\n'
    } else if (selection.from === line.from) {
      textToInsert = text + '\n\n'
    }
    
    // Calculate cursor position inside the inserted content if there's a placeholder
    const placeholderMatch = text.match(/Your code here|\/\/ Your code here|Simple Tune|Decision|x\^2|sentence|content/i)
    let newCursorPos = selection.from + textToInsert.length
    
    if (placeholderMatch) {
      // Find the placeholder in the inserted text
      const placeholderPos = textToInsert.indexOf(placeholderMatch[0])
      if (placeholderPos !== -1) {
        newCursorPos = selection.from + placeholderPos
      }
    }
    
    const transaction = editor.state.update({
      changes: { from: selection.from, to: selection.to, insert: textToInsert },
      selection: { anchor: newCursorPos }
    })
    editor.dispatch(transaction)
    editor.focus()
  }

  const insertCodeBlock = (text: string) => {
    if (!editor) return
    
    const selection = editor.state.selection.main
    const line = editor.state.doc.lineAt(selection.from)
    
    // Add newlines before if not at start of line, and exactly 1 newline after
    let textToInsert = text
    if (line.text.trim() !== '' && selection.from > line.from) {
      textToInsert = '\n\n' + text + '\n'
    } else if (selection.from === line.from) {
      textToInsert = text + '\n'
    }
    
    // Calculate cursor position inside the inserted content if there's a placeholder
    const placeholderMatch = text.match(/Your code here|\/\/ Your code here|Simple Tune|x\^2/i)
    let newCursorPos = selection.from + textToInsert.length
    
    if (placeholderMatch) {
      // Find the placeholder in the inserted text
      const placeholderPos = textToInsert.indexOf(placeholderMatch[0])
      if (placeholderPos !== -1) {
        newCursorPos = selection.from + placeholderPos
      }
    }
    
    const transaction = editor.state.update({
      changes: { from: selection.from, to: selection.to, insert: textToInsert },
      selection: { anchor: newCursorPos }
    })
    editor.dispatch(transaction)
    editor.focus()
  }

    // View mode actions
  const viewModeActions: ToolbarAction[] = [
    {
      id: 'view-split',
      label: 'Split View',
      icon: ArrowsRightLeftIcon,
      action: () => onViewModeChange?.('split')
    },
    {
      id: 'view-editor',
      label: 'Editor Only',
      icon: PencilSquareIcon,
      action: () => onViewModeChange?.('editor')
    },
    {
      id: 'view-preview',
      label: 'Preview Only',
      icon: ViewfinderCircleIcon,
      action: () => onViewModeChange?.('preview')
    }
  ]

  const actions: ToolbarAction[] = [
    {
      id: 'h1',
      label: 'Heading 1',
      icon: H1Icon,
      action: () => insertAtLineStart('# ')
    },
    {
      id: 'h2',
      label: 'Heading 2',
      icon: H2Icon,
      action: () => insertAtLineStart('## ')
    },
    {
      id: 'h3',
      label: 'Heading 3',
      icon: H3Icon,
      action: () => insertAtLineStart('### ')
    },
    {
      id: 'bold',
      label: 'Bold',
      icon: BoldIcon,
      shortcut: 'Cmd+B',
      action: () => wrapText('**')
    },
    {
      id: 'italic',
      label: 'Italic',
      icon: ItalicIcon,
      shortcut: 'Cmd+I',
      action: () => wrapText('*')
    },
    {
      id: 'strikethrough',
      label: 'Strikethrough',
      icon: StrikethroughIcon,
      shortcut: 'Cmd+S',
      action: () => wrapText('~', '~')
    },
    {
      id: 'ul',
      label: 'Unordered List',
      icon: ListBulletIcon,
      action: () => insertAtLineStart('- ')
    },
    {
      id: 'ol',
      label: 'Ordered List',
      icon: NumberedListIcon,
      action: () => insertAtLineStart('1. ')
    },
    {      
      id: 'cl',
      label: 'Check List',
      icon: QueueListIcon,
      action: () => insertAtLineStart('- [x] Task 1\n- [ ] Task 2')
    },
    {
      id: 'table',
      label: 'Table',
      icon: TableCellsIcon,
      action: () => insertCodeBlock('| Column 1 | Column 2 | Column 3 |\n|----------|----------|----------|\n| Row 1    | Data     | Data     |\n| Row 2    | Data     | Data     |')
    },
    {
      id: 'quote',
      label: 'Blockquote',
      icon: ChatBubbleLeftRightIcon,
      action: () => insertAtLineStart('> ')
    },
    {
      id: 'hr',
      label: 'Horizontal Rule',
      icon: MinusIcon,
      action: () => insertNewLines('---')
    },
    {
      id: 'link',
      label: 'Link',
      icon: LinkIcon,
      action: () => wrapText('[', '](https://example.com)')
    },
    {
      id: 'image',
      label: 'Insert image',
      icon: PhotoIcon,
      action: () => imageInputRef.current?.click()
    },
    {
      id: 'separator1',
      label: '',
      icon: '',
      action: () => {},
      separator: true
    },
    {
      id: 'embed',
      label: 'Embed from Bluesky, YouTube, Apple Music, etc.',
      icon: GlobeAltIcon,
      action: () => insertText(`\`\`\`embed\nhttps://bsky.app/profile/bsky.app/post/3m2kdgmbbzc2o\n\`\`\`\n`)
    },
    {
      id: 'spoiler',
      label: 'Spoiler',
      icon: EyeSlashIcon,
      action: () => wrapText('::spoiler:: ', ' ::spoiler::')
    },
    {
      id: 'code',
      label: 'Inline Code',
      icon: CodeBracketIcon,
      shortcut: 'Cmd+`',
      action: () => wrapText('`')
    },
    {
      id: 'code-block',
      label: 'Code Block',
      icon: CodeBracketSquareIcon,
      action: () => insertCodeBlock('```javascript\n// Your code here\n```')
    },
    {
      id: 'math',
      label: 'Math Expression',
      icon: VariableIcon,
      action: () => wrapText('$$ ', ' $$')
    },
    {
      id: 'functionplot',
      label: 'Function Plot',
      icon: PresentationChartLineIcon,
      action: () => insertCodeBlock('```functionplot\nx^2\nsin(x)\ncos(x)\n```')
    },
    {
      id: 'mermaid',
      label: 'Mermaid Diagram',
      icon: ChartPieIcon,
      action: () => insertCodeBlock('```mermaid\nflowchart TD\n    A[Start] --> B{Decision}\n    B -->|Yes| C[Action]\n    B -->|No| D[End]\n    C --> D\n```')
    },
    {
      id: 'abc',
      label: 'ABC Musical Notation',
      icon: MusicalNoteIcon,
      action: () => insertCodeBlock('```abc\nX:1\nT:Simple Tune\nK:C\nCDE F|GAB c|\n```')
    },
    {
      id: 'fountain',
      label: 'Fountain Screenplay',
      icon: FilmIcon,
      action: () => insertCodeBlock('```fountain\nINT. ROOM - DAY\n\nJOHN sits at a desk, writing.\n\nJOHN\n(thoughtful)\nThis is going to be great.\n\nFADE OUT.\n```')
    }
  ]

  return (
    <div className="bg-gray-50 dark:bg-gray-750 px-3 py-2 border border-gray-200 dark:border-gray-600 flex items-center gap-0.5 flex-wrap overflow-x-auto rounded-t-lg">
      <input
        ref={imageInputRef}
        type="file"
        accept="image/*"
        className="hidden"
        onChange={async (e) => {
          const file = e.target.files?.[0]
          e.target.value = ''
          if (!file) return
          try {
            const compressed = await compressImage(file)
            if (compressed.size > MAX_IMAGE_BYTES) {
              onImageError?.(`Image exceeds ${MAX_IMAGE_LABEL} even after compression. Please use a smaller image.`)
              return
            }
            const url = URL.createObjectURL(compressed)
            const alt = file.name.replace(/\.[^.]+$/, '')
            insertText(`![${alt}](${url})`)
          } catch {
            onImageError?.('Image could not be processed. Please try a different file.')
          }
        }}
      />
      {/* View mode buttons */}
      {onViewModeChange && (
        <>
          <div className="flex items-center gap-0.5 mr-2">
            {viewModeActions.map((action) => {
              const IconComponent = action.icon as React.ComponentType<{ className?: string }>
              return (
                <button
                  type="button"
                  key={action.id}
                  onClick={action.action}
                  className={`px-2.5 py-1.5 text-xs font-medium rounded-md transition-colors duration-100 min-w-[32px] text-center flex-shrink-0 flex items-center justify-center ${
                    (action.id === 'view-split' && viewMode === 'split') ||
                    (action.id === 'view-editor' && viewMode === 'editor') ||
                    (action.id === 'view-preview' && viewMode === 'preview')
                      ? 'bg-blue-100 dark:bg-blue-900 text-blue-900 dark:text-blue-100 border border-blue-200 dark:border-blue-800'
                      : 'hover:bg-gray-200 dark:hover:bg-gray-600 active:bg-gray-300 dark:active:bg-gray-500 text-gray-700 dark:text-gray-900 hover:text-gray-900 dark:hover:text-gray-100'
                  }`}
                  title={action.label}
                >
                  <IconComponent className="h-4 w-4" />
                </button>
              )
            })}
          </div>
          <div className="w-px h-5 bg-gray-300 dark:bg-gray-500 mx-1 flex-shrink-0" />
        </>
      )}
      
      {/* Regular toolbar actions */}
      {actions.map((action) => {
        if (action.separator) {
          return (
            <div
              key={action.id}
              className="w-px h-5 bg-gray-300 dark:bg-gray-500 mx-1 flex-shrink-0"
            />
          )
        }

        const IconComponent = action.icon as React.ComponentType<{ className?: string }>
        const isStringIcon = typeof action.icon === 'string'
        const isUndersizedIcon =  IconComponent === CodeBracketIcon || 
                                  IconComponent === CodeBracketSquareIcon || 
                                  IconComponent === VariableIcon ||
                                  IconComponent === PresentationChartLineIcon ||
                                  IconComponent === ChartPieIcon ||
                                  IconComponent === MusicalNoteIcon ||
                                  IconComponent === FilmIcon
        
        return (
          <button
            type="button"
            key={action.id}
            onClick={action.action}
            className="px-2.5 py-1.5 text-xs font-medium rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 active:bg-gray-300 dark:active:bg-gray-500 transition-colors duration-100 min-w-[32px] text-center flex-shrink-0 text-gray-700 dark:text-gray-900 hover:text-gray-900 dark:hover:text-gray-100 flex items-center justify-center"
            title={action.shortcut ? `${action.label} (${action.shortcut})` : action.label}
          >
            {isStringIcon ? (
              <span>{action.icon as string}</span>
            ) : (
              <IconComponent className={`${isUndersizedIcon ? "size-5": "size-4"}`} />
            )}
          </button>
        )
      })}
    </div>
  )
}

export default CodeMirrorToolbar