import 'dart:async';
import 'dart:isolate';

import 'package:drift/drift.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:routemaster/routemaster.dart';

import '../Entities/drift_database.dart';
import '../Pages/Dialog/dialog_action.dart';
import '../Pages/Dialog/dialog_input_field.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/transaction_repository.dart';
import '../Services/navigation_service.dart';
import '../Services/transaction_service.dart';
import 'account_controller.dart';
import 'port_controller.dart';

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

  /// Erstellt eine neue Instanz dieser Klasse
  TransactionController._internal() {
    _newTransactionDialog = DynamicDialog(
      title: 'Neue Transaktion erstellen',
      icon: Icons.swap_horiz,
      inputFields: [
        const DialogInputField(id: 'name', label: 'Name', autoFocus: true),
        DialogInputField(
          id: 'date',
          label: 'Transaktionsdatum',
          keyboardType: TextInputType.datetime,
          inputType: DialogInputFieldTypeEnum.date,
          initialValue: DateTime.now(),
        ),
        const DialogInputField(
          id: 'amount',
          label: 'Betrag €',
          keyboardType: TextInputType.number,
        ),
      ],
      actions: [
        DialogAction(label: 'Abbruch'),
        DialogAction(
          label: 'Speichern',
          isPrimary: true,
          onPressed: _saveNewTransaction,
        ),
      ],
    );

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

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

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

    if (!kIsWeb) {
      final ReceivePort updateTransactionsReceivePort = ReceivePort()
        ..listen((_) {
          Logger().i('Received update-transactions signal');
          unawaited(updateTransactions());
        });

      PortController().addPort(
        updateTransactionsReceivePort.sendPort,
        'update-transactions',
      );

      final ReceivePort gotToTransactionsReceivePort = ReceivePort()
        ..listen((final value) {
          Logger().i('Received go-to-transactions signal');

          final List<Transaction> transactions =
              TransactionService.transactionsFromString(value);

          goToTransactions(transactions: transactions);
        });

      PortController().addPort(
        gotToTransactionsReceivePort.sendPort,
        'go-to-transactions',
      );
    }

    unawaited(updateTransactions());
  }

  static final TransactionController _instance =
      TransactionController._internal();
  final TransactionRepository _transactionRepository = TransactionRepository();
  final AccountController _accountController = AccountController();

  DynamicDialog? _newTransactionDialog;
  DynamicDialog? _errorTransactionValueEmptyDialog;
  DynamicDialog? _transactionCreatedDialog;

  final ValueNotifier<List<Transaction>> _transactions =
      ValueNotifier<List<Transaction>>([]);

  /// Stellt die Liste der Transaktionen dar
  ValueNotifier<List<Transaction>> get transactions {
    if (_transactions.value == []) {
      unawaited(updateTransactions());
    }

    return _transactions;
  }

  set transactions(final List<Transaction> transactions) {
    _transactions.value = transactions;
  }

  Account? _selectedAccount;

  /// Aktualisiert die Transaktionen in der internen Liste
  Future<void> updateTransactions() async {
    if (_selectedAccount != null) {
      final List<Transaction> transactions = await _transactionRepository
          .findBy(account: _selectedAccount, orderBy: 'dateDesc');

      _transactions.value = transactions;
    }
  }

  /// Wechselt zur Übersicht über die Transaktionen
  void goToTransactions({final List<Transaction>? transactions}) {
    final BuildContext? context = NavigationService.getCurrentBuildContext();

    if (context != null && transactions != null) {
      if (transactions.length == 1) {
        Routemaster.of(
          context,
        ).push('/trend', queryParameters: {'name': transactions.first.name});
      }

      Routemaster.of(context).replace('/trend');
    }
  }

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

  /// Startet den Prozess, um eine Transaktion zu bearbeiten
  Future<void> editTransactionHandler(final int transactionId) async {
    final Transaction? transaction = await _transactionRepository.find(
      transactionId,
    );

    if (transaction != null) {
      final String title;
      final String submitButtonText;

      if (transaction.checked) {
        title = '${transaction.name} bearbeiten';
        submitButtonText = 'Bearbeiten';
      } else {
        title = '${transaction.name} prüfen';
        submitButtonText = 'Prüfen';
      }

      final editTransactionDialog = DynamicDialog(
        title: title,
        icon: Icons.edit,
        inputFields: [
          DialogInputField(
            id: 'name',
            label: 'Name',
            autoFocus: true,
            initialValue: transaction.name,
          ),
          DialogInputField(
            id: 'date',
            label: 'Transaktionsdatum',
            keyboardType: TextInputType.datetime,
            inputType: DialogInputFieldTypeEnum.date,
            initialValue: transaction.date,
          ),
          DialogInputField(
            id: 'amount',
            label: 'Betrag €',
            keyboardType: TextInputType.number,
            initialValue: transaction.amount.toString(),
          ),
        ],
        actions: [
          DialogAction(label: 'Abbruch'),
          DialogAction(
            label: submitButtonText,
            isPrimary: true,
            onPressed: _editTransaction,
          ),
        ],
        hiddenValues: {'transaction': transaction},
      );
      unawaited(editTransactionDialog.show());
    }
  }

  /// Startet den Prozess, um eine Transaktion zu löschen
  Future<void> deleteTransactionHandler(final int transactionId) async {
    final Transaction? transaction = await _transactionRepository.find(
      transactionId,
    );

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

  Future<void> _saveNewTransaction(final Map<String, dynamic> values) async {
    if (values['name'] == null ||
        values['name'] == '' ||
        values['date'] == null ||
        values['date'] == '' ||
        values['amount'] == null ||
        values['amount'] == '' ||
        _selectedAccount == null) {
      await _errorTransactionValueEmptyDialog?.show();
    } else {
      final String amount = values['amount'];
      values['amount'] = double.tryParse(amount);

      if (values['amount'] == null || values['amount'] == 0) {
        await _errorTransactionValueEmptyDialog?.show();
      } else {
        final transaction = TransactionsCompanion(
          name: Value(values['name']),
          date: Value(values['date']),
          amount: Value(values['amount']),
          accountId: Value(_selectedAccount!.id),
        );

        await _transactionRepository.add(transaction);
        await _transactionCreatedDialog?.show();

        await updateTransactions();
      }
    }
  }

  Future<void> _editTransaction(final Map<String, dynamic> values) async {
    if (values['transaction'] != null &&
        values['transaction'] != null &&
        values['date'] != null &&
        values['date'] != '' &&
        values['amount'] != null &&
        values['amount'] != '') {
      final String amount = values['amount'];
      values['amount'] = double.tryParse(amount);

      if (values['amount'] != null && values['amount'] != 0) {
        final Transaction transaction = values['transaction'];
        final rtc = TransactionsCompanion(
          id: Value(transaction.id),
          name: Value(values['name']),
          date: Value(values['date']),
          amount: Value(values['amount']),
          accountId: Value(transaction.accountId),
          checked: const Value(true),
        );

        await _transactionRepository.update(rtc);
        await updateTransactions();
      }
    }
  }

  Future<void> _deleteTransaction(final Map<String, dynamic> values) async {
    if (values['transaction'] != null) {
      final Transaction transaction = values['transaction'];
      await _transactionRepository.remove(transaction);

      await updateTransactions();
    }
  }
}
