/**
 * FilesPage
 *
 * Split-view file management: FolderTree on the left, FormGeneratorTable on the right.
 * The tree is the master – it dictates which folder's files the table shows (paginated).
 * Tree files are managed by FileContext (lazy-loaded per expanded folder).
 */

import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
import api from '../../api';
import { useUserFiles, useFileOperations } from '../../hooks/useFiles';
import { useFileContext } from '../../contexts/FileContext';
import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable';
import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm';
import FolderTree from '../../components/FolderTree/FolderTree';
import { useResizablePanels } from '../../hooks/useResizablePanels';
import { FaSync, FaUpload, FaDownload, FaEye, FaFolderPlus } from 'react-icons/fa';
import { useToast } from '../../contexts/ToastContext';
import { usePrompt } from '../../hooks/usePrompt';
import styles from '../admin/Admin.module.css';
import { useLanguage } from '../../providers/language/LanguageContext';
import { getUserDataCache } from '../../utils/userCache';

interface UserFile {
  id: string;
  fileName: string;
  mimeType?: string;
  fileSize?: number;
  folderId?: string | null;
  featureInstanceId?: string;
  [key: string]: any;
}

export const FilesPage: React.FC = () => {
  const { t } = useLanguage();
  const fileInputRef = useRef<HTMLInputElement>(null);
  const { showSuccess, showError } = useToast();
  const { prompt: promptInput, PromptDialog } = usePrompt();
  const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null);

  const {
    leftWidth, isDragging, handleMouseDown, containerRef,
  } = useResizablePanels({
    storageKey: 'filesPage-panelWidth',
    defaultLeftWidth: 22,
    minLeftWidth: 15,
    maxLeftWidth: 40,
  });

  // ── Table data (paginated, filtered by selectedFolderId) ──────────────
  const {
    data: tableFiles,
    attributes,
    permissions,
    loading: tableLoading,
    error,
    refetch: tableRefetch,
    pagination,
    fetchFileById,
    updateFileOptimistically,
  } = useUserFiles();

  const {
    handleFileDownload,
    handleFileDelete,
    handleFileDeleteMultiple,
    handleFileUpload,
    handleFileUpdate,
    handleFilePreview,
    handleInlineUpdate,
    deletingFiles,
    downloadingFiles,
    uploadingFile,
    previewingFiles,
  } = useFileOperations();

  // ── Tree data (from FileContext – lazy-loaded per expanded folder) ─────
  const {
    folders,
    refreshFolders,
    treeFileNodes,
    refreshTreeFiles,
    updateTreeFileNode,
    expandedFolderIds,
    toggleFolderExpanded,
    handleCreateFolder,
    handleRenameFolder,
    handleDeleteFolder,
    handleMoveFolder,
    handleMoveFolders,
    handleMoveFile: contextMoveFile,
    handleMoveFiles: contextMoveFiles,
    handleDownloadFolder,
  } = useFileContext();

  const [editingFile, setEditingFile] = useState<UserFile | null>(null);
  const [selectedFiles, setSelectedFiles] = useState<UserFile[]>([]);
  const [treeSelectedIds, setTreeSelectedIds] = useState<Set<string>>(new Set());
  const [highlightedFileId, setHighlightedFileId] = useState<string | null>(null);

  // ── Table refetch: filter by real folderId ───────────────────────────
  const _tableRefetch = useCallback(async (params?: any) => {
    const nextParams = { ...(params || {}) };
    const nextFilters = { ...(nextParams.filters || {}) };

    if (!selectedFolderId) {
      nextFilters.folderId = null;
    } else {
      nextFilters.folderId = selectedFolderId;
    }

    nextParams.filters = nextFilters;
    await tableRefetch(nextParams);
  }, [tableRefetch, selectedFolderId]);

  useEffect(() => {
    _tableRefetch({ page: 1, pageSize: 25 });
  }, [selectedFolderId, _tableRefetch]);

  const _refreshAll = useCallback(async () => {
    await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
  }, [_tableRefetch, refreshTreeFiles, refreshFolders]);

  const _handleScopeChange = useCallback(async (fileId: string, newScope: string) => {
    updateTreeFileNode(fileId, { scope: newScope });
    try {
      await api.patch(`/api/files/${fileId}/scope`, { scope: newScope });
      _tableRefetch();
    } catch (err) {
      console.error('Failed to update scope:', err);
      await Promise.all([refreshTreeFiles(), _tableRefetch()]);
    }
  }, [updateTreeFileNode, refreshTreeFiles, _tableRefetch]);

  const _handleNeutralizeToggle = useCallback(async (fileId: string, newValue: boolean) => {
    updateTreeFileNode(fileId, { neutralize: newValue });
    try {
      await api.patch(`/api/files/${fileId}/neutralize`, { neutralize: newValue });
      _tableRefetch();
    } catch (err) {
      console.error('Failed to toggle neutralize:', err);
      await Promise.all([refreshTreeFiles(), _tableRefetch()]);
    }
  }, [updateTreeFileNode, refreshTreeFiles, _tableRefetch]);

  // ── Folder nodes for tree (real folders only) ────────────────────────
  const folderNodes = useMemo(() => {
    return folders.map(f => ({
      id: f.id,
      name: f.name,
      parentId: f.parentId ?? null,
      fileCount: f.fileCount ?? 0,
    }));
  }, [folders]);

  // ── Columns ───────────────────────────────────────────────────────────
  const columns = useMemo(() => {
    const hiddenColumns = ['id', 'fileHash', 'folderId'];
    const cols = (attributes || [])
      .filter(attr => !hiddenColumns.includes(attr.name))
      .map(attr => ({
        key: attr.name,
        label: attr.label || attr.name,
        type: attr.type as any,
        sortable: attr.sortable !== false,
        filterable: attr.filterable !== false,
        searchable: attr.searchable !== false,
        width: attr.width || 150,
        minWidth: attr.minWidth || 100,
        maxWidth: attr.maxWidth || 400,
        fkSource: (attr as any).fkSource,
        fkDisplayField: (attr as any).fkDisplayField,
      }));
    cols.push({
      key: 'sysCreatedBy',
      label: t('Erstellt von'),
      type: 'text' as any,
      sortable: true,
      filterable: true,
      searchable: true,
      width: 150,
      minWidth: 100,
      maxWidth: 250,
      fkSource: '/api/users/',
      fkDisplayField: 'username',
    } as any);
    return cols;
  }, [attributes, t]);

  const canCreate = permissions?.create !== 'n';
  const canUpdate = permissions?.update !== 'n';
  const canDelete = permissions?.delete !== 'n';

  const currentUserId = useMemo(() => getUserDataCache()?.id || '', []);
  const _isOwned = useCallback((row: UserFile) => row.sysCreatedBy === currentUserId, [currentUserId]);

  // ── Tree event handlers ───────────────────────────────────────────────
  const _handleTreeFileSelect = useCallback((fileId: string) => {
    const file = treeFileNodes.find(f => f.id === fileId);
    if (file) {
      setSelectedFolderId(file.folderId ?? null);
      setHighlightedFileId(fileId);
      requestAnimationFrame(() => {
        const row = document.querySelector('tr[data-highlighted="true"]');
        if (row) row.scrollIntoView({ behavior: 'smooth', block: 'center' });
      });
      setTimeout(() => setHighlightedFileId(null), 2500);
    }
  }, [treeFileNodes]);

  const _handleMoveFile = useCallback(async (fileId: string, targetFolderId: string | null) => {
    await contextMoveFile(fileId, targetFolderId);
    await _tableRefetch();
  }, [contextMoveFile, _tableRefetch]);

  const _handleMoveFiles = useCallback(async (fileIds: string[], targetFolderId: string | null) => {
    await contextMoveFiles(fileIds, targetFolderId);
    await _tableRefetch();
  }, [contextMoveFiles, _tableRefetch]);

  const _handleRenameFile = useCallback(async (fileId: string, newName: string) => {
    await handleFileUpdate(fileId, { fileName: newName });
    await Promise.all([_tableRefetch(), refreshTreeFiles()]);
  }, [handleFileUpdate, _tableRefetch, refreshTreeFiles]);

  const _handleDeleteTreeFile = useCallback(async (fileId: string) => {
    await handleFileDelete(fileId);
    await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
  }, [handleFileDelete, _tableRefetch, refreshTreeFiles, refreshFolders]);

  const _handleDeleteTreeFiles = useCallback(async (fileIds: string[]) => {
    await handleFileDeleteMultiple(fileIds);
    await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
  }, [handleFileDeleteMultiple, _tableRefetch, refreshTreeFiles, refreshFolders]);

  const _handleDeleteTreeFolders = useCallback(async (folderIds: string[]) => {
    await api.post('/api/files/batch-delete', { folderIds, recursiveFolders: true });
    await Promise.all([refreshFolders(), refreshTreeFiles(), _tableRefetch()]);
  }, [refreshFolders, refreshTreeFiles, _tableRefetch]);

  // ── Table event handlers ──────────────────────────────────────────────
  const handleEditClick = async (file: UserFile) => {
    const fullFile = await fetchFileById(file.id);
    if (fullFile) setEditingFile(fullFile as UserFile);
  };

  const handleEditSubmit = async (data: Partial<UserFile>) => {
    if (!editingFile) return;
    const changes: Record<string, any> = {};
    const editableFields = ['fileName', 'scope', 'tags', 'description', 'folderId', 'neutralize'] as const;
    for (const field of editableFields) {
      if (data[field] !== undefined && data[field] !== editingFile[field]) {
        changes[field] = data[field];
      }
    }
    if (Object.keys(changes).length === 0 && data.fileName) {
      changes.fileName = data.fileName;
    }
    const result = await handleFileUpdate(editingFile.id, changes);
    if (result.success) {
      setEditingFile(null);
      await Promise.all([_tableRefetch(), refreshTreeFiles()]);
    }
  };

  const handleDelete = async (file: UserFile) => {
    const success = await handleFileDelete(file.id);
    if (success) await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
  };

  const handleDeleteMultiple = async (filesToDelete: UserFile[]) => {
    const ids = filesToDelete.map(f => f.id);
    const success = await handleFileDeleteMultiple(ids);
    if (success) await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
  };

  const handleDownload = async (file: UserFile) => {
    await handleFileDownload(file.id, file.fileName);
  };

  const handlePreview = async (file: UserFile) => {
    const result = await handleFilePreview(file.id, file.fileName, file.mimeType);
    if (result.success && result.previewUrl) {
      window.open(result.previewUrl, '_blank');
    }
  };

  const handleUploadClick = () => { fileInputRef.current?.click(); };

  const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const picked = e.target.files;
    if (picked && picked.length > 0) {
      let successCount = 0;
      let errorCount = 0;
      for (const file of Array.from(picked)) {
        const result = await handleFileUpload(file);
        if (result?.success) successCount++; else errorCount++;
      }
      if (fileInputRef.current) fileInputRef.current.value = '';
      await Promise.all([_tableRefetch(), refreshTreeFiles(), refreshFolders()]);
      if (successCount > 0) {
        showSuccess(
          t('Upload erfolgreich'),
          errorCount > 0
            ? t('{successCount} Datei(en) hochgeladen, {errorCount} fehlgeschlagen', { successCount, errorCount })
            : t('{successCount} Datei(en) hochgeladen', { successCount }),
        );
      } else if (errorCount > 0) {
        showError(t('Upload fehlgeschlagen'), t('{errorCount} Datei(en) konnten nicht hochgeladen werden', { errorCount }));
      }
    }
  };

  const _handleNewFolder = useCallback(async () => {
    const name = await promptInput(t('Neuer Ordnername:'), { title: t('Neuer Ordner'), placeholder: t('Ordnername') });
    if (name?.trim()) {
      await handleCreateFolder(name.trim(), selectedFolderId);
    }
  }, [handleCreateFolder, selectedFolderId, promptInput, t]);

  const _onRowDragStart = useCallback((e: React.DragEvent<HTMLTableRowElement>, row: UserFile) => {
    const isInSelection = selectedFiles.some(f => f.id === row.id);
    if (isInSelection && selectedFiles.length > 1) {
      e.dataTransfer.setData('application/file-ids', JSON.stringify(selectedFiles.map(f => f.id)));
    } else {
      e.dataTransfer.setData('application/file-id', row.id);
    }
    e.dataTransfer.effectAllowed = 'move';
  }, [selectedFiles]);

  const formAttributes = useMemo(() => {
    const excludedFields = ['id', 'mandateId', 'fileHash', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'creationDate', 'source'];
    return (attributes || []).filter(attr => !excludedFields.includes(attr.name));
  }, [attributes]);

  if (error) {
    return (
      <div className={styles.adminPage}>
        <div className={styles.errorContainer}>
          <span className={styles.errorIcon}>⚠️</span>
          <p className={styles.errorMessage}>{t('Fehler beim Laden der Dateien: {detail}', { detail: String(error) })}</p>
          <button className={styles.secondaryButton} onClick={() => _tableRefetch()}>
            <FaSync /> {t('Erneut versuchen')}
          </button>
        </div>
      </div>
    );
  }

  return (
    <div className={`${styles.adminPage} ${styles.adminPageFill}`}>
      <input
        ref={fileInputRef}
        type="file"
        multiple
        style={{ display: 'none' }}
        onChange={handleFileSelect}
      />

      <div className={styles.pageHeader}>
        <div>
          <h1 className={styles.pageTitle}>{t('Dateien')}</h1>
          <p className={styles.pageSubtitle}>{t('Dateiverwaltung')}</p>
        </div>
        <div className={styles.headerActions}>
          <button className={styles.secondaryButton} onClick={() => _refreshAll()} disabled={tableLoading}>
            <FaSync className={tableLoading ? 'spinning' : ''} /> {t('Aktualisieren')}
          </button>
        </div>
      </div>

      <div
        ref={containerRef as React.RefObject<HTMLDivElement>}
        style={{ display: 'flex', flex: 1, overflow: 'hidden', minHeight: 0, position: 'relative' }}
      >
        {/* Left panel: FolderTree */}
        <div style={{
          width: `${leftWidth}%`,
          minWidth: 0,
          overflow: 'auto',
          borderRight: '1px solid var(--color-border, #e0e0e0)',
          padding: '8px 4px',
        }}>
          <FolderTree
            folders={folderNodes}
            files={treeFileNodes}
            showFiles={true}
            selectedFolderId={selectedFolderId}
            onSelect={setSelectedFolderId}
            onFileSelect={_handleTreeFileSelect}
            selectedItemIds={treeSelectedIds}
            onSelectionChange={setTreeSelectedIds}
            expandedIds={expandedFolderIds}
            onToggleExpand={toggleFolderExpanded}
            onRefresh={_refreshAll}
            onCreateFolder={handleCreateFolder}
            onRenameFolder={handleRenameFolder}
            onDeleteFolder={async (folderId) => {
              await handleDeleteFolder(folderId);
              if (selectedFolderId === folderId) setSelectedFolderId(null);
              await _tableRefetch();
            }}
            onMoveFolder={handleMoveFolder}
            onMoveFolders={handleMoveFolders}
            onMoveFile={_handleMoveFile}
            onMoveFiles={_handleMoveFiles}
            onRenameFile={_handleRenameFile}
            onDeleteFile={_handleDeleteTreeFile}
            onDeleteFiles={_handleDeleteTreeFiles}
            onDeleteFolders={_handleDeleteTreeFolders}
            onDownloadFolder={handleDownloadFolder}
            onScopeChange={_handleScopeChange}
            onNeutralizeToggle={_handleNeutralizeToggle}
          />
        </div>

        {/* Resizable divider */}
        <div
          onMouseDown={handleMouseDown}
          style={{
            width: 6,
            cursor: 'col-resize',
            background: isDragging ? 'var(--primary-dark-bg, rgba(242, 88, 67, 0.2))' : 'transparent',
            transition: isDragging ? 'none' : 'background 0.15s',
            flexShrink: 0,
            zIndex: 10,
          }}
          onMouseEnter={(e) => { (e.target as HTMLElement).style.background = 'var(--color-border-hover, #bbb)'; }}
          onMouseLeave={(e) => { if (!isDragging) (e.target as HTMLElement).style.background = 'transparent'; }}
        />

        {/* Right panel: File table */}
        <div style={{ flex: 1, minWidth: 0, overflow: 'auto', display: 'flex', flexDirection: 'column' }}>
          <div style={{
            display: 'flex', gap: 8, padding: '8px 12px',
            borderBottom: '1px solid var(--color-border, #e0e0e0)',
            flexShrink: 0, alignItems: 'center', flexWrap: 'wrap',
          }}>
            <button className={styles.secondaryButton} onClick={_handleNewFolder}>
              <FaFolderPlus /> {t('Neuer Ordner')}
            </button>
            {canCreate && (
              <button className={styles.primaryButton} onClick={handleUploadClick} disabled={uploadingFile}>
                <FaUpload /> {uploadingFile ? t('Wird hochgeladen…') : t('Datei hochladen')}
              </button>
            )}
          </div>

          <div style={{ flex: 1, overflow: 'auto' }}>
            <FormGeneratorTable
              data={tableFiles || []}
              columns={columns}
              apiEndpoint="/api/files/list"
              loading={tableLoading}
              pagination={true}
              pageSize={25}
              searchable={true}
              filterable={true}
              sortable={true}
              selectable={true}
              onRowSelect={(rows) => setSelectedFiles(rows as UserFile[])}
              rowDraggable={true}
              onRowDragStart={_onRowDragStart}
              getRowDataAttributes={(row: UserFile) =>
                ({ highlighted: row.id === highlightedFileId ? 'true' : 'false' })
              }
              actionButtons={[
                ...(canUpdate ? [{
                  type: 'edit' as const,
                  onAction: handleEditClick,
                  title: t('Bearbeiten'),
                  disabled: (row: UserFile) => !_isOwned(row) ? { disabled: true, message: t('Nur Eigentümer kann bearbeiten') } : false,
                }] : []),
                ...(canDelete ? [{
                  type: 'delete' as const,
                  title: t('Löschen'),
                  loading: (row: UserFile) => deletingFiles.has(row.id),
                  disabled: (row: UserFile) => !_isOwned(row) ? { disabled: true, message: t('Nur Eigentümer kann löschen') } : false,
                }] : []),
              ]}
              customActions={[
                {
                  id: 'download',
                  icon: <FaDownload />,
                  onClick: handleDownload,
                  title: t('Herunterladen'),
                  loading: (row: UserFile) => downloadingFiles.has(row.id),
                },
                {
                  id: 'preview',
                  icon: <FaEye />,
                  onClick: handlePreview,
                  title: t('Vorschau'),
                  loading: (row: UserFile) => previewingFiles.has(row.id),
                },
              ]}
              onDelete={handleDelete}
              onDeleteMultiple={handleDeleteMultiple}
              hookData={{
                refetch: _tableRefetch,
                pagination,
                permissions,
                handleDelete: handleFileDelete,
                handleInlineUpdate,
                updateOptimistically: updateFileOptimistically,
              }}
              emptyMessage={t('Keine Dateien gefunden')}
            />
          </div>
        </div>
      </div>

      {editingFile && (
        <div className={styles.modalOverlay} onClick={() => setEditingFile(null)}>
          <div className={styles.modal} onClick={e => e.stopPropagation()}>
            <div className={styles.modalHeader}>
              <h2 className={styles.modalTitle}>{t('Datei bearbeiten')}</h2>
              <button className={styles.modalClose} onClick={() => setEditingFile(null)}>✕</button>
            </div>
            <div className={styles.modalContent}>
              {formAttributes.length === 0 ? (
                <div className={styles.loadingContainer}>
                  <div className={styles.spinner} />
                  <span>{t('Formular laden')}</span>
                </div>
              ) : (
                <FormGeneratorForm
                  attributes={formAttributes}
                  data={editingFile}
                  mode="edit"
                  onSubmit={handleEditSubmit}
                  onCancel={() => setEditingFile(null)}
                  submitButtonText={t('Speichern')}
                  cancelButtonText={t('Abbrechen')}
                />
              )}
            </div>
          </div>
        </div>
      )}
      <PromptDialog />
    </div>
  );
};

export default FilesPage;
