import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logger/logger.dart';

import 'package:calibre_web_companion/features/book_view/bloc/book_view_event.dart';
import 'package:calibre_web_companion/features/book_view/bloc/book_view_state.dart';

import 'package:calibre_web_companion/features/book_view/data/repositories/book_view_repository.dart';

class BookViewBloc extends Bloc<BookViewEvent, BookViewState> {
  final BookViewRepository repository;
  final Logger logger;

  static const int _seriesFolderLimit = 1000;

  BookViewBloc({required this.repository, required this.logger})
    : super(const BookViewState()) {
    on<LoadViewSettings>(_onLoadSettings);
    on<LoadBooks>(_onLoadBooks);
    on<LoadMoreBooks>(_onLoadMoreBooks);
    on<RefreshBooks>(_onRefreshBooks);
    on<ChangeSort>(_onChangeSort);
    on<EnterSeriesFolderMode>(_onEnterSeriesFolderMode);
    on<SearchBooks>(_onSearchBooks);
    on<UploadBook>(_onUploadBook);
    on<ChangeColumnCount>(_onChangeColumnCount);
    on<SetViewMode>(_onSetViewMode);
    on<UploadCancel>(_onUploadCancel);
    on<ResetUploadStatus>(_onResetUploadStatus);
    on<ChangeLibrary>(_onChangeLibrary);
  }

  Future<void> _onLoadSettings(
    LoadViewSettings event,
    Emitter<BookViewState> emit,
  ) async {
    try {
      final columnCount = await repository.getColumnCount();
      final isListView = await repository.getIsListView();
      final caps = repository.getCapabilities();
      final libraries = repository.getLibraries();
      emit(
        state.copyWith(
          columnCount: columnCount,
          isListView: isListView,
          canAddBooks: caps.addBooks,
          canLookupMetadata: caps.metadataLookup,
          canBrowseSeriesFolders: caps.discover,
          multiLibrary: caps.multiLibrary && libraries.length > 1,
          libraries: libraries,
          currentLibraryId: repository.getCurrentLibraryId(),
        ),
      );
    } catch (e) {
      logger.e('Error loading settings: $e');
    }
  }

  Future<void> _onLoadBooks(
    LoadBooks event,
    Emitter<BookViewState> emit,
  ) async {
    emit(state.copyWith(isLoading: true, hasError: false));
    try {
      final isOpds = repository.getIsOpds();
      final caps = repository.getCapabilities();
      final limit = state.seriesFolderMode ? _seriesFolderLimit : state.limit;

      final books = await repository.fetchBooks(
        offset: 0,
        limit: limit,
        searchQuery: state.searchQuery,
        sortBy: state.sortBy,
        sortOrder: state.sortOrder,
      );

      emit(
        state.copyWith(
          isLoading: false,
          books: books,
          offset: limit,
          hasMoreBooks:
              state.seriesFolderMode
                  ? false
                  : (caps.pagination ? books.isNotEmpty : false),
          isOpds: isOpds,
        ),
      );
    } catch (e) {
      logger.e('Error loading books: $e');
      emit(
        state.copyWith(
          isLoading: false,
          hasError: true,
          errorMessage: e.toString(),
        ),
      );
    }
  }

  Future<void> _onLoadMoreBooks(
    LoadMoreBooks event,
    Emitter<BookViewState> emit,
  ) async {
    if (state.isLoading || !state.hasMoreBooks) return;

    emit(state.copyWith(isLoading: true));

    try {
      final moreBooks = await repository.fetchBooks(
        offset: state.offset,
        limit: state.limit,
        searchQuery: state.searchQuery,
        sortBy: state.sortBy,
        sortOrder: state.sortOrder,
      );

      final allBooks = [...state.books, ...moreBooks];

      emit(
        state.copyWith(
          books: allBooks,
          isLoading: false,
          hasMoreBooks: moreBooks.isNotEmpty,
          offset: state.offset + state.limit,
        ),
      );
    } catch (e) {
      logger.e('Error loading more books: $e');
      emit(
        state.copyWith(
          isLoading: false,
          hasError: true,
          errorMessage: e.toString(),
        ),
      );
    }
  }

  Future<void> _onRefreshBooks(
    RefreshBooks event,
    Emitter<BookViewState> emit,
  ) async {
    emit(
      state.copyWith(
        offset: 0,
        books: [],
        hasMoreBooks: true,
        isLoading: false,
        hasError: false,
        errorMessage: '',
      ),
    );

    add(const LoadBooks());
  }

  Future<void> _onChangeSort(
    ChangeSort event,
    Emitter<BookViewState> emit,
  ) async {
    logger.i('Sorting by ${event.sortBy} ${event.sortOrder}');

    emit(
      state.copyWith(
        sortBy: event.sortBy,
        sortOrder: event.sortOrder,
        seriesFolderMode: false,
        offset: 0,
        books: [],
        hasMoreBooks: true,
      ),
    );

    add(const LoadBooks());
  }

  Future<void> _onEnterSeriesFolderMode(
    EnterSeriesFolderMode event,
    Emitter<BookViewState> emit,
  ) async {
    emit(
      state.copyWith(
        sortBy: 'series',
        sortOrder: 'asc',
        seriesFolderMode: true,
        isLoading: true,
        offset: 0,
        books: [],
        hasMoreBooks: true,
      ),
    );

    add(const LoadBooks());
  }

  Future<void> _onSearchBooks(
    SearchBooks event,
    Emitter<BookViewState> emit,
  ) async {
    emit(
      state.copyWith(
        searchQuery: event.query,
        seriesFolderMode: false,
        offset: 0,
        books: [],
        hasMoreBooks: true,
      ),
    );

    add(const LoadBooks());
  }

  Future<void> _onUploadBook(
    UploadBook event,
    Emitter<BookViewState> emit,
  ) async {
    emit(
      state.copyWith(
        uploadStatus: UploadStatus.loading,
        hasError: false,
        errorMessage: '',
      ),
    );

    try {
      emit(state.copyWith(uploadStatus: UploadStatus.uploading));

      final result = await repository.uploadEbook(event.book);

      emit(
        state.copyWith(
          uploadStatus: result ? UploadStatus.success : UploadStatus.failed,
        ),
      );

      if (result) {
        add(const RefreshBooks());
      }
    } catch (e) {
      logger.e('Error uploading book: $e');
      emit(
        state.copyWith(
          uploadStatus: UploadStatus.failed,
          hasError: true,
          errorMessage: e.toString(),
        ),
      );
    }
  }

  Future<void> _onChangeColumnCount(
    ChangeColumnCount event,
    Emitter<BookViewState> emit,
  ) async {
    try {
      await repository.setColumnCount(event.count);
      await repository.setIsListView(false);
      emit(state.copyWith(columnCount: event.count, isListView: false));
    } catch (e) {
      logger.e('Error changing column count: $e');
    }
  }

  Future<void> _onSetViewMode(
    SetViewMode event,
    Emitter<BookViewState> emit,
  ) async {
    try {
      await repository.setIsListView(event.isListView);
      emit(state.copyWith(isListView: event.isListView));
    } catch (e) {
      logger.e('Error changing view mode: $e');
    }
  }

  Future<void> _onChangeLibrary(
    ChangeLibrary event,
    Emitter<BookViewState> emit,
  ) async {
    if (event.libraryId == state.currentLibraryId) return;

    await repository.setCurrentLibraryId(event.libraryId);
    emit(
      state.copyWith(
        currentLibraryId: event.libraryId,
        books: [],
        offset: 0,
        hasMoreBooks: true,
        searchQuery: '',
      ),
    );
    add(const LoadBooks());
  }

  void _onUploadCancel(UploadCancel event, Emitter<BookViewState> emit) {
    emit(state.copyWith(uploadStatus: UploadStatus.initial));
  }

  void _onResetUploadStatus(
    ResetUploadStatus event,
    Emitter<BookViewState> emit,
  ) {
    emit(state.copyWith(uploadStatus: UploadStatus.initial));
  }
}
