import React, { useState, useEffect, useCallback, useMemo } from 'react';
import api from '../../../api';

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

const _chatPromptSourceId = '__chat_prompt__';
const _placeholderRx = /\[([a-z]+)\.([a-f0-9-]{36})\]/g;

interface NeutralizationMapping {
  id: string;
  originalText: string;
  placeholder: string;
  patternType: string;
  fileId?: string;
  fileName?: string;
  createdAt?: string;
}

interface NeutralizationSnapshot {
  id: string;
  sourceLabel: string;
  neutralizedText: string;
  placeholderCount: number;
}

interface NeutralizationSource {
  fileId: string;
  fileName: string;
  neutralizationStatus: string;
  mappingCount: number;
  isVirtual?: boolean;
}

interface NeutralizationPanelProps {
  instanceId: string;
}

function _normalizeApiRow(raw: Record<string, unknown>): NeutralizationMapping {
  const id = String(raw.id ?? '');
  const patternType = String(raw.patternType ?? 'unknown');
  const existingPh = raw.placeholder;
  const placeholder =
    typeof existingPh === 'string' && existingPh
      ? existingPh
      : id
        ? `[${patternType}.${id}]`
        : '';
  return {
    id,
    originalText: String(raw.originalText ?? ''),
    placeholder,
    patternType,
    fileId: raw.fileId != null && raw.fileId !== '' ? String(raw.fileId) : undefined,
    fileName: raw.fileName != null ? String(raw.fileName) : undefined,
    createdAt:
      raw.createdAt != null
        ? String(raw.createdAt)
        : raw.sysCreatedAt != null
          ? String(raw.sysCreatedAt)
          : undefined,
  };
}

function _partitionAttributes(rows: unknown[]): {
  byFile: Record<string, NeutralizationMapping[]>;
  unscoped: NeutralizationMapping[];
} {
  const byFile: Record<string, NeutralizationMapping[]> = {};
  const unscoped: NeutralizationMapping[] = [];
  for (const item of rows) {
    if (!item || typeof item !== 'object') continue;
    const raw = item as Record<string, unknown>;
    const m = _normalizeApiRow(raw);
    const fid = raw.fileId;
    if (fid == null || fid === '') {
      unscoped.push(m);
    } else {
      const key = String(fid);
      if (!byFile[key]) byFile[key] = [];
      byFile[key].push(m);
    }
  }
  return { byFile, unscoped };
}

const _phTypeColors: Record<string, string> = {
  name: '#7c3aed',
  email: '#2563eb',
  phone: '#0891b2',
  address: '#059669',
  financial: '#d97706',
  id: '#dc2626',
  logic: '#be185d',
  company: '#4f46e5',
  product: '#7c3aed',
  location: '#059669',
  other: '#6b7280',
};

function _renderHighlightedText(
  text: string,
  mappingLookup: Map<string, NeutralizationMapping>,
): React.ReactNode[] {
  const parts: React.ReactNode[] = [];
  let lastIdx = 0;
  const rx = new RegExp(_placeholderRx.source, 'g');
  let match: RegExpExecArray | null;

  while ((match = rx.exec(text)) !== null) {
    if (match.index > lastIdx) {
      parts.push(<span key={`t-${lastIdx}`}>{text.slice(lastIdx, match.index)}</span>);
    }
    const phType = match[1];
    const phId = match[2];
    const fullPh = match[0];
    const mapping = mappingLookup.get(phId);
    const color = _phTypeColors[phType] || _phTypeColors.other;
    parts.push(
      <span
        key={`ph-${match.index}`}
        title={mapping ? `${mapping.originalText} (${phType})` : phType}
        style={{
          background: color + '18',
          color,
          border: `1px solid ${color}40`,
          borderRadius: 4,
          padding: '1px 4px',
          fontFamily: 'monospace',
          fontSize: '0.78rem',
          cursor: 'help',
          whiteSpace: 'nowrap',
        }}
      >
        {fullPh}
      </span>,
    );
    lastIdx = match.index + match[0].length;
  }
  if (lastIdx < text.length) {
    parts.push(<span key={`t-${lastIdx}`}>{text.slice(lastIdx)}</span>);
  }
  return parts;
}

const NeutralizationPanel: React.FC<NeutralizationPanelProps> = ({ instanceId }) => {
  const { t } = useLanguage();
  const [sources, setSources] = useState<NeutralizationSource[]>([]);
  const [selectedSource, setSelectedSource] = useState<string | null>(null);
  const [mappings, setMappings] = useState<NeutralizationMapping[]>([]);
  const [loading, setLoading] = useState(true);
  const [attributeByFile, setAttributeByFile] = useState<Record<string, NeutralizationMapping[]>>({});
  const [attributeUnscoped, setAttributeUnscoped] = useState<NeutralizationMapping[]>([]);
  const [snapshots, setSnapshots] = useState<NeutralizationSnapshot[]>([]);
  const [expandedSnapshot, setExpandedSnapshot] = useState<string | null>(null);

  const _mappingLookup = useMemo(() => {
    const map = new Map<string, NeutralizationMapping>();
    for (const m of attributeUnscoped) map.set(m.id, m);
    for (const arr of Object.values(attributeByFile)) {
      for (const m of arr) map.set(m.id, m);
    }
    return map;
  }, [attributeUnscoped, attributeByFile]);

  const _loadSources = useCallback(async () => {
    setLoading(true);
    try {
      const headers = instanceId ? { 'X-Instance-Id': instanceId } : undefined;
      const [filesResponse, attrResponse] = await Promise.all([
        api.get(`/api/workspace/${instanceId}/files`, { headers }),
        api.get('/api/neutralization/attributes', { headers }),
      ]);

      let snapAxios: { data: unknown } = { data: [] };
      try {
        const _enc = encodeURIComponent(instanceId);
        snapAxios = await api.get(`/api/neutralization/${_enc}/snapshots`, { headers });
      } catch (_snapErr) {
        console.warn('NeutralizationPanel: scoped snapshots failed, trying unscoped:', _snapErr);
        try {
          snapAxios = await api.get('/api/neutralization/snapshots', { headers });
        } catch (_snapErr2) {
          console.error('NeutralizationPanel: snapshots could not be loaded:', _snapErr2);
          snapAxios = { data: [] };
        }
      }

      const rawFiles = filesResponse.data;
      const files = Array.isArray(rawFiles) ? rawFiles : (rawFiles?.files || rawFiles?.data || []);
      const fileList = Array.isArray(files) ? files : [];

      const attrPayload = attrResponse.data?.data ?? attrResponse.data ?? [];
      const attrRows = Array.isArray(attrPayload) ? attrPayload : [];
      const { byFile, unscoped } = _partitionAttributes(attrRows);
      setAttributeByFile(byFile);
      setAttributeUnscoped(unscoped);

      const _snapBody = snapAxios.data as { data?: unknown } | unknown[] | null | undefined;
      const snapPayload =
        Array.isArray(_snapBody) ? _snapBody : (_snapBody && typeof _snapBody === 'object' && 'data' in _snapBody
          ? ( _snapBody as { data: unknown }).data
          : _snapBody) ?? [];
      const snapList: NeutralizationSnapshot[] = Array.isArray(snapPayload) ? (snapPayload as NeutralizationSnapshot[]) : [];
      setSnapshots(snapList);
      if (snapList.length > 0 && snapList[0].id) {
        setExpandedSnapshot(snapList[0].id);
      } else {
        setExpandedSnapshot(null);
      }

      const neutralizedFiles = fileList.filter((f: Record<string, unknown>) => f.neutralize);

      const nextSources: NeutralizationSource[] = [];
      if (unscoped.length > 0) {
        nextSources.push({
          fileId: _chatPromptSourceId,
          fileName: 'Chat, Prompt & Kontext',
          neutralizationStatus: 'completed',
          mappingCount: unscoped.length,
          isVirtual: true,
        });
      }
      for (const f of neutralizedFiles) {
        const fid = String(f.id ?? '');
        if (!fid) continue;
        nextSources.push({
          fileId: fid,
          fileName: String(f.fileName ?? f.name ?? 'unknown'),
          neutralizationStatus: String(f.neutralizationStatus ?? f.status ?? 'unknown'),
          mappingCount: byFile[fid]?.length ?? 0,
        });
      }
      setSources(nextSources);
    } catch (err) {
      console.error('Failed to load neutralization sources:', err);
    } finally {
      setLoading(false);
    }
  }, [instanceId]);

  useEffect(() => {
    _loadSources();
  }, [_loadSources]);

  useEffect(() => {
    if (!selectedSource) {
      setMappings([]);
      return;
    }
    if (selectedSource === _chatPromptSourceId) {
      setMappings(attributeUnscoped);
      return;
    }
    setMappings(attributeByFile[selectedSource] ?? []);
  }, [selectedSource, attributeByFile, attributeUnscoped]);

  const _handleDeleteMapping = async (mappingId: string) => {
    try {
      await api.delete(`/api/neutralization/attributes/single/${mappingId}`, {
        headers: instanceId ? { 'X-Instance-Id': instanceId } : undefined,
      });
      await _loadSources();
    } catch (err) {
      console.error('Failed to delete mapping:', err);
    }
  };

  const _handleRetrigger = async (fileId: string) => {
    try {
      await api.post(
        '/api/neutralization/retrigger',
        { fileId },
        { headers: instanceId ? { 'X-Instance-Id': instanceId } : undefined },
      );
      await _loadSources();
    } catch (err) {
      console.error('Failed to retrigger neutralization:', err);
    }
  };

  const _statusBadge = (status: string) => {
    const colors: Record<string, { bg: string; text: string }> = {
      completed: { bg: '#dcfce7', text: '#166534' },
      pending: { bg: '#fef3c7', text: '#92400e' },
      failed: { bg: '#fef2f2', text: '#991b1b' },
      not_required: { bg: '#f3f4f6', text: '#6b7280' },
    };
    const c = colors[status] || colors.not_required;
    return (
      <span style={{ fontSize: '0.75rem', padding: '2px 8px', borderRadius: 10, background: c.bg, color: c.text }}>
        {status}
      </span>
    );
  };

  if (loading) return <div style={{ padding: 16, textAlign: 'center', color: '#6b7280' }}>{t('Neutralisierungsdaten werden geladen')}</div>;

  const _hasAnyData = sources.length > 0 || snapshots.length > 0;

  return (
    <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 16 }}>
      <h3 style={{ margin: 0, fontSize: '1.1rem' }}>{t('Neutralisierung')}</h3>
      <p style={{ margin: 0, fontSize: '0.85rem', color: 'var(--text-secondary, #6b7280)' }}>
        {t('Neutralisierte Texte mit Platzhaltern und die zugehörigen Mappings (Original &harr; Platzhalter).')}
      </p>

      {/* ── Snapshots: neutralisierter Text ──────────────────────── */}
      {snapshots.length > 0 && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ fontSize: '0.85rem', fontWeight: 600, color: 'var(--text-primary, #111827)' }}>
            {t('Neutralisierter Text')} ({snapshots.length})
          </div>
          {snapshots.map((snap) => {
            const _isExpanded = expandedSnapshot === snap.id;
            return (
              <div key={snap.id} style={{ border: '1px solid var(--border-color, #e5e7eb)', borderRadius: 8, overflow: 'hidden' }}>
                <div
                  onClick={() => setExpandedSnapshot(_isExpanded ? null : snap.id)}
                  style={{
                    padding: '8px 12px',
                    background: 'var(--bg-hover, #f9fafb)',
                    cursor: 'pointer',
                    display: 'flex',
                    justifyContent: 'space-between',
                    alignItems: 'center',
                    fontSize: '0.85rem',
                  }}
                >
                  <span style={{ fontWeight: 500 }}>{snap.sourceLabel}</span>
                  <span style={{ fontSize: '0.75rem', color: '#9ca3af' }}>
                    {snap.placeholderCount} {t('Platzhalter')} {_isExpanded ? '\u25BC' : '\u25B6'}
                  </span>
                </div>
                {_isExpanded && (
                  <div
                    style={{
                      padding: '10px 12px',
                      fontSize: '0.82rem',
                      lineHeight: 1.6,
                      maxHeight: 400,
                      overflowY: 'auto',
                      whiteSpace: 'pre-wrap',
                      wordBreak: 'break-word',
                      background: 'var(--bg-primary, #fff)',
                    }}
                  >
                    {_renderHighlightedText(snap.neutralizedText, _mappingLookup)}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}

      {/* ── Quellen (Dateien + Chat/Prompt) ──────────────────────── */}
      {sources.length > 0 && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ fontSize: '0.85rem', fontWeight: 600, color: 'var(--text-primary, #111827)' }}>
            {t('Datenquellen')}
          </div>
          {sources.map((src) => (
            <div
              key={src.fileId}
              style={{
                padding: '10px 14px',
                borderRadius: 8,
                border:
                  selectedSource === src.fileId ? '2px solid var(--accent, #4f46e5)' : '1px solid var(--border-color, #e5e7eb)',
                cursor: 'pointer',
                display: 'flex',
                justifyContent: 'space-between',
                alignItems: 'center',
              }}
              onClick={() => setSelectedSource(src.fileId === selectedSource ? null : src.fileId)}
            >
              <div>
                <div style={{ fontWeight: 500, fontSize: '0.9rem' }}>{src.fileName}</div>
                <div style={{ fontSize: '0.8rem', color: '#6b7280', marginTop: 2 }}>
                  {_statusBadge(src.neutralizationStatus)}
                  {src.mappingCount > 0 && (
                    <span style={{ marginLeft: 8, color: '#9ca3af' }}>{src.mappingCount} {t('Mapping(s)')}</span>
                  )}
                </div>
              </div>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                {!src.isVirtual && (
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      _handleRetrigger(src.fileId);
                    }}
                    style={{
                      fontSize: '0.8rem',
                      padding: '4px 10px',
                      borderRadius: 6,
                      border: '1px solid var(--border-color, #d1d5db)',
                      background: 'transparent',
                      cursor: 'pointer',
                    }}
                  >
                    Erneut neutralisieren
                  </button>
                )}
                <span style={{ fontSize: '0.8rem', color: '#9ca3af' }}>{selectedSource === src.fileId ? '\u25BC' : '\u25B6'}</span>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* ── Mappings für ausgewählte Quelle ──────────────────────── */}
      {selectedSource && mappings.length > 0 && (
        <div style={{ border: '1px solid var(--border-color, #e5e7eb)', borderRadius: 8, overflow: 'hidden' }}>
          <div style={{ padding: '8px 16px', background: 'var(--bg-hover, #f9fafb)', fontSize: '0.85rem', fontWeight: 500 }}>
            {t('Platzhalter-Mappings')} ({mappings.length})
          </div>
          <div style={{ maxHeight: 300, overflowY: 'auto' }}>
            {mappings.map((m) => (
              <div
                key={m.id}
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  padding: '8px 16px',
                  borderTop: '1px solid var(--border-color, #f3f4f6)',
                  fontSize: '0.8rem',
                  gap: 12,
                }}
              >
                <span style={{ flex: 1, fontFamily: 'monospace', color: _phTypeColors[m.patternType] || '#4f46e5' }}>{m.placeholder}</span>
                <span style={{ color: '#9ca3af' }}>{'\u2192'}</span>
                <span style={{ flex: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={m.originalText}>
                  {m.originalText}
                </span>
                <span style={{ fontSize: '0.7rem', color: '#9ca3af', flexShrink: 0 }}>{m.patternType}</span>
                <button
                  onClick={() => _handleDeleteMapping(m.id)}
                  style={{ color: '#ef4444', border: 'none', background: 'transparent', cursor: 'pointer', fontSize: '0.9rem', padding: '2px 6px' }}
                  title={t('Mapping löschen')}
                >
                  {'\u00D7'}
                </button>
              </div>
            ))}
          </div>
        </div>
      )}

      {selectedSource && mappings.length === 0 && (
        <div style={{ padding: 16, textAlign: 'center', color: '#9ca3af', fontSize: '0.85rem' }}>
          {selectedSource === _chatPromptSourceId
            ? t('Keine Mappings ohne Dateizuordnung')
            : t('Keine gespeicherten Mappings für diese')}
        </div>
      )}

      {!_hasAnyData && (
        <div style={{ padding: 24, textAlign: 'center', color: '#9ca3af', fontSize: '0.85rem' }}>
          {t('Noch keine Neutralisierungsdaten vorhanden. Sende eine Nachricht mit aktivierter Neutralisierung.')}
        </div>
      )}
    </div>
  );
};

export default NeutralizationPanel;
