import 'dart:async';

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

import '../Entities/drift_database.dart';
import '../Entities/time_frame_enum.dart';
import '../PlatformDependent/Web/date_utils_web.dart'
    if (dart.library.io) 'package:flutter/material.dart';
import '../PlatformDependent/Web/update_transactions.dart'
    if (dart.library.io) '../PlatformDependent/Native/update_transactions.dart';
import '../Repositories/recurring_transacation_repository.dart';
import '../Repositories/transaction_repository.dart';
import 'task.dart';

/// Generiert neue Transaktionen
/// anhand der erstellten wiederkehrenden Transaktionen
class GenerateTransactionsTask extends Task {
  final TransactionRepository _transactionRepository = TransactionRepository();
  final RecurringTransactionRepository _recurringTransactionRepository =
      RecurringTransactionRepository();

  final Logger _logger = Logger();

  @override
  Future<bool> execute() async {
    _logger.i('Generating Transactions of recurring Transactions...');

    final List<RecurringTransaction> recurringTransactions =
        await _recurringTransactionRepository.findBy();

    for (final recurringTransaction in recurringTransactions) {
      _logger.i('Generating Transactions of ${recurringTransaction.name}...');

      final List<Transaction> transactions = await _transactionRepository
          .findBy(
            recurringTransaction: recurringTransaction,
            orderBy: 'dateDesc',
          );
      final Transaction? transaction = transactions.firstOrNull;

      await _generateTransactions(
        recurringTransaction,
        transaction?.date,
        number: transactions.length,
      );
    }

    _logger.i('Generating transactions completed.');

    updateTransactions();

    return true;
  }

  Future<bool> _generateTransactions(
    final RecurringTransaction recurringTransaction,
    final DateTime? lastTransactionDate, {
    final int number = 0,
  }) async {
    final DateTime newTransactionDate;

    if (lastTransactionDate != null) {
      switch (recurringTransaction.timeFrame) {
        case TimeFrameEnum.daily:
          newTransactionDate = lastTransactionDate.add(const Duration(days: 1));
        case TimeFrameEnum.weekly:
          newTransactionDate = lastTransactionDate.add(const Duration(days: 7));
        case TimeFrameEnum.monthly:
          newTransactionDate = DateUtils.addMonthsToMonthDate(
            lastTransactionDate,
            1,
          );
        case TimeFrameEnum.yearly:
          newTransactionDate = DateUtils.addMonthsToMonthDate(
            lastTransactionDate,
            12,
          );
      }
    } else {
      newTransactionDate = recurringTransaction.startDate!;
    }

    _logger.i('New transaction-date is $newTransactionDate');

    final DateTime endOfMonth = DateTime(
      DateTime.now().year,
      DateTime.now().month + 1,
      0,
    );

    if (endOfMonth.compareTo(newTransactionDate) >= 0) {
      _logger.i('$newTransactionDate is before $endOfMonth');

      final TransactionsCompanion transaction = TransactionsCompanion(
        name: Value('${recurringTransaction.name} #$number'),
        date: Value(newTransactionDate),
        amount: Value(recurringTransaction.amount),
        checked: const Value(false),
        accountId: Value(recurringTransaction.accountId),
        recurringTransactionId: Value(recurringTransaction.id),
      );

      _logger.i(
        'Adding transaction ${transaction.name.value}'
        ' on ${transaction.date.value}',
      );

      await _transactionRepository.add(transaction);

      return _generateTransactions(
        recurringTransaction,
        newTransactionDate,
        number: number + 1,
      );
    } else {
      _logger.i('$newTransactionDate is after $endOfMonth');
      return true;
    }
  }
}
