import 'package:bloc/bloc.dart';
import 'file_browser_event.dart';
import 'file_browser_state.dart';
import '../../services/file_service.dart';
import '../../models/file_item.dart';
import '../../models/api_error.dart';
import 'package:path/path.dart' as p;

class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
  final FileService _fileService;
  String _currentOrgId = '';
  String _currentPath = '/';
  List<FileItem> _currentFiles = [];
  List<FileItem> _filteredFiles = [];
  int _currentPage = 1;
  int _pageSize = 20;
  String _sortBy = 'name';
  bool _isAscending = true;
  String _currentFilter = '';

  FileBrowserBloc(this._fileService) : super(DirectoryInitial()) {
    on<LoadDirectory>(_onLoadDirectory);
    on<NavigateToFolder>(_onNavigateToFolder);
    on<RefreshDirectory>(_onRefreshDirectory);
    on<ApplySort>(_onApplySort);
    on<ApplyFilter>(_onApplyFilter);
    on<CreateFolder>(_onCreateFolder);
    on<MoveFile>(_onMoveFile);
    on<RenameFile>(_onRenameFile);
    on<DeleteFile>(_onDeleteFile);
    on<ResetFileBrowser>(_onResetFileBrowser);
    on<LoadPage>(_onLoadPage);
    on<ChangePageSize>(_onChangePageSize);
    on<SearchFiles>(_onSearchFiles);
  }

  String _getErrorMessage(dynamic error) {
    if (error is ApiError) {
      switch (error.code) {
        case 'UNAUTHENTICATED':
          return 'Session expired. Please log in again.';
        case 'PERMISSION_DENIED':
          return 'You do not have access to this folder.';
        case 'NOT_FOUND':
          return 'The requested folder or file was not found.';
        case 'CONFLICT':
          return 'A conflict occurred. Please try again.';
        case 'INVALID_ARGUMENT':
          return 'Invalid input provided.';
        case 'INTERNAL':
        default:
          return 'An internal error occurred. Please try again later.';
      }
    }
    return error.toString();
  }

  void _onLoadDirectory(
    LoadDirectory event,
    Emitter<FileBrowserState> emit,
  ) async {
    emit(DirectoryLoading());
    _currentOrgId = event.orgId;
    _currentPath = event.path;
    try {
      final files = await _fileService.getFiles(event.orgId, event.path);
      _currentFiles = _sortFiles(files, _sortBy, _isAscending);
      _filteredFiles = _currentFiles;
      _currentFilter = '';
      _currentPage = 1;
      if (files.isEmpty) {
        emit(DirectoryEmpty(_currentPath));
      } else {
        _emitLoadedState(emit);
      }
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _onNavigateToFolder(
    NavigateToFolder event,
    Emitter<FileBrowserState> emit,
  ) async {
    // Assume event.folderId is the path
    add(LoadDirectory(orgId: _currentOrgId, path: event.folderId));
  }

  void _onRefreshDirectory(
    RefreshDirectory event,
    Emitter<FileBrowserState> emit,
  ) async {
    add(LoadDirectory(orgId: _currentOrgId, path: _currentPath));
  }

  void _onApplySort(ApplySort event, Emitter<FileBrowserState> emit) {
    _sortBy = event.sortBy;
    _isAscending = event.isAscending;
    _currentFiles = _sortFiles(_currentFiles, _sortBy, _isAscending);
    _filteredFiles = _currentFiles
        .where((f) => f.name.toLowerCase().contains(_currentFilter))
        .toList();
    _currentPage = 1;
    _emitLoadedState(emit);
  }

  void _onApplyFilter(ApplyFilter event, Emitter<FileBrowserState> emit) {
    _currentFilter = event.filter.toLowerCase();
    _filteredFiles = _currentFiles
        .where((f) => f.name.toLowerCase().contains(_currentFilter))
        .toList();
    _currentPage = 1;
    _emitLoadedState(emit);
  }

  void _onCreateFolder(
    CreateFolder event,
    Emitter<FileBrowserState> emit,
  ) async {
    try {
      await _fileService.createFolder(
        event.orgId,
        event.parentPath,
        event.folderName,
      );
      // Reload directory to get the folder with proper ID from backend
      add(LoadDirectory(orgId: event.orgId, path: event.parentPath));
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _onMoveFile(MoveFile event, Emitter<FileBrowserState> emit) async {
    try {
      await _fileService.moveFile(
        event.orgId,
        event.sourcePath,
        event.targetPath,
      );
      // If moving into current directory, reload to get the new file details
      if (event.targetPath == _currentPath) {
        add(LoadDirectory(orgId: event.orgId, path: _currentPath));
      } else {
        // Remove the moved file if it was in current directory
        _currentFiles.removeWhere((f) => f.path == event.sourcePath);
        _filteredFiles = _currentFiles
            .where((f) => f.name.toLowerCase().contains(_currentFilter))
            .toList();
        _emitLoadedState(emit);
      }
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _onRenameFile(RenameFile event, Emitter<FileBrowserState> emit) async {
    try {
      await _fileService.renameFile(event.orgId, event.path, event.newName);
      final index = _currentFiles.indexWhere((f) => f.path == event.path);
      if (index != -1) {
        final oldItem = _currentFiles[index];
        final newPath = '${p.dirname(event.path)}/${event.newName}';
        final newItem = oldItem.copyWith(name: event.newName, path: newPath);
        _currentFiles[index] = newItem;
        _currentFiles = _sortFiles(_currentFiles, _sortBy, _isAscending);
        _filteredFiles = _currentFiles
            .where((f) => f.name.toLowerCase().contains(_currentFilter))
            .toList();
        _emitLoadedState(emit);
      }
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _onDeleteFile(DeleteFile event, Emitter<FileBrowserState> emit) async {
    try {
      await _fileService.deleteFile(event.orgId, event.path);
      // Create new list to trigger Equatable change detection
      _currentFiles = _currentFiles.where((f) => f.path != event.path).toList();
      _filteredFiles = _currentFiles
          .where((f) => f.name.toLowerCase().contains(_currentFilter))
          .toList();
      _emitLoadedState(emit);
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _onResetFileBrowser(
    ResetFileBrowser event,
    Emitter<FileBrowserState> emit,
  ) {
    _currentPath = '/';
    _currentFiles = [];
    _filteredFiles = [];
    _currentFilter = '';
    _currentPage = 1;
    _pageSize = 20;
    _sortBy = 'name';
    _isAscending = true;
  }

  void _onLoadPage(LoadPage event, Emitter<FileBrowserState> emit) {
    _currentPage = event.page;
    _emitLoadedState(emit);
  }

  void _onChangePageSize(ChangePageSize event, Emitter<FileBrowserState> emit) {
    _pageSize = event.pageSize;
    _currentPage = 1; // Reset to first page
    _emitLoadedState(emit);
  }

  void _onSearchFiles(SearchFiles event, Emitter<FileBrowserState> emit) async {
    emit(DirectoryLoading());
    try {
      final files = await _fileService.searchFiles(event.orgId, event.query);
      _currentFiles = files;
      _filteredFiles = files;
      _currentFilter = '';
      _currentPage = 1;
      if (files.isEmpty) {
        emit(DirectoryEmpty(_currentPath));
      } else {
        _emitLoadedState(emit);
      }
    } catch (e) {
      emit(DirectoryError(_getErrorMessage(e)));
    }
  }

  void _emitLoadedState(Emitter<FileBrowserState> emit) {
    final paginatedFiles = _getPaginatedFiles();
    final totalPages = (_filteredFiles.length / _pageSize).ceil();
    emit(
      DirectoryLoaded(
        files: _currentFiles,
        filteredFiles: _filteredFiles,
        paginatedFiles: paginatedFiles,
        breadcrumbs: _generateBreadcrumbs(_currentPath),
        currentPath: _currentPath,
        currentPage: _currentPage,
        pageSize: _pageSize,
        totalPages: totalPages,
        sortBy: _sortBy,
        isAscending: _isAscending,
      ),
    );
  }

  List<FileItem> _getPaginatedFiles() {
    final start = (_currentPage - 1) * _pageSize;
    final end = start + _pageSize;
    return _filteredFiles.sublist(start, end.clamp(0, _filteredFiles.length));
  }

  List<FileItem> _sortFiles(
    List<FileItem> files,
    String sortBy,
    bool isAscending,
  ) {
    final sorted = List<FileItem>.from(files);
    sorted.sort((a, b) {
      // Always put folders first, then files
      if (a.type != b.type) {
        return a.type == FileType.folder ? -1 : 1;
      }

      // Within the same type (both folders or both files), sort by the selected criterion
      switch (sortBy) {
        case 'name':
          return isAscending
              ? a.name.compareTo(b.name)
              : b.name.compareTo(a.name);
        case 'date':
          return isAscending
              ? a.lastModified.compareTo(b.lastModified)
              : b.lastModified.compareTo(a.lastModified);
        case 'size':
          return isAscending
              ? a.size.compareTo(b.size)
              : b.size.compareTo(a.size);
        case 'type':
          // Already handled above (folders vs files)
          return isAscending
              ? a.name.compareTo(b.name)
              : b.name.compareTo(a.name);
        default:
          return isAscending
              ? a.name.compareTo(b.name)
              : b.name.compareTo(a.name);
      }
    });
    return sorted;
  }

  List<Breadcrumb> _generateBreadcrumbs(String path) {
    final parts = path.split('/').where((p) => p.isNotEmpty).toList();
    final breadcrumbs = <Breadcrumb>[];
    String currentPath = '';
    for (final part in parts) {
      currentPath += '/$part';
      breadcrumbs.add(Breadcrumb(name: part, path: currentPath));
    }
    return breadcrumbs;
  }
}
