import 'dart:async';
import 'dart:ui';

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krezus/repositories/metadata/metadata_repository.dart';
import 'package:krezus/repositories/metadata/models/category.dart';
import 'package:krezus/repositories/transactions/models/transaction.dart';
import 'package:krezus/repositories/transactions/transactions_repository.dart';
import 'package:uuid/uuid.dart';

part 'category_event.dart';
part 'category_state.dart';

class CategoryBloc extends Bloc<CategoryEvent, CategoryState> {
  final MetadataRepository _metadataRepository;
  final TransactionsRepository _transactionsRepository;

  CategoryBloc({
    required MetadataRepository metadataRepository, 
    required TransactionsRepository transactionsRepository,
  }) 
  : _metadataRepository = metadataRepository, 
    _transactionsRepository = transactionsRepository,
    super(const CategoryState())  {
    on<CategoriesLoad>(_onCategoriesLoad);
    on<CategoryEditColor>(_onCategoryEditColor);
    on<CategoryEditTransfert>(_onCategoryEditTransfert);
    on<CategoryEditEssential>(_onCategoryEditEssential);
    on<CategoryEditLabel>(_onCategoryEditLabel);
    on<CategoryRemove>(_onCategoryRemove);
    on<CategoryAdd>(_onCategoryAdd);

    _metadataRepository
      .getCategoriesStream()
      .listen((categories) => add(CategoriesLoad(categories)));
  }

  _onCategoriesLoad(
    CategoriesLoad event, Emitter<CategoryState> emit
  ) {
    emit(_computeState(event.categories));
  }

  FutureOr<void> _onCategoryEditColor(CategoryEditColor event, Emitter<CategoryState> emit) {
    Category category = event.category;
    category.color = event.color;
    emit(_computeState(_saveCategory(category)));
  }

  FutureOr<void> _onCategoryEditTransfert(CategoryEditTransfert event, Emitter<CategoryState> emit) {
    Category category = event.category;
    category.transfert = event.transfert;
    emit(_computeState(_saveCategory(category)));
  }

  FutureOr<void> _onCategoryEditEssential(CategoryEditEssential event, Emitter<CategoryState> emit) {
    Category category = event.category;
    category.essential = event.essential;
    emit(_computeState(_saveCategory(category)));
  }

  FutureOr<void> _onCategoryEditLabel(CategoryEditLabel event, Emitter<CategoryState> emit) {
    // TODO check for existance, rename every transaction
  }

  FutureOr<void> _onCategoryRemove(CategoryRemove event, Emitter<CategoryState> emit) {
    CategoryState originalCategoryState = state.copyWith();
    Category categoryToRemove = event.category;
    List<Category> categories = state.categories;
    List<Transaction> transactions = _transactionsRepository.getTransactions();

    if (transactions.any((transaction) => transaction.category == categoryToRemove.label)) {
      emit(CategoryRemoveFail());
      emit(originalCategoryState);
    } else {
      categories.removeWhere((category) => category.label == categoryToRemove.label);
      emit(CategoryRemoveSucess());
      emit(_computeState(_metadataRepository.saveCategories(categories)));
    }
  }

  FutureOr<void> _onCategoryAdd(CategoryAdd event, Emitter<CategoryState> emit) {
    String uuid = const Uuid().v8();
    Category category = Category(
      label: 'Category $uuid',
      color: 'FF74feff',
    );

    emit(_computeState(_saveCategory(category)));
  }

  List<Category> _saveCategory(Category categoryToSave) {
    List<Category> categories = _metadataRepository.getCategories();

    try {
      Category categoryFound = categories.firstWhere((category) => category.label == categoryToSave.label);
      categoryFound.color = categoryToSave.color;
      categoryFound.essential = categoryToSave.essential;
      categoryFound.transfert = categoryToSave.transfert;
    } catch (e) {
      if (categories.isEmpty) {
        categories = [categoryToSave];
      } else {
        categories.add(categoryToSave);
      }
    }

    _metadataRepository.saveCategories(categories);
    return categories;
  }

  CategoryState _computeState(List<Category> categories) {
    return state.copyWith(
      categories: categories,
      categoriesColors: { for (var category in categories) category.label : category.rgbToColor() }
    );
  }
}
