import 'dart:async';

import 'package:drift/drift.dart';
import 'package:flutter/material.dart';

import '../Entities/drift_database.dart';
import '../Entities/time_frame_enum.dart';
import '../Pages/Dialog/dialog_action.dart';
import '../Pages/Dialog/dialog_input_field.dart';
import '../Pages/Dialog/dialog_input_field_select_item.dart';
import '../Pages/Dialog/dialog_input_field_type_enum.dart';
import '../Pages/Dialog/dialog_type_enum.dart';
import '../Pages/Dialog/dynamic_dialog.dart';
import '../Repositories/recurring_transacation_repository.dart';
import 'account_controller.dart';
import 'background_task_controller.dart';

/// Steuert die Interaktion mit den wiederkehrenden Transaktionen
class RecurringTransactionController {
  /// Gibt die aktuell gültige Instanz der Klasse zurück
  factory RecurringTransactionController() => _instance;

  /// Erstellt eine neue Instanz dieser Klasse
  RecurringTransactionController._internal() {
    _newRecurringTransactionDialog = DynamicDialog(
      title: 'Neue wiederkehrende Transaktion erstellen',
      icon: Icons.repeat,
      inputFields: [
        const DialogInputField(id: 'name', label: 'Name', autoFocus: true),
        const DialogInputField(
          id: 'startDate',
          label: 'Anfangsdatum',
          keyboardType: TextInputType.datetime,
          inputType: DialogInputFieldTypeEnum.date,
        ),
        DialogInputField(
          id: 'timeFrame',
          label: 'Zeitraum',
          inputType: DialogInputFieldTypeEnum.select,
          selectItems: TimeFrameEnum.values
              .map(
                (final value) => DialogInputFieldSelectItem(
                  id: value.index,
                  value: value.name,
                ),
              )
              .toList(),
        ),
        const DialogInputField(
          id: 'amount',
          label: 'Betrag €',
          keyboardType: TextInputType.number,
        ),
      ],
      actions: [
        DialogAction(label: 'Abbruch'),
        DialogAction(
          label: 'Speichern',
          isPrimary: true,
          onPressed: _saveNewRecurringTransaction,
        ),
      ],
    );

    _errorRecurringTransactionValueEmptyDialog = DynamicDialog(
      title: 'Fehler!',
      icon: Icons.error,
      content: const Text('Es wurden nicht alle Werte eingetragen!'),
      dialogType: DialogTypeEnum.error,
    );

    _recurringTransactionCreatedDialog = DynamicDialog(
      title: 'Wiederkehrende Transaktion erstellt!',
      icon: Icons.check_circle,
      content: const Text(
        'Die wiederkehrende Transaktion wurde erfolgreich erstellt!',
      ),
      dialogType: DialogTypeEnum.success,
    );

    _selectedAccount = _accountController.selected.value;
    _accountController.selected.addListener(() {
      _selectedAccount = _accountController.selected.value;
      unawaited(updateRecurringTransactions());
    });

    unawaited(updateRecurringTransactions());
  }

  static final RecurringTransactionController _instance =
      RecurringTransactionController._internal();
  final RecurringTransactionRepository _recurringTransactionRepository =
      RecurringTransactionRepository();
  final AccountController _accountController = AccountController();

  DynamicDialog? _newRecurringTransactionDialog;
  DynamicDialog? _errorRecurringTransactionValueEmptyDialog;
  DynamicDialog? _recurringTransactionCreatedDialog;

  final ValueNotifier<List<RecurringTransaction>> _recurringTransactions =
      ValueNotifier<List<RecurringTransaction>>([]);

  /// Stellt die Liste der wiederkehrenden Transaktionen dar
  ValueNotifier<List<RecurringTransaction>> get recurringTransactions {
    if (_recurringTransactions.value == []) {
      unawaited(updateRecurringTransactions());
    }

    return _recurringTransactions;
  }

  set recurringTransactions(
    final List<RecurringTransaction> recurringTransactions,
  ) {
    _recurringTransactions.value = recurringTransactions;
  }

  Account? _selectedAccount;

  /// Aktualisiert die gespeicherten wiederkehrenden Transaktionen
  /// in der internen Liste.
  Future<void> updateRecurringTransactions() async {
    if (_selectedAccount != null) {
      final List<RecurringTransaction> recurringTransactions =
          await _recurringTransactionRepository.findBy(
            account: _selectedAccount,
            orderBy: 'nameAsc',
          );

      _recurringTransactions.value = recurringTransactions;
    }
  }

  /// Startet den Prozess, um eine neue wiederkehrende Transaktion anzulegen
  void newRecurringTransactionHandler() {
    unawaited(_newRecurringTransactionDialog?.show());
  }

  /// Startet den Prozess, um eine wiederkehrende Transaktion zu bearbeiten
  Future<void> editRecurringTransactionHandler(
    final int recurringTransactionId,
  ) async {
    final RecurringTransaction? recurringTransaction =
        await _recurringTransactionRepository.find(recurringTransactionId);

    if (recurringTransaction != null) {
      final editRecurringTransactionDialog = DynamicDialog(
        title: '${recurringTransaction.name} bearbeiten',
        icon: Icons.edit,
        inputFields: [
          DialogInputField(
            id: 'name',
            label: 'Name',
            autoFocus: true,
            initialValue: recurringTransaction.name,
          ),
          DialogInputField(
            id: 'startDate',
            label: 'Anfangsdatum',
            keyboardType: TextInputType.datetime,
            inputType: DialogInputFieldTypeEnum.date,
            initialValue: recurringTransaction.startDate,
          ),
          DialogInputField(
            id: 'timeFrame',
            label: 'Zeitraum',
            inputType: DialogInputFieldTypeEnum.select,
            selectItems: TimeFrameEnum.values
                .map(
                  (final value) => DialogInputFieldSelectItem(
                    id: value.index,
                    value: value.name,
                  ),
                )
                .toList(),
            initialValue: recurringTransaction.timeFrame,
          ),
          DialogInputField(
            id: 'amount',
            label: 'Betrag €',
            keyboardType: TextInputType.number,
            initialValue: recurringTransaction.amount.toString(),
          ),
        ],
        actions: [
          DialogAction(label: 'Abbruch'),
          DialogAction(
            label: 'Bearbeiten',
            isPrimary: true,
            onPressed: _editRecurringTransaction,
          ),
        ],
        hiddenValues: {'recurringTransaction': recurringTransaction},
      );
      unawaited(editRecurringTransactionDialog.show());
    }
  }

  /// Startet den Prozess, um eine wiederkehrende Transaktion zu löschen
  Future<void> deleteRecurringTransactionHandler(
    final int recurringTransactionId,
  ) async {
    final RecurringTransaction? recurringTransaction =
        await _recurringTransactionRepository.find(recurringTransactionId);

    if (recurringTransaction != null) {
      final deleteRecurringTransactionDialog = DynamicDialog(
        dialogType: DialogTypeEnum.error,
        title: '${recurringTransaction.name} löschen',
        content: Text(
          'Willst du ${recurringTransaction.name} wirklich löschen?',
        ),
        icon: Icons.delete_forever,
        actions: [
          DialogAction(label: 'Abbruch', isPrimary: true),
          DialogAction(
            label: 'Löschen',
            onPressed: _deleteRecurringTransaction,
          ),
        ],
        hiddenValues: {'recurringTransaction': recurringTransaction},
      );
      unawaited(deleteRecurringTransactionDialog.show());
    }
  }

  Future<void> _saveNewRecurringTransaction(
    final Map<String, dynamic> values,
  ) async {
    if (values['name'] == null ||
        values['name'] == '' ||
        values['startDate'] == null ||
        values['startDate'] == '' ||
        values['timeFrame'] == null ||
        values['timeFrame'] == '' ||
        values['amount'] == null ||
        values['amount'] == '' ||
        _selectedAccount == null) {
      await _errorRecurringTransactionValueEmptyDialog?.show();
    } else {
      final DialogInputFieldSelectItem timeFrame = values['timeFrame'];
      values['timeFrame'] = TimeFrameEnum.values[timeFrame.id];

      final String amount = values['amount'];
      values['amount'] = double.tryParse(amount);

      if (values['amount'] == null || values['amount'] == 0) {
        await _errorRecurringTransactionValueEmptyDialog?.show();
      } else {
        final recurringTransaction = RecurringTransactionsCompanion(
          name: Value(values['name']),
          startDate: Value(values['startDate']),
          timeFrame: Value(values['timeFrame']),
          amount: Value(values['amount']),
          accountId: Value(_selectedAccount!.id),
        );

        await _recurringTransactionRepository.add(recurringTransaction);
        await _recurringTransactionCreatedDialog?.show();

        await updateRecurringTransactions();
        unawaited(BackgroundTaskController.run('generate_transactions'));
      }
    }
  }

  Future<void> _editRecurringTransaction(
    final Map<String, dynamic> values,
  ) async {
    if (values['recurringTransaction'] != null &&
        values['recurringTransaction'] != null &&
        values['startDate'] != null &&
        values['startDate'] != '' &&
        values['timeFrame'] != null &&
        values['timeFrame'] != '' &&
        values['amount'] != null &&
        values['amount'] != '') {
      final DialogInputFieldSelectItem timeFrame = values['timeFrame'];
      values['timeFrame'] = TimeFrameEnum.values[timeFrame.id];

      final String amount = values['amount'];
      values['amount'] = double.tryParse(amount);

      if (values['amount'] != null && values['amount'] != 0) {
        final RecurringTransaction recurringTransaction =
            values['recurringTransaction'];
        final rtc = RecurringTransactionsCompanion(
          id: Value(recurringTransaction.id),
          name: Value(values['name']),
          startDate: Value(values['startDate']),
          timeFrame: Value(values['timeFrame']),
          amount: Value(values['amount']),
          accountId: Value(recurringTransaction.accountId),
        );

        await _recurringTransactionRepository.update(rtc);
        await updateRecurringTransactions();
      }
    }
  }

  Future<void> _deleteRecurringTransaction(
    final Map<String, dynamic> values,
  ) async {
    if (values['recurringTransaction'] != null) {
      final RecurringTransaction recurringTransaction =
          values['recurringTransaction'];
      await _recurringTransactionRepository.remove(recurringTransaction);

      await updateRecurringTransactions();
    }
  }
}
