import { useEffect, useState, useCallback, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useProjectStore } from '../../store/projectStore';
import { useEditorStore } from '../../store/editorStore';
import { useCollaborationStore } from '../../store/collaborationStore';
import { useProjectSocket, getSocket } from '../../hooks/useWebSocket';
import {
  Blocks, Eye, Code2, FileType, Palette, Play,
  Save, ArrowLeft, Grid3X3, Maximize2, Minimize2,
  PanelLeft, PanelRight, Users
} from 'lucide-react';
import FileTree from './FileTree';
import Toolbar from './Toolbar';
import BlockEditor from '../BlockEditor/BlockEditor';
import VisualEditor from '../VisualEditor/VisualEditor';
import CssRulePanel from '../VisualEditor/CssRulePanel';
import CodeView from '../CodeView/CodeView';
import ScriptPreview from './ScriptPreview';
import ShareModal from '../Collaboration/ShareModal';
import CollaboratorAvatars from '../Collaboration/CollaboratorAvatars';
import { ProjectFile } from '../../types/blocks';
import { generatePreviewHtml } from '../../compilers/web';

type FileCategory = 'markup' | 'script' | 'other';

function getFileCategory(fileType: string): FileCategory {
  if (['html', 'css'].includes(fileType)) return 'markup';
  if (['js', 'ts', 'typescript', 'jsx', 'tsx', 'csharp', 'xaml'].includes(fileType)) return 'script';
  return 'other';
}

function findFileById(files: ProjectFile[], id: string): ProjectFile | null {
  for (const f of files) {
    if (f.id === id) return f;
    if (f.children) {
      const found = findFileById(f.children, id);
      if (found) return found;
    }
  }
  return null;
}

// Migrate file contents that contain legacy Blockly XML instead of generated code
function migrateBlocklyXmlContent(files: ProjectFile[]): ProjectFile[] {
  return files.map((f) => {
    if (f.content && /^<xml\s|xmlns="http:\/\/www\.w3\.org\/1999\/xhtml"/.test(f.content)) {
      return { ...f, content: '' };
    }
    if (f.children) {
      return { ...f, children: migrateBlocklyXmlContent(f.children) };
    }
    return f;
  });
}

export default function ProjectEditor() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const { currentProject, loadProject, updateProject, loading, error } = useProjectStore();
  const {
    editorMode, setEditorMode, viewMode, setViewMode,
    activeFileId, setActiveFile, fileTree, setFileTree,
    visualElements, setVisualElements, setElementRegistry,
    elementRegistry, blocksXml, blockCode, cssRules,
    snapToGrid, toggleSnapToGrid,
    showMiniMap, toggleMiniMap, setBlocksXml, setCssRules
  } = useEditorStore();
  const [showSidebar, setShowSidebar] = useState(true);
  const [saving, setSaving] = useState(false);
  const [showShareModal, setShowShareModal] = useState(false);
  const [showPreview, setShowPreview] = useState(false);

  const { emitFileChanged, emitActiveFileChanged, emitSave, emitCursorMove } = useProjectSocket(id);

  const connected = useCollaborationStore((s) => s.connected);

  useEffect(() => {
    if (id) loadProject(id);
    if (id) (window as any).__projectId = id;
    return () => { (window as any).__projectId = undefined; };
  }, [id, loadProject]);

  useEffect(() => {
    if (currentProject?.data) {
      const migratedFiles = migrateBlocklyXmlContent((currentProject.data.files || []));
      setFileTree(migratedFiles);

      // Load element registry from persisted project data
      const visualData = currentProject.data.visual || {};
      setElementRegistry(visualData);

      // Set visual elements for the active file
      setVisualElements(
        visualData[activeFileId || ''] || []
      );

      // Restore per-file blocksXml metadata
      if (currentProject.data.blocksXml) {
        const xmlMap = currentProject.data.blocksXml;
        // Handle legacy string format: old blocksXml was a global string, not per-file
        if (typeof xmlMap === 'string') {
          // Discard old global blocksXml; blocks start fresh per-file
        } else {
          Object.entries(xmlMap).forEach(([fid, xml]) => {
            // Don't overwrite if store already has a non-empty value for this file.
            // The in-memory state from initialBlocksXml (sent on join_project) is
            // more recent than the SQLite snapshot, since live block_state_sync
            // happens before auto-save persists to the database.
            const current = useEditorStore.getState().blocksXml[fid];
            if (!current || current === '') {
              setBlocksXml(fid, xml);
            }
          });
        }
      }
      // Restore per-file block code cache
      const { setBlockCode } = useEditorStore.getState();
      if (currentProject.data.blockCode) {
        Object.entries(currentProject.data.blockCode).forEach(([fid, code]) => {
          setBlockCode(fid, code);
        });
      }
      // Restore per-file CSS rules
      if (currentProject.data.cssRules) {
        Object.entries(currentProject.data.cssRules).forEach(([fid, rules]) => {
          setCssRules(fid, rules as any[]);
        });
      }
      if (!activeFileId && currentProject.data.files?.length > 0) {
        const firstFile = currentProject.data.files[0];
        if (firstFile) {
          setActiveFile(firstFile.id);
          const cat = getFileCategory(firstFile.type);
          if (firstFile.type === 'css') setEditorMode('css');
          else if (cat === 'markup') setEditorMode('visual');
          else if (cat === 'script') setEditorMode('blocks');
        }
      }
    }
  }, [currentProject, setFileTree, setVisualElements, setActiveFile, activeFileId, setEditorMode, setBlocksXml, setElementRegistry, setCssRules]);

  // When activeFileId changes, load visual elements from the registry
  useEffect(() => {
    if (activeFileId) {
      const registry = useEditorStore.getState().elementRegistry;
      setVisualElements(registry[activeFileId] || []);
    }
  }, [activeFileId, setVisualElements]);

  const activeFile = useMemo(
    () => (activeFileId ? findFileById(fileTree, activeFileId) : null),
    [activeFileId, fileTree]
  );

  const fileCategory = activeFile ? getFileCategory(activeFile.type) : 'other';

  // Check if the active file is script linked under an HTML file (for preview)
  const isScriptUnderHtml = useMemo(() => {
    if (!activeFile || fileCategory !== 'script') return false;
    // Check if any HTML file has this script as a linked file
    const htmlFiles = fileTree.filter(f => f.type === 'html');
    return htmlFiles.some(html => (html.linkedFiles || []).includes(activeFile.id));
  }, [activeFile, fileCategory, fileTree]);

  const availableModes = useMemo(() => {
    if (fileCategory === 'markup') {
      if (activeFile?.type === 'css') {
        return [
          { id: 'css' as const, label: 'Styles', icon: Palette },
          { id: 'code' as const, label: 'Code', icon: Code2 },
          { id: 'files' as const, label: 'Files', icon: FileType },
        ];
      }
      return [
        { id: 'visual' as const, label: 'Visual', icon: Eye },
        { id: 'code' as const, label: 'Code', icon: Code2 },
        { id: 'files' as const, label: 'Files', icon: FileType },
      ];
    }
    if (fileCategory === 'script') {
      const modes: { id: string; label: string; icon: any }[] = [
        { id: 'blocks', label: 'Blocks', icon: Blocks },
        { id: 'code', label: 'Code', icon: Code2 },
      ];
      modes.push({ id: 'files', label: 'Files', icon: FileType });
      return modes;
    }
    return [
      { id: 'code' as const, label: 'Code', icon: Code2 },
      { id: 'files' as const, label: 'Files', icon: FileType },
    ];
  }, [fileCategory, isScriptUnderHtml, activeFile]);

  useEffect(() => {
    if (!activeFileId) return;
    const validIds = availableModes.map(m => m.id);
    if (!validIds.includes(editorMode)) {
      setEditorMode(validIds[0] as any);
    }
    setViewMode('design');
  }, [activeFileId, availableModes]);

  // Compute preview content for ScriptPreview
  const previewContent = useMemo(() => {
    if (!activeFile || fileCategory !== 'script' || !isScriptUnderHtml) return null;
    // Find the parent HTML file that links this script
    const htmlFile = fileTree.find(f => f.type === 'html' && (f.linkedFiles || []).includes(activeFile.id));
    if (!htmlFile) return null;

    // Build htmlContent: prefer file content, otherwise generate from visual elements
    let htmlContent: string;
    if (htmlFile.content) {
      htmlContent = htmlFile.content;
    } else {
      const elements = elementRegistry[htmlFile.id] || [];
      if (elements.length > 0) {
        // Inline linked CSS content so it works inside the sandboxed iframe
        const linkedIds = htmlFile.linkedFiles || [];
        const linkedCssContent = linkedIds
          .map(id => findFileById(fileTree, id))
          .filter((f): f is ProjectFile => f !== null && f.type === 'css')
          .map(f => ({ content: f.content || '' }));
        htmlContent = generatePreviewHtml({
          visualElements: elements,
          projectName: currentProject?.name || 'My Web App',
          linkedCssContent,
        });
      } else {
        htmlContent = '';
      }
    }

    return {
      htmlContent,
      scriptContent: activeFile.content || '',
      fileName: activeFile.name,
    };
  }, [activeFile, fileCategory, isScriptUnderHtml, fileTree, elementRegistry, currentProject]);

  // Emit active file changes to collaborators
  useEffect(() => {
    if (id && activeFileId) {
      emitActiveFileChanged(activeFileId);
    }
  }, [id, activeFileId, emitActiveFileChanged]);

  // Set up global block selection WS emit
  useEffect(() => {
    (window as any).__wsBlockSelection = (blockId: string | null) => {
      const projectId = (window as any).__projectId;
      const socket = (window as any).__wsSocket || getSocket();
      if (socket?.connected && projectId) {
        socket.emit('block_selection_changed', { projectId, blockId });
      }
    };
    return () => { (window as any).__wsBlockSelection = undefined; };
  }, []);

  // Keep global socket reference
  useEffect(() => {
    const socket = getSocket();
    (window as any).__wsSocket = socket;
  });

  const handleSave = useCallback(async () => {
    if (!currentProject || !id) return;
    setSaving(true);
    try {
      const { blocksXml: currentBlocksXml, blockCode: currentBlockCode, elementRegistry: currentRegistry, cssRules: currentCssRules } = useEditorStore.getState();

      // Sync current visual elements into registry before saving
      const fullRegistry = { ...currentRegistry };
      if (activeFileId) {
        fullRegistry[activeFileId] = visualElements;
      }

      const data = {
        ...currentProject.data,
        files: fileTree,
        visual: fullRegistry,
        blocksXml: currentBlocksXml,
        blockCode: currentBlockCode,
        cssRules: currentCssRules,
      };

      // Try WebSocket save first, fall back to REST
      if (connected) {
        await emitSave(data);
      } else {
        await updateProject(id, { data });
      }
    } finally {
      setSaving(false);
    }
  }, [currentProject, id, fileTree, visualElements, activeFileId, updateProject, connected, emitSave]);

  // Auto-save via WebSocket when data changes (no more 30s polling)
  useEffect(() => {
    if (!currentProject || !id || !connected) return;
    const timer = setTimeout(() => handleSave(), 2000);
    return () => clearTimeout(timer);
  }, [currentProject, id, fileTree, visualElements, elementRegistry, activeFileId, blocksXml, blockCode, cssRules, connected, handleSave]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 's') {
        e.preventDefault();
        handleSave();
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [handleSave]);

  if (loading && !currentProject) {
    return (
      <div className="min-h-screen bg-surface-950 flex items-center justify-center">
        <div className="flex flex-col items-center gap-4">
          <div className="animate-spin rounded-full h-10 w-10 border-2 border-primary-500 border-t-transparent" />
          <p className="text-surface-400 text-sm">Loading project...</p>
        </div>
      </div>
    );
  }

  if (error && !currentProject) {
    return (
      <div className="min-h-screen bg-surface-950 flex items-center justify-center">
        <div className="text-center max-w-md mx-auto p-8">
          <div className="text-red-400 text-lg font-semibold mb-2">Failed to load project</div>
          <p className="text-surface-400 text-sm mb-6">{error}</p>
          <button onClick={() => navigate('/dashboard')} className="btn-primary">
            Back to Dashboard
          </button>
        </div>
      </div>
    );
  }

  if (!currentProject) {
    return (
      <div className="min-h-screen bg-surface-950 flex items-center justify-center">
        <p className="text-surface-400 text-sm">No project data</p>
      </div>
    );
  }

  const framework = currentProject.framework;
  const displayMode = !activeFileId ? 'files' : editorMode;

  return (
    <div className="h-screen bg-surface-950 flex flex-col">
      <Toolbar
        projectName={currentProject.name}
        framework={currentProject.framework}
        onBack={() => navigate('/dashboard')}
        onSave={handleSave}
        saving={saving}
        leftContent={
          <div className="flex items-center gap-1 bg-surface-800 rounded-lg p-0.5">
            {availableModes.map((mode) => {
              const Icon = mode.icon;
              return (
                <button
                  key={mode.id}
                  onClick={() => setEditorMode(mode.id as any)}
                  className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
                    displayMode === mode.id
                      ? 'bg-primary-600 text-white'
                      : 'text-surface-400 hover:text-white'
                  }`}
                >
                  <Icon className="w-3.5 h-3.5" />
                  {mode.label}
                </button>
              );
            })}
          </div>
        }
        rightContent={
          <div className="flex items-center gap-2">
            <CollaboratorAvatars />
            {displayMode === 'visual' && (
              <>
                <button
                  onClick={toggleSnapToGrid}
                  className={`btn-ghost p-1.5 ${snapToGrid ? 'text-primary-400' : 'text-surface-400'}`}
                  title="Snap to Grid"
                >
                  <Grid3X3 className="w-4 h-4" />
                </button>
                <button
                  onClick={toggleMiniMap}
                  className={`btn-ghost p-1.5 ${showMiniMap ? 'text-primary-400' : 'text-surface-400'}`}
                  title="Toggle Minimap"
                >
                  {showMiniMap ? <Maximize2 className="w-4 h-4" /> : <Minimize2 className="w-4 h-4" />}
                </button>
              </>
            )}
            {isScriptUnderHtml && (
              <button
                onClick={() => setShowPreview(!showPreview)}
                className={`flex items-center gap-1 px-2 py-1.5 rounded-md text-xs font-medium transition-colors ${
                  showPreview ? 'bg-green-600 text-white' : 'text-surface-400 hover:text-white hover:bg-surface-800'
                }`}
                title="Run script preview"
              >
                <Play className="w-3.5 h-3.5" />
                {showPreview ? 'Close Preview' : 'Run'}
              </button>
            )}
            <button
              onClick={() => setShowShareModal(true)}
              className={`btn-ghost p-1.5 ${connected ? 'text-green-400' : 'text-surface-400'}`}
              title={connected ? 'Collaborators online' : 'Share project'}
            >
              <Users className="w-4 h-4" />
            </button>
            <button
              onClick={() => setShowSidebar(!showSidebar)}
              className="btn-ghost p-1.5"
              title="Toggle Sidebar"
            >
              {showSidebar ? <PanelLeft className="w-4 h-4" /> : <PanelRight className="w-4 h-4" />}
            </button>
            <button
              onClick={handleSave}
              disabled={saving}
              className="btn-primary text-xs py-1.5"
            >
              <Save className="w-3.5 h-3.5" />
              {saving ? 'Saving...' : 'Save'}
            </button>
          </div>
        }
      />

      <div className="flex-1 flex overflow-hidden">
        {showSidebar && (
          <div className="w-56 panel flex-shrink-0 overflow-y-auto">
            <FileTree
              files={fileTree}
              activeFileId={activeFileId}
              onFileSelect={setActiveFile}
              framework={framework}
            />
          </div>
        )}

        <div className="flex-1 flex overflow-hidden">
          {showPreview && previewContent && (
            <div className="w-96 flex-shrink-0 border-r border-surface-700">
              <ScriptPreview
                htmlContent={previewContent.htmlContent}
                scriptContent={previewContent.scriptContent}
                fileName={previewContent.fileName}
                onClose={() => setShowPreview(false)}
              />
            </div>
          )}
          <div className="flex-1 overflow-hidden">
            {displayMode === 'blocks' && <BlockEditor />}
            {displayMode === 'visual' && <VisualEditor />}
            {displayMode === 'css' && <CssRulePanel />}
            {displayMode === 'code' && <CodeView />}
            {displayMode === 'files' && (
            <div className="p-6">
              <FileTree
                files={fileTree}
                activeFileId={activeFileId}
                onFileSelect={setActiveFile}
                framework={framework}
                expanded
              />
            </div>
          )}
          </div>
        </div>
      </div>

      {id && (
        <ShareModal
          projectId={id}
          isOpen={showShareModal}
          onClose={() => setShowShareModal(false)}
        />
      )}
    </div>
  );
}
