import { useState, useRef, useEffect, useMemo } from 'react'
import Editor from '@monaco-editor/react'
import useAppStore from '../store/appStore'
import { useCopyToClipboard } from '../hooks/useCopyToClipboard'
import {
  deleteFile as apiDeleteFile,
  writeFile as apiWriteFile,
} from '../api/projects'

// Monaco language by extension / filename.
//
// We pick from the language IDs Monaco bundles natively — the editor lazy-
// loads the appropriate tokenizer when the file is opened.  Anything we
// don't recognize falls through to 'plaintext' (no highlighting) rather
// than masquerading as JavaScript, which used to silently mis-highlight
// .py files in the FastAPI templates.
//
// To add a language: drop its extensions into _EXT_LANG; if Monaco doesn't
// ship it natively, fall back to a syntactically-similar one (e.g.
// .prisma → graphql is a closer match than javascript).
const _EXT_LANG = {
  // JS / TS family
  js:    'javascript', jsx: 'javascript', mjs: 'javascript', cjs: 'javascript',
  ts:    'typescript', tsx: 'typescript',
  // Python
  py:    'python', pyi: 'python', pyx: 'python',
  // Web
  css:   'css', scss: 'scss', sass: 'scss', less: 'less',
  html:  'html', htm: 'html', svg: 'xml', xml: 'xml',
  vue:   'html', svelte: 'html',
  // Data / config
  json:  'json', json5: 'json', jsonc: 'json',
  yml:   'yaml', yaml: 'yaml',
  toml:  'ini', ini: 'ini', env: 'ini',
  // DB / schema
  sql:   'sql',
  prisma:'graphql',         // close-enough tokenizer
  graphql:'graphql', gql:'graphql',
  // Docs
  md:    'markdown', mdx: 'markdown',
  // Shell
  sh:    'shell', bash: 'shell', zsh: 'shell', fish: 'shell',
  // Container / infra
  dockerfile:'dockerfile',
  // Systems languages
  go:    'go',
  rs:    'rust',
  java:  'java', kt: 'kotlin', kts: 'kotlin',
  swift: 'swift',
  c:     'c', h: 'c',
  cpp:   'cpp', cc: 'cpp', cxx: 'cpp', hpp: 'cpp', hh: 'cpp',
  cs:    'csharp',
  rb:    'ruby',
  php:   'php',
  // Functional / scripting
  lua:   'lua', pl: 'perl', r: 'r', scala: 'scala', dart: 'dart',
  // Misc
  txt:   'plaintext', log: 'plaintext',
}

// Extensionless conventional names — handled before the extension lookup.
const _NAME_LANG = {
  'dockerfile':      'dockerfile',
  'containerfile':   'dockerfile',
  'makefile':        'makefile',
  'rakefile':        'ruby',
  'gemfile':         'ruby',
  '.gitignore':      'ignore',
  '.dockerignore':   'ignore',
  '.env':            'ini',
  '.env.local':      'ini',
  '.env.development':'ini',
  '.env.production': 'ini',
}

const LANG = (path = '') => {
  if (!path) return 'plaintext'
  const base = path.split('/').pop().toLowerCase()
  if (_NAME_LANG[base]) return _NAME_LANG[base]
  // Match the longest known multi-dot suffix first (e.g. ".env.local" already
  // caught by _NAME_LANG; ".d.ts" handled here).
  if (base.endsWith('.d.ts')) return 'typescript'
  const dot = base.lastIndexOf('.')
  if (dot === -1) return 'plaintext'
  return _EXT_LANG[base.slice(dot + 1)] || 'plaintext'
}

// 500ms after the last keystroke before we PUT the file. Long enough that
// rapid typing produces one save, short enough that a quick tab-switch
// doesn't risk data loss.
const SAVE_DEBOUNCE_MS = 500

function TrashIcon() {
  return (
    <svg width="10" height="10" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 5h10M7 3h2" />
      <rect x="4" y="5" width="8" height="8" rx="1" />
      <path d="M6 8v3M10 8v3" />
    </svg>
  )
}

function FileIcon({ active }) {
  return (
    <svg width="10" height="10" viewBox="0 0 16 16" fill="none" stroke="currentColor"
      strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
      style={{ color: active ? 'var(--accent-hi)' : 'var(--text-faint)', flexShrink: 0 }}>
      <path d="M9 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6L9 2z" />
      <path d="M9 2v4h4" />
    </svg>
  )
}

function FolderIcon() {
  return (
    <svg width="10" height="10" viewBox="0 0 16 16" fill="none" stroke="currentColor"
      strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
      style={{ color: 'var(--text-faint)', flexShrink: 0 }}>
      <path d="M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 1.5h4.5A1.5 1.5 0 0 1 14 6v6.5A1.5 1.5 0 0 1 12.5 14h-9A1.5 1.5 0 0 1 2 12.5v-8z" />
    </svg>
  )
}

// Depth from a flat path like "frontend/src/App.jsx" → 2
function depthOf(path) {
  return path.split('/').length - 1
}

// Display name = last segment.
function baseName(path) {
  const i = path.lastIndexOf('/')
  return i === -1 ? path : path.slice(i + 1)
}

export default function CodePanel() {
  const {
    project, setProjectFile, removeProjectFile, addToTree, setActiveFile, markDirty,
    runtime, theme,
  } = useAppStore()

  const fileEntries = useMemo(
    () => project.tree, // already depth-first ordered by backend
    [project.tree],
  )

  const activeFile = project.activeFile
  const currentCode = activeFile ? (project.files[activeFile] ?? '') : ''

  const [isAddingFile, setIsAddingFile] = useState(false)
  const [newFileName, setNewFileName]   = useState('')
  const [contextMenu, setContextMenu]   = useState(null) // { x, y, path }

  const { copy } = useCopyToClipboard()
  const newFileInputRef = useRef(null)
  const saveTimers = useRef(new Map())

  useEffect(() => {
    if (isAddingFile) setTimeout(() => newFileInputRef.current?.focus(), 0)
  }, [isAddingFile])

  useEffect(() => {
    if (!contextMenu) return
    const close = () => setContextMenu(null)
    document.addEventListener('click', close)
    return () => document.removeEventListener('click', close)
  }, [contextMenu])

  // Flush any pending save timers when the panel unmounts (e.g. on mode
  // switch) so we don't leave them hanging.  We capture the ref's current
  // value at unmount time so the eslint hook-deps rule is satisfied.
  useEffect(() => {
    const timers = saveTimers.current
    return () => {
      for (const t of timers.values()) clearTimeout(t)
      timers.clear()
    }
  }, [])

  function scheduleSave(path, content) {
    if (!project.id) return
    const timers = saveTimers.current
    if (timers.has(path)) clearTimeout(timers.get(path))
    markDirty(path, true)
    const t = setTimeout(async () => {
      try {
        await apiWriteFile(project.id, path, content)
      } catch (e) {
        console.error('save', path, e)
      } finally {
        markDirty(path, false)
        timers.delete(path)
      }
    }, SAVE_DEBOUNCE_MS)
    timers.set(path, t)
  }

  function handleEditorChange(value) {
    if (!activeFile) return
    const content = value || ''
    setProjectFile(activeFile, content)
    scheduleSave(activeFile, content)
  }

  async function handleDelete(path) {
    setContextMenu(null)
    if (!project.id) return
    try {
      await apiDeleteFile(project.id, path)
      removeProjectFile(path)
    } catch (e) {
      console.error('delete', path, e)
    }
  }

  async function commitNewFile() {
    const trimmed = newFileName.trim()
    setIsAddingFile(false)
    setNewFileName('')
    if (!trimmed || !project.id) return

    // No leading slash — paths are relative to the project root.
    const path = trimmed.replace(/^\/+/, '')
    if (project.files[path] != null) {
      // Just open it.
      setActiveFile(path)
      return
    }
    try {
      await apiWriteFile(project.id, path, '')
      setProjectFile(path, '')
      addToTree({ path, is_dir: false, size: 0 })
      setActiveFile(path)
    } catch (e) {
      console.error('create', path, e)
    }
  }

  function handleContextMenu(e, path) {
    e.preventDefault()
    setContextMenu({ x: e.clientX, y: e.clientY, path })
  }

  const fileFiles  = fileEntries.filter((e) => !e.is_dir)
  const hasAnyFile = fileFiles.length > 0

  return (
    <div className="flex h-full" style={{ background: 'var(--bg)' }}>

      {/* ── File Explorer ────────────────────────────────────── */}
      <div
        className="flex flex-col shrink-0 border-r"
        style={{ width: 220, background: 'var(--surface)', borderColor: 'var(--border)' }}
      >
        <div
          className="flex items-center justify-between px-3 py-2 border-b shrink-0"
          style={{ borderColor: 'var(--border)' }}
        >
          <span
            className="text-[9.5px] font-semibold tracking-widest uppercase"
            style={{ color: 'var(--text-faint)' }}
          >
            Files
          </span>
          <button
            onClick={() => setIsAddingFile(true)}
            disabled={!project.id}
            className="w-5 h-5 rounded flex items-center justify-center disabled:opacity-30"
            style={{ color: 'var(--text-dim)' }}
            title="New file"
          >
            <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
              <path d="M8 3v10M3 8h10" />
            </svg>
          </button>
        </div>

        <div className="flex-1 overflow-y-auto py-1">
          {!project.id && (
            <p className="text-[11px] px-3 py-2 leading-relaxed" style={{ color: 'var(--text-faint)' }}>
              No project loaded.
            </p>
          )}

          {project.id && fileEntries.length === 0 && !isAddingFile && (
            <p className="text-[11px] px-3 py-2 leading-relaxed" style={{ color: 'var(--text-faint)' }}>
              Empty project.
              <br />Click + to add a file.
            </p>
          )}

          {fileEntries.map((entry) => {
            const { path, is_dir } = entry
            const isActive = !is_dir && activeFile === path
            const indent = depthOf(path) * 10 + 10

            if (is_dir) {
              return (
                <div
                  key={path}
                  className="flex items-center gap-1.5 px-2 py-1 select-none"
                  style={{ paddingLeft: indent }}
                >
                  <FolderIcon />
                  <span
                    className="flex-1 text-[11px] mono truncate"
                    style={{ color: 'var(--text-faint)' }}
                  >
                    {baseName(path)}
                  </span>
                </div>
              )
            }

            const isDirty = project.dirty[path]
            return (
              <div
                key={path}
                onClick={() => setActiveFile(path)}
                onContextMenu={(e) => handleContextMenu(e, path)}
                className="group relative flex items-center gap-1.5 py-1 cursor-pointer select-none"
                style={{
                  paddingLeft: indent,
                  paddingRight: 8,
                  background:  isActive ? 'var(--accent-bg)' : 'transparent',
                  borderLeft:  isActive ? '2px solid var(--accent)' : '2px solid transparent',
                }}
                title={path}
              >
                <FileIcon active={isActive} />
                <span
                  className="flex-1 text-[11px] mono truncate"
                  style={{ color: isActive ? 'var(--text)' : 'var(--text-dim)' }}
                >
                  {baseName(path)}
                </span>
                {isDirty && (
                  <span
                    className="w-1.5 h-1.5 rounded-full shrink-0"
                    style={{ background: 'var(--accent)' }}
                    title="Unsaved"
                  />
                )}
                <button
                  onClick={(e) => { e.stopPropagation(); handleDelete(path) }}
                  className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded flex items-center justify-center shrink-0 transition-opacity"
                  style={{ color: 'var(--text-faint)' }}
                  title="Delete file"
                >
                  <TrashIcon />
                </button>
              </div>
            )
          })}

          {isAddingFile && (
            <div className="px-2.5 py-1.5">
              <input
                ref={newFileInputRef}
                value={newFileName}
                onChange={(e) => setNewFileName(e.target.value)}
                onBlur={commitNewFile}
                onKeyDown={(e) => {
                  if (e.key === 'Enter')  commitNewFile()
                  if (e.key === 'Escape') { setIsAddingFile(false); setNewFileName('') }
                }}
                placeholder="path/to/file.jsx"
                className="w-full text-[11px] mono outline-none px-1.5 py-0.5 rounded"
                style={{
                  background: 'var(--surface-2)',
                  border:     '1px solid var(--accent)',
                  color:      'var(--text)',
                }}
              />
            </div>
          )}
        </div>
      </div>

      {/* ── Editor area ──────────────────────────────────────── */}
      <div className="flex-1 flex flex-col min-w-0">
        <div
          className="flex items-center gap-2 px-3 py-1.5 border-b shrink-0"
          style={{ background: 'var(--surface)', borderColor: 'var(--border)' }}
        >
          <span className="text-[12px] mono flex-1 truncate" style={{ color: 'var(--text-dim)' }}>
            {activeFile || '(no file)'}
          </span>
          <div className="flex items-center gap-1.5 shrink-0">
            {activeFile && (
              <button
                onClick={() => copy(currentCode, 'File copied')}
                className="h-7 px-2.5 rounded text-[11.5px] flex items-center gap-1 transition-colors"
                style={{ color: 'var(--text-dim)', border: '1px solid var(--border-2)' }}
                title="Copy file"
              >
                <svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                  <rect x="4" y="4" width="9" height="9" rx="1.5" />
                  <path d="M3 11V3.5A1.5 1.5 0 0 1 4.5 2H11" />
                </svg>
                Copy
              </button>
            )}
            {/* Server status badge — changes reflect via Vite HMR automatically */}
            <div
              className="flex items-center gap-1.5 px-2 h-7 rounded text-[10.5px] mono"
              style={{ border: '1px solid var(--border-2)', color: 'var(--text-faint)' }}
              title={runtime.status === 'running' ? `Dev server on :${runtime.frontendPort}` : 'Click Run in Preview to start dev servers'}
            >
              <span style={{
                width: 5, height: 5, borderRadius: '50%', flexShrink: 0,
                background: runtime.status === 'running' ? '#22c55e' : 'var(--text-faint)',
              }} />
              {runtime.status === 'running' ? `HMR :${runtime.frontendPort}` : 'offline'}
            </div>
          </div>
        </div>

        <div className="flex-1 min-h-0">
          {!activeFile || !hasAnyFile ? (
            <div className="h-full flex flex-col items-center justify-center gap-2">
              <svg width="32" height="32" viewBox="0 0 16 16" fill="none" stroke="currentColor"
                strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
                style={{ color: 'var(--text-faint)' }}>
                <path d="M9 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6L9 2z" />
                <path d="M9 2v4h4" />
              </svg>
              <p className="text-[12.5px] text-center" style={{ color: 'var(--text-faint)' }}>
                {project.id
                  ? 'Click a file in the tree to start editing.'
                  : 'Open or create a session to begin.'}
              </p>
            </div>
          ) : (
            <Editor
              height="100%"
              path={activeFile}
              language={LANG(activeFile)}
              value={currentCode}
              onChange={handleEditorChange}
              theme={theme === 'light' ? 'vs' : 'vs-dark'}
              options={{
                fontSize: 13,
                fontFamily: "'JetBrains Mono', ui-monospace, monospace",
                fontLigatures: false,
                minimap: { enabled: false },
                scrollBeyondLastLine: false,
                wordWrap: 'on',
                lineNumbers: 'on',
                padding: { top: 12 },
                renderLineHighlight: 'gutter',
                smoothScrolling: true,
              }}
            />
          )}
        </div>
      </div>

      {/* ── Right-click context menu ─────────────────────────── */}
      {contextMenu && (
        <div
          onClick={(e) => e.stopPropagation()}
          style={{
            position:  'fixed',
            top:       contextMenu.y,
            left:      contextMenu.x,
            zIndex:    1000,
            background: 'var(--surface-3)',
            border:    '1px solid var(--border-2)',
            borderRadius: 8,
            padding:   4,
            minWidth:  140,
            boxShadow: '0 8px 24px rgba(0,0,0,0.5)',
          }}
        >
          <button
            onClick={() => handleDelete(contextMenu.path)}
            className="w-full flex items-center gap-2 px-3 py-1.5 rounded text-[12px] text-left"
            style={{ color: 'var(--danger)' }}
          >
            <TrashIcon />
            Delete
          </button>
        </div>
      )}
    </div>
  )
}
