import React, { createContext, useContext, useCallback, useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import api from '../api';
import { useFileOperations } from '../hooks/useFiles';
import type { FolderInfo } from '../api/fileApi';
import type { FileNode } from '../components/FolderTree/FolderTree';

export type { FolderInfo };

interface FileContextType {
  folders: FolderInfo[];
  foldersLoading: boolean;
  refreshFolders: () => Promise<void>;

  treeFileNodes: FileNode[];
  treeFilesLoading: boolean;
  loadTreeFiles: (folderId: string) => Promise<void>;
  refreshTreeFiles: () => Promise<void>;
  updateTreeFileNode: (fileId: string, patch: Partial<FileNode>) => void;

  expandedFolderIds: Set<string>;
  toggleFolderExpanded: (id: string) => void;

  handleCreateFolder: (name: string, parentId: string | null) => Promise<void>;
  handleRenameFolder: (folderId: string, newName: string) => Promise<void>;
  handleDeleteFolder: (folderId: string) => Promise<void>;
  handleMoveFolder: (folderId: string, targetParentId: string | null) => Promise<void>;
  handleMoveFolders: (folderIds: string[], targetParentId: string | null) => Promise<void>;
  handleMoveFile: (fileId: string, targetFolderId: string | null) => Promise<void>;
  handleMoveFiles: (fileIds: string[], targetFolderId: string | null) => Promise<void>;
  handleDownloadFolder: (folderId: string, folderName: string) => Promise<void>;
  handleFileDelete: (fileId: string, onOptimisticDelete?: () => void) => Promise<boolean>;
  handleFileUpload: (file: File, workflowId?: string) => Promise<{ success: boolean; fileData?: any; error?: string }>;
  handleFilePreview: (fileId: string, fileName: string, mimeType?: string) => Promise<{ success: boolean; previewUrl?: string | null; blob?: Blob | null; isJsonContent?: boolean; decodedContent?: any; isTextContent?: boolean; error?: string }>;
  handleFileDownload: (fileId: string, fileName: string) => Promise<void>;
  uploadingFile: boolean;
  deletingFiles: Set<string>;
  previewingFiles: Set<string>;
  downloadingFiles: Set<string>;
}

export const FileContext = createContext<FileContextType | undefined>(undefined);

const _ROOT_KEY = '';

function _toFileNode(f: any): FileNode {
  return {
    id: f.id,
    fileName: f.fileName || f.name || 'unknown',
    mimeType: f.mimeType,
    fileSize: f.fileSize,
    folderId: f.folderId ?? null,
    scope: f.scope,
    neutralize: f.neutralize,
    sysCreatedBy: f.sysCreatedBy,
  };
}

export function FileProvider({ children }: { children: React.ReactNode }) {
  const {
    handleFileUpload: hookHandleFileUpload,
    handleFileDelete: hookHandleFileDelete,
    handleFilePreview,
    handleFileDownload,
    uploadingFile,
    deletingFiles,
    previewingFiles,
    downloadingFiles,
  } = useFileOperations();

  // ── Derive a session-scoped storage key from the current feature-instance URL ──
  const location = useLocation();
  const storageKey = useMemo(() => {
    const match = location.pathname.match(/^\/mandates\/([^/]+)\/([^/]+)\/([^/]+)/);
    const instanceId = match ? match[3] : '_global';
    return `folderTree-expandedIds-${instanceId}`;
  }, [location.pathname]);

  // ── Folder expanded state (persisted per feature-instance in sessionStorage) ──
  const _loadExpanded = (key: string): Set<string> => {
    try {
      const stored = sessionStorage.getItem(key);
      if (!stored) return new Set<string>();
      const ids: string[] = JSON.parse(stored);
      return new Set(ids.filter(id => id && id !== '__root__'));
    } catch { return new Set<string>(); }
  };

  const [expandedFolderIds, setExpandedFolderIds] = useState<Set<string>>(() => _loadExpanded(storageKey));

  useEffect(() => {
    setExpandedFolderIds(_loadExpanded(storageKey));
    setTreeFilesMap(new Map());
    setFolders([]);
  }, [storageKey]);

  // ── Folder state ──────────────────────────────────────────────────────
  const [folders, setFolders] = useState<FolderInfo[]>([]);
  const [foldersLoading, setFoldersLoading] = useState(false);

  const refreshFolders = useCallback(async () => {
    setFoldersLoading(true);
    try {
      const response = await api.get('/api/files/folders');
      const data = Array.isArray(response.data) ? response.data : [];
      setFolders(data);
    } catch (err) {
      console.error('Failed to load folders:', err);
    } finally {
      setFoldersLoading(false);
    }
  }, []);

  useEffect(() => { refreshFolders(); }, [refreshFolders, storageKey]);

  // ── Tree files: lazy-loaded per expanded folder ───────────────────────
  const [treeFilesMap, setTreeFilesMap] = useState<Map<string, FileNode[]>>(new Map());
  const [treeFilesLoading, setTreeFilesLoading] = useState(false);

  const loadTreeFiles = useCallback(async (folderId: string) => {
    const key = folderId || _ROOT_KEY;
    setTreeFilesLoading(true);
    try {
      const filterValue = folderId || null;
      const resp = await api.get('/api/files/list', {
        params: {
          pagination: JSON.stringify({
            page: 1,
            pageSize: 500,
            filters: { folderId: filterValue },
          }),
        },
      });
      const items: any[] = resp.data?.items || [];
      setTreeFilesMap(prev => {
        const next = new Map(prev);
        next.set(key, items.map(_toFileNode));
        return next;
      });
    } catch (err) {
      console.error(`Failed to load tree files for folder ${folderId}:`, err);
    } finally {
      setTreeFilesLoading(false);
    }
  }, []);

  const _removeTreeFiles = useCallback((folderId: string) => {
    const key = folderId || _ROOT_KEY;
    setTreeFilesMap(prev => {
      const next = new Map(prev);
      next.delete(key);
      return next;
    });
  }, []);

  const refreshTreeFiles = useCallback(async () => {
    const keys = Array.from(treeFilesMap.keys());
    if (!keys.includes(_ROOT_KEY)) keys.push(_ROOT_KEY);
    await Promise.all(
      keys.map(key => loadTreeFiles(key === _ROOT_KEY ? '' : key)),
    );
  }, [treeFilesMap, loadTreeFiles]);

  const updateTreeFileNode = useCallback((fileId: string, patch: Partial<FileNode>) => {
    setTreeFilesMap(prev => {
      const next = new Map<string, FileNode[]>();
      let found = false;
      for (const [key, files] of prev) {
        const updated = files.map(f => {
          if (f.id === fileId) {
            found = true;
            return { ...f, ...patch };
          }
          return f;
        });
        next.set(key, updated);
      }
      return found ? next : prev;
    });
  }, []);

  // Load root files on mount and on context change
  useEffect(() => { loadTreeFiles(''); }, [loadTreeFiles, storageKey]);

  // Load files for expanded folders on mount and context change
  useEffect(() => {
    expandedFolderIds.forEach(id => {
      if (!treeFilesMap.has(id)) {
        loadTreeFiles(id);
      }
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [storageKey]);

  const treeFileNodes: FileNode[] = useMemo(() => {
    const result: FileNode[] = [];
    for (const [, files] of treeFilesMap) {
      for (const f of files) result.push(f);
    }
    return result;
  }, [treeFilesMap]);

  // ── Toggle expand: load/unload tree files ─────────────────────────────
  const toggleFolderExpanded = useCallback((id: string) => {
    setExpandedFolderIds(prev => {
      const next = new Set(prev);
      if (next.has(id)) {
        next.delete(id);
      } else {
        next.add(id);
        loadTreeFiles(id);
      }
      try { sessionStorage.setItem(storageKey, JSON.stringify([...next])); } catch {}
      return next;
    });
  }, [storageKey, loadTreeFiles]);

  // ── Folder operations ─────────────────────────────────────────────────
  const handleCreateFolder = useCallback(async (name: string, parentId: string | null) => {
    await api.post('/api/files/folders', { name, parentId: parentId || null });
    await refreshFolders();
  }, [refreshFolders]);

  const handleRenameFolder = useCallback(async (folderId: string, newName: string) => {
    await api.put(`/api/files/folders/${folderId}`, { name: newName });
    await refreshFolders();
  }, [refreshFolders]);

  const handleDeleteFolder = useCallback(async (folderId: string) => {
    await api.delete(`/api/files/folders/${folderId}`, { params: { recursive: true } });
    _removeTreeFiles(folderId);
    await refreshFolders();
  }, [refreshFolders, _removeTreeFiles]);

  const handleMoveFolder = useCallback(async (folderId: string, targetParentId: string | null) => {
    await api.post(`/api/files/folders/${folderId}/move`, { targetParentId });
    await refreshFolders();
  }, [refreshFolders]);

  const handleMoveFolders = useCallback(async (folderIds: string[], targetParentId: string | null) => {
    await api.post('/api/files/batch-move', { folderIds, targetParentId });
    await refreshFolders();
  }, [refreshFolders]);

  // ── File operations ───────────────────────────────────────────────────
  const handleMoveFile = useCallback(async (fileId: string, targetFolderId: string | null) => {
    await api.post(`/api/files/${fileId}/move`, { targetFolderId });
    await refreshTreeFiles();
    await refreshFolders();
  }, [refreshTreeFiles, refreshFolders]);

  const handleMoveFiles = useCallback(async (fileIds: string[], targetFolderId: string | null) => {
    await api.post('/api/files/batch-move', { fileIds, targetFolderId });
    await refreshTreeFiles();
    await refreshFolders();
  }, [refreshTreeFiles, refreshFolders]);

  const handleFileUpload = useCallback(async (file: File, workflowId?: string) => {
    const result = await hookHandleFileUpload(file, workflowId);
    if (result.success) {
      await refreshTreeFiles();
      await refreshFolders();
    }
    return result;
  }, [hookHandleFileUpload, refreshTreeFiles, refreshFolders]);

  const handleFileDelete = useCallback(async (fileId: string, onOptimisticDelete?: () => void) => {
    const success = await hookHandleFileDelete(fileId, () => {
      onOptimisticDelete?.();
    });
    if (success) {
      await refreshTreeFiles();
      await refreshFolders();
    }
    return success;
  }, [hookHandleFileDelete, refreshTreeFiles, refreshFolders]);

  const handleDownloadFolder = useCallback(async (folderId: string, folderName: string) => {
    try {
      const response = await api.get(`/api/files/folders/${folderId}/download`, {
        responseType: 'blob',
      });
      const url = window.URL.createObjectURL(response.data);
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `${folderName}.zip`);
      document.body.appendChild(link);
      link.click();
      link.remove();
      window.URL.revokeObjectURL(url);
    } catch (err) {
      console.error('Failed to download folder:', err);
    }
  }, []);

  return (
    <FileContext.Provider
      value={{
        folders,
        foldersLoading,
        refreshFolders,
        treeFileNodes,
        treeFilesLoading,
        loadTreeFiles,
        refreshTreeFiles,
        updateTreeFileNode,
        expandedFolderIds,
        toggleFolderExpanded,
        handleCreateFolder,
        handleRenameFolder,
        handleDeleteFolder,
        handleMoveFolder,
        handleMoveFolders,
        handleMoveFile,
        handleMoveFiles,
        handleDownloadFolder,
        handleFileDelete,
        handleFileUpload,
        handleFilePreview: handleFilePreview as FileContextType['handleFilePreview'],
        handleFileDownload: async (fileId: string, fileName: string) => {
          await handleFileDownload(fileId, fileName);
        },
        uploadingFile,
        deletingFiles,
        previewingFiles,
        downloadingFiles,
      }}
    >
      {children}
    </FileContext.Provider>
  );
}

export function useFileContext() {
  const context = useContext(FileContext);
  if (context === undefined) {
    throw new Error('useFileContext must be used within a FileProvider');
  }
  return context;
}
