import 'dart:async';
import 'dart:io';

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

part 'account_event.dart';
part 'account_state.dart';

final colors = [
  'FF74feff',
  'FF64c0ff',
  'FF5873fe',
  'FF4b4cff',
  'FFc0fcfd',
  'FFa7caff',
  'FF8d7efd',
  'FF7f65fe',
  'FFe7ffff',
  'FFd7dafd',
  'FFd8a6ff',
  'FFc065fe',
  'FFffffff',
  'FFffe6fe',
  'FFffbdff',
  'FFff80fe',
];

class AccountBloc extends Bloc<AccountEvent, AccountState> {
  final TransactionsRepository _transactionsRepository;
  final MetadataRepository _metadataRepository;

  AccountBloc({
    required MetadataRepository metadataRepository,
    required TransactionsRepository transactionsRepository,
  })
  : _metadataRepository = metadataRepository,
    _transactionsRepository = transactionsRepository,
    super(const AccountState()
  ) {
    on<AccountImportCSV>(_onAccountImportCSV);
    on<AccountLoad>(_onAccountLoad);
    // on<AccountExportJSON>(_onAccountImportJSON);
    // on<AccountExportCSV>(_onAccountImportJSON);
    on<AccountAdd>(_onAccountAdd);
    on<AccountRemove>(_onAcountRemove);
    on<AccountEditLabel>(_onAccountEditLabel);
    on<AccountEditSaving>(_onAccountEditSaving);
    on<AccountEditColor>(_onAccountEditColor);
    on<ClearData>(_onClearData);

    _metadataRepository
      .getAccountsStream()
      .listen((subAccounts) => add(AccountLoad(subAccounts)));
  }

  double _universalConvertToDouble(dynamic value) {
    if (value is String) {
      return double.parse(value);
    } else if (value is int) {
      return value.toDouble();
    } else if (value is double) {
      return value;
    } else {
      throw Error();
    }
  }

  _onAccountImportCSV(AccountImportCSV event, Emitter<AccountState> emit) async {
    int colorIndex = 0;
    FilePickerResult? result = await FilePicker.platform.pickFiles();

    final csvPath = result?.files.first.path;
    if (csvPath != null) {
      final File csv = File(csvPath);
      final String csvFileContent = await csv.readAsString();
      final List<List<dynamic>> csvList = const CsvToListConverter(fieldDelimiter: '|', eol: '\n').convert(csvFileContent);

      final Map<String, Category> categoriesMap = {};
      final Map<String, Account> accounts = {};

      final transactions = csvList
        .map((line) {
          
          double value = _universalConvertToDouble(line[4]);
          String? categoryLabel = line[1];
          if (categoryLabel == null || categoryLabel == '') {
            categoryLabel = 'N/A';
          }

          if (categoriesMap[categoryLabel] == null) {
            // if (categoryLabel == 'N/A') {
            //   categoriesMap[categoryLabel] = Category(label: 'N/A', color: 'FFFF0000' );
            // } else {
            //   String color = colorIndex >= colors.length ? 'FF0000FF' : colors[colorIndex];
            //   colorIndex++;
            //   categoriesMap[categoryLabel] = Category(label: categoryLabel, color: color );
            // }

            categoriesMap[categoryLabel] = Category(label: categoryLabel, color: value > 0 ? 'FF21E297' : 'FFFFB4AB' );
          }

          String accountLabel = line[3];
          if (accounts[accountLabel] == null) {
            accounts[accountLabel] = Account(label: accountLabel, color: 'FF74feff');
          }

          return Transaction(
            uuid: const Uuid().v8(),
            date: DateTime.parse(line[0]),
            category: categoryLabel,
            description: line[2],
            account: line[3],
            value: value
          );
        })
        .toList();

      _metadataRepository.deleteMetadata();
      _transactionsRepository.deleteTransactions();

      _metadataRepository.saveAccounts(accounts.values.toList());
      _metadataRepository.saveBudgets([]);
      _metadataRepository.saveCategories(categoriesMap.values.toList());
      _transactionsRepository.saveTransactions(transactions);
    }
  }

  _onAccountLoad(
    AccountLoad event, Emitter<AccountState> emit
  ) {
    emit(
      state.copyWith(event.subAccounts)
    );
  }

  _onAccountAdd(AccountAdd event, Emitter<AccountState> emit) {
    String uuid = const Uuid().v8();
    Account account = Account(
      label: 'Account $uuid',
      color: 'FF74feff',
      saving: false
    );
    emit(
      state.copyWith(_saveAccount(account))
    );
  }

  _onAcountRemove(AccountRemove event, Emitter<AccountState> emit) {
    Account accountToRemove = event.account;
    List<Account> accounts = state.accounts;
    List<Transaction> transactions = _transactionsRepository.getTransactions();

    if (transactions.any((transaction) => transaction.account == accountToRemove.label)) {
      emit(AccountRemoveFail());
      emit(AccountState(accounts: accounts));
    } else {
      accounts.removeWhere((account) => account.label == accountToRemove.label);
      emit(AccountRemoveSucess());
      emit(
        state.copyWith(_metadataRepository.saveAccounts(accounts))
      );
    }
  }

  _onAccountEditLabel(AccountEditLabel event, Emitter<AccountState> emit) {
    Account account = event.account;
    // TODO check for existance, rename every transaction
  }

  _onAccountEditSaving(AccountEditSaving event, Emitter<AccountState> emit) {
    Account account = event.account;
    account.saving = event.saving;
    emit(
      state.copyWith(_saveAccount(account))
    );
  }

  _onAccountEditColor(AccountEditColor event, Emitter<AccountState> emit) {
    Account account = event.account;
    account.color = event.color;
    emit(
      state.copyWith(_saveAccount(account))
    );
  }

  List<Account> _saveAccount(Account accountToSave) {
    List<Account> accounts = _metadataRepository.getAccounts();

    try {
      Account accountFound = accounts.firstWhere((account) => account.label == accountToSave.label);
      accountFound.color = accountToSave.color;
      accountFound.saving = accountToSave.saving;
    } catch (e) {
      if (accounts.isEmpty) {
        accounts = [accountToSave];
      } else {
        accounts.add(accountToSave);
      }
    }

    _metadataRepository.saveAccounts(accounts);
    return accounts;
  }

  FutureOr<void> _onClearData(ClearData event, Emitter<AccountState> emit) {
    _metadataRepository.deleteMetadata();
    _transactionsRepository.deleteTransactions();
  }
}
