'use client'

import { useCallback, useEffect, useRef, useState } from 'react'
import { Command } from 'cmdk'
import {
  Search,
  FileText,
  File,
  Image,
  Settings,
  LayoutDashboard,
  Plus,
  Users,
  ArrowRightLeft,
  ClipboardList,
  Upload,
  BarChart3,
  Clock,
  Maximize2,
  Minimize2,
  Sparkles,
  SpellCheck,
  Wand2,
} from 'lucide-react'
import { toast } from 'sonner'
import { cmsApi } from '../lib/api.js'
import {
  coauthorActionDescription,
  coauthorActionLabel,
  coauthorText,
  type CoauthorAction,
  type ProofreadChange,
} from '../lib/coauthor-client.js'
import {
  isAiSlashCommandInput,
  orderedAiActions,
  parseAiSlashCommand,
} from '../lib/slash-command.js'
import { getActiveEditor } from '../lib/active-editor.js'
import { CoauthorResultPopover } from './CoauthorResultPopover.js'
import type { Editor } from '@tiptap/react'

interface SearchResultDocument {
  id: string
  title: string | null
  slug: string | null
  collection: string
  status: string
  updatedAt: string
}

interface SearchResultMedia {
  id: string
  filename: string
  altText: string | null
  mimeType: string
  storageKey: string
}

interface SearchResultUser {
  id: string
  name: string
  email: string
  role: string
}

interface RecentItem {
  id: string
  label: string
  path: string
  type: 'document' | 'media' | 'user' | 'action'
}

const RECENT_ITEMS_KEY = 'actuate-recent-items'
const MAX_RECENT_ITEMS = 5

function loadRecentItems(): RecentItem[] {
  try {
    const raw = localStorage.getItem(RECENT_ITEMS_KEY)
    return raw ? JSON.parse(raw) : []
  } catch {
    return []
  }
}

function saveRecentItem(item: RecentItem): void {
  try {
    const items = loadRecentItems().filter((i) => i.id !== item.id)
    items.unshift(item)
    localStorage.setItem(RECENT_ITEMS_KEY, JSON.stringify(items.slice(0, MAX_RECENT_ITEMS)))
  } catch {
    /* localStorage unavailable */
  }
}

const staticActions = [
  { id: 'action-new-page', label: 'Create new page', icon: Plus, action: '/pages/new' },
  { id: 'action-new-post', label: 'Create new post', icon: Plus, action: '/posts/new' },
  { id: 'action-upload-media', label: 'Upload media', icon: Upload, action: '/media?upload=true' },
  { id: 'action-view-seo', label: 'View SEO', icon: BarChart3, action: '/seo' },
  { id: 'action-open-settings', label: 'Open settings', icon: Settings, action: '/settings' },
]

const AI_ACTION_ICONS: Record<CoauthorAction, typeof Sparkles> = {
  rewrite: Wand2,
  expand: Maximize2,
  compress: Minimize2,
  proofread: SpellCheck,
}

interface AiResultState {
  open: boolean
  loading: boolean
  action: CoauthorAction
  before: string
  after: string
  error?: string
  changes?: ProofreadChange[]
  /** Editor captured at submission time; null means clipboard-only. */
  target: Editor | null
}

const INITIAL_AI_RESULT: AiResultState = {
  open: false,
  loading: false,
  action: 'rewrite',
  before: '',
  after: '',
  target: null,
}

function truncatePreview(text: string, max = 60): string {
  if (text.length <= max) return text
  return `${text.slice(0, max - 1)}…`
}

const navigationCommands = [
  { id: 'nav-dashboard', label: 'Dashboard', icon: LayoutDashboard, action: '/' },
  { id: 'nav-posts', label: 'Posts', icon: FileText, action: '/posts' },
  { id: 'nav-pages', label: 'Pages', icon: File, action: '/pages' },
  { id: 'nav-media', label: 'Media', icon: Image, action: '/media' },
  { id: 'nav-forms', label: 'Forms', icon: ClipboardList, action: '/forms' },
  { id: 'nav-redirects', label: 'Redirects', icon: ArrowRightLeft, action: '/redirects' },
  { id: 'nav-users', label: 'Users', icon: Users, action: '/users' },
  { id: 'nav-settings', label: 'Settings', icon: Settings, action: '/settings' },
]

export interface CommandPaletteProps {
  open: boolean
  onOpenChange: (open: boolean) => void
  onNavigate: (path: string) => void
}

export function CommandPalette({ open, onOpenChange, onNavigate }: CommandPaletteProps) {
  const [search, setSearch] = useState('')
  const [documents, setDocuments] = useState<SearchResultDocument[]>([])
  const [media, setMedia] = useState<SearchResultMedia[]>([])
  const [users, setUsers] = useState<SearchResultUser[]>([])
  const [recentItems, setRecentItems] = useState<RecentItem[]>([])
  const [loading, setLoading] = useState(false)
  const [aiResult, setAiResult] = useState<AiResultState>(INITIAL_AI_RESULT)
  const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined)

  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        onOpenChange(!open)
      }
    }
    document.addEventListener('keydown', down)
    return () => document.removeEventListener('keydown', down)
  }, [open, onOpenChange])

  useEffect(() => {
    if (open) {
      setRecentItems(loadRecentItems())
    }
  }, [open])

  const fetchResults = useCallback(async (query: string) => {
    if (!query.trim()) {
      setDocuments([])
      setMedia([])
      setUsers([])
      return
    }
    setLoading(true)
    try {
      const res = await cmsApi<{
        documents: SearchResultDocument[]
        media: SearchResultMedia[]
        users: SearchResultUser[]
      }>(`/search/global?q=${encodeURIComponent(query)}`)
      if (res.data) {
        setDocuments(res.data.documents ?? [])
        setMedia(res.data.media ?? [])
        setUsers(res.data.users ?? [])
      }
    } catch {
      setDocuments([])
      setMedia([])
      setUsers([])
    } finally {
      setLoading(false)
    }
  }, [])

  useEffect(() => {
    if (debounceRef.current) clearTimeout(debounceRef.current)
    if (!search.trim() || isAiSlashCommandInput(search)) {
      setDocuments([])
      setMedia([])
      setUsers([])
      return
    }
    debounceRef.current = setTimeout(() => fetchResults(search), 250)
    return () => {
      if (debounceRef.current) clearTimeout(debounceRef.current)
    }
  }, [search, fetchResults])

  const handleSelect = (action: string, recent?: RecentItem) => {
    if (recent) {
      saveRecentItem(recent)
    }
    onNavigate(action)
    onOpenChange(false)
    setSearch('')
  }

  const runSlashAction = useCallback(
    async (action: CoauthorAction, text: string) => {
      if (!text.trim()) {
        toast.message(`Provide text after the command: /ai ${action} <your text>`)
        return
      }
      const target = getActiveEditor()
      onOpenChange(false)
      setSearch('')
      setAiResult({
        open: true,
        loading: true,
        action,
        before: text,
        after: '',
        target,
      })

      const outcome = await coauthorText(action, text)
      if (!outcome.ok) {
        setAiResult((prev) => ({ ...prev, loading: false, error: outcome.error }))
        toast.error(outcome.error)
        return
      }
      setAiResult((prev) => ({
        ...prev,
        loading: false,
        after: outcome.result.text,
        changes: outcome.result.changes,
        error: undefined,
      }))
    },
    [onOpenChange],
  )

  const handleAiApply = useCallback(() => {
    const { target, after, action } = aiResult
    if (!target) {
      void navigator.clipboard
        ?.writeText(after)
        .then(() => toast.success('Copied to clipboard.'))
        .catch(() => toast.error('Clipboard unavailable — copy manually.'))
      setAiResult(INITIAL_AI_RESULT)
      return
    }
    const { from, to, empty } = target.state.selection
    const chain = target.chain().focus()
    if (!empty) {
      chain.insertContentAt({ from, to }, after).run()
    } else {
      chain.insertContent(after).run()
    }
    toast.success(`${coauthorActionLabel(action)} applied.`)
    setAiResult(INITIAL_AI_RESULT)
  }, [aiResult])

  const handleAiCopy = useCallback(async () => {
    try {
      await navigator.clipboard.writeText(aiResult.after)
      toast.success('Copied to clipboard.')
    } catch {
      toast.error('Clipboard unavailable — copy manually.')
    }
  }, [aiResult.after])

  const handleAiDiscard = useCallback(() => {
    setAiResult(INITIAL_AI_RESULT)
  }, [])

  const hasQuery = search.trim().length > 0
  const hasResults = documents.length > 0 || media.length > 0 || users.length > 0
  const isAiMode = isAiSlashCommandInput(search)
  const aiCommand = parseAiSlashCommand(search)
  const aiPayload = aiCommand?.text ?? ''
  const aiActions = orderedAiActions(search)

  const popover = (
    <CoauthorResultPopover
      open={aiResult.open}
      loading={aiResult.loading}
      action={aiResult.action}
      before={aiResult.before}
      after={aiResult.after}
      error={aiResult.error}
      changes={aiResult.changes}
      applyLabel={aiResult.target ? 'Apply to editor' : 'Copy to clipboard'}
      onApply={handleAiApply}
      onCopy={handleAiCopy}
      onDiscard={handleAiDiscard}
    />
  )

  if (!open) return popover

  return (
    <>
      <div
        className="fixed inset-0 z-50 flex items-start justify-center bg-black/50 px-4 pt-[20vh]"
        onClick={() => {
          onOpenChange(false)
          setSearch('')
        }}
        role="presentation"
      >
        <div
          role="dialog"
          aria-modal="true"
          className="w-full max-w-2xl"
          onClick={(e) => e.stopPropagation()}
        >
          <Command
            className="w-full overflow-hidden rounded-lg bg-white shadow-2xl"
            onKeyDown={(e) => {
              if (e.key === 'Escape') {
                onOpenChange(false)
                setSearch('')
              }
            }}
            shouldFilter={!hasQuery}
          >
            <div className="flex items-center border-b border-gray-200 px-4">
              <Search className="mr-3 h-5 w-5 shrink-0 text-gray-400" />
              <Command.Input
                value={search}
                onValueChange={setSearch}
                placeholder="Search, jump to, or type /ai to use the AI co-author…"
                className="w-full bg-transparent py-4 text-base placeholder:text-gray-400 focus:outline-none"
              />
              {loading && (
                <div className="mr-2 h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
              )}
              <kbd className="hidden shrink-0 rounded bg-gray-100 px-2 py-1 font-mono text-xs text-gray-600 sm:inline-block">
                ESC
              </kbd>
            </div>

            <Command.List className="max-h-[400px] overflow-y-auto p-2">
              <Command.Empty className="py-6 text-center text-sm text-gray-500">
                {loading ? 'Searching...' : 'No results found.'}
              </Command.Empty>

              {isAiMode && (
                <Command.Group heading="AI co-author" className="px-2 py-2">
                  <div className="mb-2 flex items-center gap-1 text-xs font-medium text-gray-500">
                    <Sparkles className="h-3 w-3" aria-hidden /> AI CO-AUTHOR
                  </div>
                  {aiActions.map((action) => {
                    const Icon = AI_ACTION_ICONS[action]
                    const isExactMatch = aiCommand?.action === action
                    const previewable = isExactMatch && aiPayload.length > 0
                    return (
                      <Command.Item
                        key={`ai-${action}`}
                        value={`ai ${action}`}
                        onSelect={() => runSlashAction(action, aiPayload)}
                        className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                      >
                        <Icon className="h-4 w-4 text-blue-600" />
                        <div className="flex min-w-0 flex-col">
                          <span className="font-medium">
                            {coauthorActionLabel(action)}
                            {previewable && (
                              <span className="ml-1 font-normal text-gray-500">
                                “{truncatePreview(aiPayload)}”
                              </span>
                            )}
                          </span>
                          <span className="text-xs text-gray-400">
                            {coauthorActionDescription(action)}
                          </span>
                        </div>
                        <kbd className="ml-auto rounded bg-gray-100 px-1.5 py-0.5 font-mono text-[10px] text-gray-500">
                          /ai {action}
                        </kbd>
                      </Command.Item>
                    )
                  })}
                </Command.Group>
              )}

              {!isAiMode && !hasQuery && recentItems.length > 0 && (
                <Command.Group heading="Recent" className="px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">RECENT</div>
                  {recentItems.map((item) => (
                    <Command.Item
                      key={`recent-${item.id}`}
                      value={`recent ${item.label}`}
                      onSelect={() => handleSelect(item.path, item)}
                      className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                    >
                      <Clock className="h-4 w-4 text-gray-400" />
                      <span>{item.label}</span>
                      <span className="ml-auto text-xs text-gray-400 capitalize">{item.type}</span>
                    </Command.Item>
                  ))}
                </Command.Group>
              )}

              {!isAiMode && hasQuery && documents.length > 0 && (
                <Command.Group heading="Documents" className="px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">DOCUMENTS</div>
                  {documents.map((doc) => (
                    <Command.Item
                      key={`doc-${doc.id}`}
                      value={doc.title ?? doc.slug ?? doc.id}
                      onSelect={() =>
                        handleSelect(`/collections/${doc.collection}/${doc.id}`, {
                          id: `doc-${doc.id}`,
                          label: doc.title ?? doc.slug ?? doc.id,
                          path: `/collections/${doc.collection}/${doc.id}`,
                          type: 'document',
                        })
                      }
                      className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                    >
                      <FileText className="h-4 w-4 text-gray-500" />
                      <div className="flex min-w-0 flex-col">
                        <span className="truncate">{doc.title ?? doc.slug ?? doc.id}</span>
                        <span className="text-xs text-gray-400">
                          {doc.collection} &middot; {doc.status}
                        </span>
                      </div>
                    </Command.Item>
                  ))}
                </Command.Group>
              )}

              {!isAiMode && hasQuery && media.length > 0 && (
                <Command.Group heading="Media" className="mt-1 px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">MEDIA</div>
                  {media.map((m) => (
                    <Command.Item
                      key={`media-${m.id}`}
                      value={m.filename}
                      onSelect={() =>
                        handleSelect(`/media?selected=${m.id}`, {
                          id: `media-${m.id}`,
                          label: m.filename,
                          path: `/media?selected=${m.id}`,
                          type: 'media',
                        })
                      }
                      className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                    >
                      <Image className="h-4 w-4 text-gray-500" />
                      <div className="flex min-w-0 flex-col">
                        <span className="truncate">{m.filename}</span>
                        <span className="text-xs text-gray-400">{m.mimeType}</span>
                      </div>
                    </Command.Item>
                  ))}
                </Command.Group>
              )}

              {!isAiMode && hasQuery && users.length > 0 && (
                <Command.Group heading="Users" className="mt-1 px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">USERS</div>
                  {users.map((u) => (
                    <Command.Item
                      key={`user-${u.id}`}
                      value={`${u.name} ${u.email}`}
                      onSelect={() =>
                        handleSelect(`/users?selected=${u.id}`, {
                          id: `user-${u.id}`,
                          label: u.name,
                          path: `/users?selected=${u.id}`,
                          type: 'user',
                        })
                      }
                      className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                    >
                      <Users className="h-4 w-4 text-gray-500" />
                      <div className="flex min-w-0 flex-col">
                        <span className="truncate">{u.name}</span>
                        <span className="text-xs text-gray-400">
                          {u.email} &middot; {u.role}
                        </span>
                      </div>
                    </Command.Item>
                  ))}
                </Command.Group>
              )}

              {!isAiMode && (!hasQuery || !hasResults) && (
                <Command.Group heading="Navigation" className="px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">NAVIGATION</div>
                  {navigationCommands.map((cmd) => {
                    const Icon = cmd.icon
                    return (
                      <Command.Item
                        key={cmd.id}
                        value={cmd.label}
                        onSelect={() => handleSelect(cmd.action)}
                        className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                      >
                        <Icon className="h-4 w-4 text-gray-500" />
                        <span>{cmd.label}</span>
                      </Command.Item>
                    )
                  })}
                </Command.Group>
              )}

              {!isAiMode && (
                <Command.Group heading="Actions" className="mt-2 px-2 py-2">
                  <div className="mb-2 text-xs font-medium text-gray-500">ACTIONS</div>
                  {staticActions.map((cmd) => {
                    const Icon = cmd.icon
                    return (
                      <Command.Item
                        key={cmd.id}
                        value={cmd.label}
                        onSelect={() =>
                          handleSelect(cmd.action, {
                            id: cmd.id,
                            label: cmd.label,
                            path: cmd.action,
                            type: 'action',
                          })
                        }
                        className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 hover:bg-gray-100 data-[selected=true]:bg-gray-100"
                      >
                        <Icon className="h-4 w-4 text-gray-500" />
                        <span>{cmd.label}</span>
                      </Command.Item>
                    )
                  })}
                </Command.Group>
              )}
            </Command.List>

            <div className="flex items-center justify-between border-t border-gray-200 bg-gray-50 px-4 py-3 text-xs text-gray-500">
              <div className="flex items-center gap-4">
                <span className="flex items-center gap-1">
                  <kbd className="rounded border border-gray-300 bg-white px-1.5 py-0.5 text-[10px]">
                    ↑
                  </kbd>
                  <kbd className="rounded border border-gray-300 bg-white px-1.5 py-0.5 text-[10px]">
                    ↓
                  </kbd>
                  to navigate
                </span>
                <span className="flex items-center gap-1">
                  <kbd className="rounded border border-gray-300 bg-white px-1.5 py-0.5 text-[10px]">
                    ↵
                  </kbd>
                  to select
                </span>
              </div>
            </div>
          </Command>
        </div>
      </div>
      {popover}
    </>
  )
}
