/**
 * EditorChatPanel
 *
 * AI Chat sidebar for the GraphicalEditor.
 * Streams responses via SSE (same pattern as Workspace chat).
 * File & data-source attachment UX mirrors WorkspaceInput:
 *   - Files: drag & drop from FilesTab (UDB) onto input area, or click in UDB
 *   - Data Sources: 🔗 picker button next to input (toggle-select from active sources)
 */
import React, { useState, useCallback, useEffect, useRef } from 'react';
import { startSseStream } from '../../../utils/sseClient';
import { ChatMessageList } from '../../Chat';
import type { ChatMessage } from '../../Chat';
import { getPageIcon } from '../../../config/pageRegistry';
import api from '../../../api';

interface PersistedEditorChatMessage {
  id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  timestamp?: number;
  sequenceNr?: number;
}

interface PersistedEditorChatResponse {
  chatWorkflowId: string | null;
  messages: PersistedEditorChatMessage[];
}

import { useLanguage } from '../../../providers/language/LanguageContext';

export interface PendingFile {
  fileId: string;
  fileName: string;
  itemType?: 'file' | 'group';
}

export interface EditorDataSource {
  id: string;
  label: string;
  path?: string;
  sourceType?: string;
}

export interface EditorFeatureDataSource {
  id: string;
  featureInstanceId: string;
  featureCode: string;
  tableName: string;
  label: string;
}

interface EditorChatPanelProps {
  instanceId: string;
  workflowId: string | null;
  onGraphUpdated?: () => void;
  pendingFiles?: PendingFile[];
  onRemovePendingFile?: (fileId: string) => void;
  dataSources?: EditorDataSource[];
  featureDataSources?: EditorFeatureDataSource[];
}

let _msgCounter = 0;

export const EditorChatPanel: React.FC<EditorChatPanelProps> = ({ instanceId,
  workflowId,
  onGraphUpdated,
  pendingFiles = [],
  onRemovePendingFile,
  dataSources = [],
  featureDataSources = [],
}) => {
  const { t } = useLanguage();
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [loading, setLoading] = useState(false);
  const [prompt, setPrompt] = useState('');
  const [attachedDataSourceIds, setAttachedDataSourceIds] = useState<string[]>([]);
  const [attachedFeatureDataSourceIds, setAttachedFeatureDataSourceIds] = useState<string[]>([]);
  const [showSourcePicker, setShowSourcePicker] = useState(false);
  const [treeDropOver, setTreeDropOver] = useState(false);
  const [stopping, setStopping] = useState(false);
  const abortRef = useRef<(() => void) | null>(null);
  const assistantIdRef = useRef<string | null>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const pickerRef = useRef<HTMLDivElement>(null);

  // Load persisted chat history from the backend whenever the workflow changes.
  // The chat is stored in `ChatWorkflow.linkedWorkflowId == workflowId` and is
  // returned by `GET /api/workflows/{instanceId}/{workflowId}/chat/messages`.
  // For an unsaved workflow (workflowId == null) we just clear the panel.
  useEffect(() => {
    if (!workflowId) {
      setMessages([]);
      return;
    }
    let cancelled = false;
    const _loadHistory = async () => {
      setHistoryLoading(true);
      try {
        const res = await api.get<PersistedEditorChatResponse>(
          `/api/workflows/${instanceId}/${workflowId}/chat/messages`,
        );
        if (cancelled) return;
        const persisted = (res.data?.messages || []).map((m): ChatMessage => ({
          id: m.id || `persisted-${++_msgCounter}`,
          role: m.role,
          content: m.content,
          timestamp: m.timestamp ? Math.round(Number(m.timestamp) * 1000) : Date.now(),
        }));
        setMessages(persisted);
      } catch (err) {
        if (cancelled) return;
        console.warn('EditorChatPanel: failed to load chat history', err);
        setMessages([]);
      } finally {
        if (!cancelled) setHistoryLoading(false);
      }
    };
    _loadHistory();
    return () => { cancelled = true; };
  }, [instanceId, workflowId]);

  const _toggleDataSource = useCallback((dsId: string) => {
    setAttachedDataSourceIds(prev =>
      prev.includes(dsId) ? prev.filter(id => id !== dsId) : [...prev, dsId],
    );
  }, []);

  const _toggleFeatureDataSource = useCallback((fdsId: string) => {
    setAttachedFeatureDataSourceIds(prev =>
      prev.includes(fdsId) ? prev.filter(id => id !== fdsId) : [...prev, fdsId],
    );
  }, []);

  const _handleSend = useCallback(() => {
    const trimmed = prompt.trim();
    if (!workflowId || loading || !trimmed) return;

    const fileIds = pendingFiles.map(f => f.fileId);
    // Note: conversationHistory is no longer sent — the backend loads it
    // server-side from the persisted ChatWorkflow (linkedWorkflowId).
    const body: Record<string, unknown> = {
      message: trimmed,
      userLanguage: navigator.language?.slice(0, 2) || 'de',
    };
    if (fileIds.length > 0) body.fileIds = fileIds;
    if (attachedDataSourceIds.length > 0) body.dataSourceIds = attachedDataSourceIds;
    if (attachedFeatureDataSourceIds.length > 0) body.featureDataSourceIds = attachedFeatureDataSourceIds;

    const userMsg: ChatMessage = {
      id: `user-${++_msgCounter}`,
      role: 'user',
      content: trimmed,
      timestamp: Date.now(),
    };
    setMessages(prev => [...prev, userMsg]);
    setPrompt('');
    setShowSourcePicker(false);
    setLoading(true);

    const assistantId = `asst-${++_msgCounter}`;
    assistantIdRef.current = assistantId;
    let accumulated = '';
    setMessages(prev => [...prev, { id: assistantId, role: 'assistant', content: '', timestamp: Date.now() }]);

    const baseURL = api.defaults.baseURL || '';
    const cleanup = startSseStream({
      url: `${baseURL}/api/workflows/${instanceId}/${workflowId}/chat/stream`,
      body,
      handlers: {
        onChunk: (event) => {
          if (event.content) {
            accumulated += event.content;
            setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content: accumulated } : m));
          }
        },
        onRawEvent: (event) => {
          if (event.type === 'message' && event.content) {
            accumulated += event.content;
            setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content: accumulated } : m));
          }
          if (event.type === 'toolResult' || event.type === 'toolCall') {
            onGraphUpdated?.();
          }
        },
        onComplete: () => {
          if (!accumulated) {
            setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content: t('Fertig.') } : m));
          }
          onGraphUpdated?.();
          setLoading(false);
        },
        onError: (event) => {
          const errText = event.content || t('Anfrage fehlgeschlagen');
          if (!accumulated) {
            setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content: `${t('Fehler')}: ${errText}` } : m));
          }
          setLoading(false);
        },
        onStopped: () => {
          setMessages(prev => prev.map(m => m.id === assistantId
            ? { ...m, content: (m.content ? m.content + '\n\n' : '') + `_${t('Gestoppt.')}_` }
            : m));
          setLoading(false);
          setStopping(false);
        },
      },
      onConnectionError: (err) => {
        setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content: `${t('Fehler')}: ${err.message}` } : m));
        setLoading(false);
        setStopping(false);
      },
      onStreamEnd: () => { setLoading(false); setStopping(false); },
    });

    abortRef.current = cleanup;
  }, [prompt, loading, workflowId, instanceId, onGraphUpdated, pendingFiles, attachedDataSourceIds, attachedFeatureDataSourceIds, t]);

  const _handleStop = useCallback(async () => {
    if (!workflowId || stopping) return;
    setStopping(true);
    const assistantId = assistantIdRef.current;
    if (assistantId) {
      setMessages(prev => prev.map(m => m.id === assistantId
        ? { ...m, content: (m.content ? m.content + '\n\n' : '') + `_${t('Stoppen…')}_` }
        : m));
    }
    try {
      await api.post(`/api/workflows/${instanceId}/${workflowId}/chat/stop`);
    } catch {
    }
    abortRef.current?.();
  }, [workflowId, instanceId, stopping, t]);

  const _handleKeyDown = useCallback((e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      _handleSend();
    }
  }, [_handleSend]);

  const _handleDragOver = useCallback((e: React.DragEvent) => {
    if (
      e.dataTransfer.types.includes('application/tree-items') ||
      e.dataTransfer.types.includes('application/group-id') ||
      e.dataTransfer.types.includes('application/file-id') ||
      e.dataTransfer.types.includes('application/file-ids')
    ) {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'copy';
      setTreeDropOver(true);
    }
  }, []);

  const _handleDragLeave = useCallback(() => setTreeDropOver(false), []);

  const _handleDrop = useCallback((e: React.DragEvent) => {
    setTreeDropOver(false);
    const groupId = e.dataTransfer.getData('application/group-id');
    if (groupId) {
      e.preventDefault();
      e.stopPropagation();
      return;
    }
    const treeItemsJson = e.dataTransfer.getData('application/tree-items');
    if (treeItemsJson) {
      e.preventDefault();
      e.stopPropagation();
    }
  }, []);

  const hasAttachments = pendingFiles.length > 0 || attachedDataSourceIds.length > 0 || attachedFeatureDataSourceIds.length > 0;
  const sourceCount = attachedDataSourceIds.length + attachedFeatureDataSourceIds.length;
  const hasSourceOptions = dataSources.length > 0 || featureDataSources.length > 0;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg-secondary, #fafafa)' }}>
      <ChatMessageList
        messages={messages}
        isProcessing={loading || historyLoading}
        emptyMessage={historyLoading ? t('Lade Verlauf…') : t('Beschreiben Sie, was Sie tun möchten')}
      />

      {/* Pending files (from UDB drag/click) */}
      {pendingFiles.length > 0 && (
        <div style={{
          padding: '6px 12px', display: 'flex', gap: 4, flexWrap: 'wrap',
          borderTop: '1px solid var(--border-color, #e0e0e0)',
          background: 'var(--bg-secondary, #fafafa)',
        }}>
          {pendingFiles.map(pf => (
            <span key={pf.fileId} style={{
              display: 'inline-flex', alignItems: 'center', gap: 4,
              padding: '2px 8px', borderRadius: 12, fontSize: 11,
              background: pf.itemType === 'group' ? '#e3f2fd' : '#fff3e0',
              color: pf.itemType === 'group' ? '#1565c0' : '#e65100',
              fontWeight: 500, border: `1px solid ${pf.itemType === 'group' ? '#bbdefb' : '#ffe0b2'}`,
            }}>
              {pf.itemType === 'group' ? '\uD83D\uDCC2' : '\uD83D\uDCCE'} {pf.fileName.length > 20 ? pf.fileName.slice(0, 20) + '...' : pf.fileName}
              {onRemovePendingFile && (
                <button onClick={() => onRemovePendingFile(pf.fileId)} style={{
                  border: 'none', background: 'none', cursor: 'pointer', fontSize: 11, color: '#e65100', padding: 0, lineHeight: 1,
                }}>x</button>
              )}
            </span>
          ))}
        </div>
      )}

      {/* Attached data sources chips */}
      {(attachedDataSourceIds.length > 0 || attachedFeatureDataSourceIds.length > 0) && (
        <div style={{
          padding: '6px 12px', display: 'flex', gap: 4, flexWrap: 'wrap',
          borderTop: pendingFiles.length > 0 ? 'none' : '1px solid var(--border-color, #e0e0e0)',
          background: '#fafafa',
        }}>
          {attachedDataSourceIds.map(dsId => {
            const ds = dataSources.find(d => d.id === dsId);
            return (
              <span key={dsId} style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                padding: '2px 8px', borderRadius: 12, fontSize: 11,
                background: '#e8f5e9', color: '#2e7d32', fontWeight: 500,
              }}>
                \uD83D\uDD17 {ds?.label || dsId}
                <button onClick={() => _toggleDataSource(dsId)} style={{
                  border: 'none', background: 'none', cursor: 'pointer', fontSize: 11, color: '#2e7d32', padding: 0, lineHeight: 1,
                }}>x</button>
              </span>
            );
          })}
          {attachedFeatureDataSourceIds.map(fdsId => {
            const fds = featureDataSources.find(d => d.id === fdsId);
            const fdsIcon = fds ? getPageIcon(`feature.${fds.featureCode}`) : null;
            return (
              <span key={fdsId} style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                padding: '2px 8px', borderRadius: 12, fontSize: 11,
                background: '#f3e5f5', color: '#7b1fa2', fontWeight: 500,
              }}>
                <span style={{ display: 'flex', alignItems: 'center', fontSize: 11 }}>{fdsIcon || '\uD83D\uDDC3\uFE0F'}</span>
                {fds?.label || fdsId}
                <button onClick={() => _toggleFeatureDataSource(fdsId)} style={{
                  border: 'none', background: 'none', cursor: 'pointer', fontSize: 11, color: '#7b1fa2', padding: 0, lineHeight: 1,
                }}>x</button>
              </span>
            );
          })}
        </div>
      )}

      {/* Input area */}
      <div
        style={{
          borderTop: hasAttachments ? 'none' : '1px solid var(--border-color, #e0e0e0)',
          padding: '8px 12px',
          display: 'flex', gap: 6, alignItems: 'flex-end',
          outline: treeDropOver ? '2px dashed var(--primary-color, #F25843)' : 'none',
          background: treeDropOver ? 'rgba(242, 88, 67, 0.08)' : undefined,
          transition: 'background 0.15s, outline 0.15s',
        }}
        onDragOver={_handleDragOver}
        onDragLeave={_handleDragLeave}
        onDrop={_handleDrop}
      >
        <textarea
          ref={textareaRef}
          value={prompt}
          onChange={e => setPrompt(e.target.value)}
          onKeyDown={_handleKeyDown}
          placeholder={workflowId ? t('Beschreiben Sie eine Änderung') : t('Speichern Sie zuerst den Workflow')}
          disabled={!workflowId || loading}
          style={{
            flex: 1, minHeight: 36, maxHeight: 100, resize: 'vertical',
            padding: '8px 10px', borderRadius: 8,
            border: '1px solid var(--border-color, #ccc)',
            fontSize: 13, fontFamily: 'inherit', outline: 'none',
          }}
          rows={1}
        />

        {/* Source picker button */}
        {hasSourceOptions && (
          <div style={{ position: 'relative' }} ref={pickerRef}>
            <button
              onClick={() => setShowSourcePicker(prev => !prev)}
              disabled={loading || !workflowId}
              title={t('Datenquellen anhängen')}
              style={{
                width: 36, height: 36, borderRadius: 8,
                border: '1px solid var(--border-color, #ddd)',
                background: sourceCount > 0 ? '#e8f5e9' : 'var(--secondary-bg, #f5f5f5)',
                color: sourceCount > 0 ? '#2e7d32' : '#666',
                cursor: loading || !workflowId ? 'not-allowed' : 'pointer',
                fontSize: 14, display: 'flex', alignItems: 'center', justifyContent: 'center',
                opacity: loading ? 0.5 : 1, position: 'relative',
              }}
            >
              {'\uD83D\uDD17'}
              {sourceCount > 0 && (
                <span style={{
                  position: 'absolute', top: -4, right: -4,
                  background: '#2e7d32', color: '#fff', fontSize: 9, fontWeight: 700,
                  borderRadius: '50%', width: 16, height: 16,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>{sourceCount}</span>
              )}
            </button>
            {showSourcePicker && (
              <div style={{
                position: 'absolute', bottom: '100%', right: 0, marginBottom: 4,
                background: '#fff', border: '1px solid var(--border-color, #e0e0e0)',
                borderRadius: 8, boxShadow: '0 -2px 8px rgba(0,0,0,0.1)', zIndex: 20,
                minWidth: 220, maxHeight: 260, overflowY: 'auto',
              }}>
                <div style={{ padding: '8px 12px', fontSize: 11, color: '#999', fontWeight: 600, borderBottom: '1px solid #f0f0f0' }}>
                  {t('Aktive Quellen auswählen')}
                </div>
                {dataSources.map(ds => {
                  const isSelected = attachedDataSourceIds.includes(ds.id);
                  return (
                    <div key={ds.id} onClick={() => _toggleDataSource(ds.id)} style={{
                      padding: '8px 12px', cursor: 'pointer', fontSize: 12,
                      display: 'flex', alignItems: 'center', gap: 8,
                      background: isSelected ? '#e8f5e9' : 'transparent',
                    }}
                      onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = '#f5f5f5'; }}
                      onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = isSelected ? '#e8f5e9' : ''; }}
                    >
                      <span style={{
                        width: 14, height: 14, borderRadius: 3,
                        border: isSelected ? '2px solid #2e7d32' : '2px solid #ccc',
                        background: isSelected ? '#2e7d32' : 'transparent',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        color: '#fff', fontSize: 9, fontWeight: 700, flexShrink: 0,
                      }}>{isSelected ? '\u2713' : ''}</span>
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {ds.label || ds.path || ds.id}
                      </span>
                    </div>
                  );
                })}
                {featureDataSources.length > 0 && (
                  <>
                    <div style={{ padding: '8px 12px', fontSize: 11, color: '#999', fontWeight: 600, borderTop: '1px solid #f0f0f0', borderBottom: '1px solid #f0f0f0' }}>
                      {t('Feature-Datenquellen')}
                    </div>
                    {featureDataSources.map(fds => {
                      const isSelected = attachedFeatureDataSourceIds.includes(fds.id);
                      return (
                        <div key={fds.id} onClick={() => _toggleFeatureDataSource(fds.id)} style={{
                          padding: '8px 12px', cursor: 'pointer', fontSize: 12,
                          display: 'flex', alignItems: 'center', gap: 8,
                          background: isSelected ? '#f3e5f5' : 'transparent',
                        }}
                          onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = '#f5f5f5'; }}
                          onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = isSelected ? '#f3e5f5' : ''; }}
                        >
                          <span style={{
                            width: 14, height: 14, borderRadius: 3,
                            border: isSelected ? '2px solid #7b1fa2' : '2px solid #ccc',
                            background: isSelected ? '#7b1fa2' : 'transparent',
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            color: '#fff', fontSize: 9, fontWeight: 700, flexShrink: 0,
                          }}>{isSelected ? '\u2713' : ''}</span>
                          <span style={{ display: 'flex', alignItems: 'center', fontSize: 12, color: '#7b1fa2', flexShrink: 0 }}>
                            {getPageIcon(`feature.${fds.featureCode}`) || '\uD83D\uDDC3\uFE0F'}
                          </span>
                          <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                            {fds.label || fds.featureCode} – {fds.tableName}
                          </span>
                        </div>
                      );
                    })}
                  </>
                )}
              </div>
            )}
          </div>
        )}

        {loading ? (
          <button onClick={_handleStop} disabled={stopping} title={stopping ? t('Stoppen…') : t('Anfrage stoppen')} style={{
            padding: '8px 14px', borderRadius: 8, border: 'none',
            background: stopping ? '#9e9e9e' : '#f44336', color: '#fff',
            cursor: stopping ? 'wait' : 'pointer', fontWeight: 600, fontSize: 12,
            opacity: stopping ? 0.7 : 1,
          }}>{stopping ? t('Stoppen…') : t('Stopp')}</button>
        ) : (
          <button onClick={_handleSend} disabled={!prompt.trim() || !workflowId} style={{
            padding: '8px 14px', borderRadius: 8, border: 'none',
            background: prompt.trim() && workflowId ? 'var(--primary-color, #F25843)' : '#ccc',
            color: '#fff', cursor: prompt.trim() && workflowId ? 'pointer' : 'default',
            fontWeight: 600, fontSize: 12,
          }}>{t('Senden')}</button>
        )}
      </div>
    </div>
  );
};
