import 'dart:convert';

import 'package:logger/logger.dart';
import 'package:meta/meta.dart';
import 'package:redux/redux.dart';
import 'package:redux_thunk/redux_thunk.dart';
import 'package:satu/core/entity/category_entity.dart';
import 'package:satu/core/entity/goods_entity.dart';
import 'package:satu/core/entity/transaction_entity.dart';
import 'package:satu/core/entity/transaction_rec_entity.dart';
import 'package:satu/core/models/entity_data/transaction_data.dart';
import 'package:satu/core/models/flow/dao/product_dao.dart';
import 'package:satu/core/models/flow/transaction_state.dart';
import 'package:satu/core/redux/state/sell_state.dart';
import 'package:satu/core/services/db_service.dart';
import 'package:satu/core/utils/locator.dart';
import 'package:satu/core/utils/logger.dart';
import 'package:uuid/uuid.dart';

import '../store.dart';

@immutable
class SetSellStateAction {
  const SetSellStateAction(this.sellState);

  final SellState sellState;
}

final Logger log = getLogger('SetSellStateAction');

final DbService _dbService = locator<DbService>();

ThunkAction<AppState> counterOrEditSellItem(
    {required int transactionId, required double counter, double? price}) {
  return (Store<AppState> store) async {
    log.i(
      'counterSellItem id = $transactionId counter = $counter, price = $price',
    );
    final Map<String, dynamic>? map =
        await _dbService.queryById(transactionRecTableName, transactionId);

    if (map != null) {
      final TransactionRec transactionRec = TransactionRec.fromMap(map);
      final ProductDao item =
          ProductDao.fromMap(jsonDecode(transactionRec.data!));
      if (price != null) {
        item.price = price;
        item.count = counter;
      } else {
        final String val = ((item.count ?? 0) + counter).toStringAsFixed(5);
        item.count = double.parse(val);
      }
      transactionRec.data = jsonEncode(item.toMap());
      _dbService.update(transactionRecTableName, transactionRec.toMap());
    }
    // refresh from db ? after save data
    await loadSellData(store);
  };
}

ThunkAction<AppState> addSellItem({required Good good, String? excise}) {
  return (Store<AppState> store) async {
    log.i('addSellItem');
    final int? appCompanyId = store.state.userState!.auth!.companyId;

    Transaction? transaction;

    final List<Map<String, dynamic>> transactions =
        await _dbService.queryRowsWithWhere(
            transactionTableName,
            '$transactionColumnAppCompanyId = ? '
            ' and $transactionColumnStatus = ? '
            ' and $transactionColumnType = ?',
            [appCompanyId, transactionStatusPrepare, transactionTypeSell],
            orderBy: '$transactionColumnCreatedAt desc');
    if (transactions.isNotEmpty) {
      for (final Map<String, dynamic> map in transactions) {
        transaction = Transaction.fromMap(map);
      }
    }

    if (transaction == null) {
      final DateTime now = DateTime.now();
      const uuidTool = Uuid();
      final String uuid = uuidTool.v4();
      final TransactionData transactionData = TransactionData();
      transaction = Transaction()
        ..uuid = uuid
        ..status = transactionStatusPrepare
        ..appCompanyId = appCompanyId
        ..type = transactionTypeSell
        ..createdAt = now.toIso8601String()
        ..updatedAt = now.toIso8601String()
        ..data = jsonEncode(transactionData.toMap());
      final int returnKey =
          await _dbService.insert(transactionTableName, transaction.toMap());
      transaction.id = returnKey;
    }

    final List<Map<String, dynamic>> recs = await _dbService.queryRowsWithWhere(
        transactionRecTableName,
        '$transactionRecTransactionIdRef = ? ',
        [transaction.id]);
    ProductDao? item;
    if (recs.isNotEmpty) {
      for (final Map<String, dynamic> map in recs) {
        final TransactionRec transactionRec = TransactionRec.fromMap(map);
        final ProductDao _item =
            ProductDao.fromMap(jsonDecode(transactionRec.data!));
        if (_item.id == good.id) {
          log.i(map);
          _item.count = _item.count! + 1;
          transactionRec.data = jsonEncode(_item.toMap());
          await _dbService.update(
              transactionRecTableName, transactionRec.toMap());
          item = _item;
          break;
        }
      }
    }

    if (item == null) {
      item = ProductDao()
        ..id = good.id
        ..price = good.price!.toDouble()
        ..article = good.articul
        ..count = 1.0
        ..eanCode = good.ean
        ..productName = good.name
        ..excise = excise;
      //category add logic
      if (good.categoryId != null) {
        final List<Map<String, dynamic>> set = await _dbService
            .queryRowsWithWhere(categoryTableName, 'id = ?', [good.categoryId]);
        if (set.isNotEmpty) {
          final Category category = Category.fromMap(set.first);
          item.categoryId = category.id;
          item.categoryName = category.name;
        }
      }
      final TransactionRec rec = TransactionRec()
        ..data = jsonEncode(item.toMap())
        ..transactionId = transaction.id
        ..createdAt = DateTime.now().toIso8601String();
      log.i(rec.toMap());
      await _dbService.insert(transactionRecTableName, rec.toMap());
    }

    // refresh from db ? after save data
    await loadSellData(store);
  };
}

ThunkAction<AppState> removeSellItem({required int transactionId}) {
  return (Store<AppState> store) async {
    final List<Map<String, dynamic>> set = await _dbService
        .queryRowsWithWhere(transactionRecTableName, 'id = ?', [transactionId]);
    final TransactionRec rec = TransactionRec.fromMap(set.first);

    final int countdeleted =
        await _dbService.delete(transactionRecTableName, transactionId);

    final int? count = await _dbService.queryRowCount(transactionRecTableName,
        where: '$transactionRecTransactionIdRef = ${rec.transactionId}');
    if ((count ?? 0) < 1 && rec.transactionId != null) {
      await _dbService.delete(transactionTableName, rec.transactionId!);
    }

    log.i('removeSellItem $countdeleted '
        'last $count by transactionId:$transactionId');

    // refresh from db ? after save data
    await loadSellData(store);
  };
}

Future<void> removeAllSellData(Store<AppState> store) async {
  try {
    log.i('removeAllSellData');
    final int? appCompanyId = store.state.userState!.auth!.companyId;
    await _dbService.deleteByWhere(
        transactionTableName,
        '$transactionColumnAppCompanyId = ? '
        ' and $transactionColumnStatus = ? '
        ' and $transactionColumnType = ?',
        [appCompanyId, transactionStatusPrepare, transactionTypeSell]);
    await loadSellData(store);
  } catch (e, stack) {
    log.e('removeAllSellData', e, stack);
  }
}

Future<void> loadSellData(Store<AppState> store) async {
  try {
    log.i('loadSellData');
    final int? appCompanyId = store.state.userState!.auth!.companyId;
    final List<Map<String, dynamic>> set = await _dbService.queryRowsWithWhere(
        transactionTableName,
        '$transactionColumnAppCompanyId = ?'
        '  and $transactionColumnStatus = ?'
        ' and $transactionColumnType = ?',
        [appCompanyId, transactionStatusPrepare, transactionTypeSell],
        orderBy: '$transactionColumnCreatedAt desc');
    final List<ProductDao> list = [];
    String? uuid;
    int? transactionId;
    if (set.isNotEmpty) {
      final Transaction transaction = Transaction.fromMap(set.first);
      uuid = transaction.uuid;
      transactionId = transaction.id;
      final List<Map<String, dynamic>> recs =
          await _dbService.queryRowsWithWhere(transactionRecTableName,
              '$transactionRecTransactionIdRef = ? ', [transaction.id],
              orderBy: '$transactionRecColumnCreatedAt desc');
      for (final Map<String, dynamic> map in recs) {
        final TransactionRec rec = TransactionRec.fromMap(map);
        final ProductDao productDao = ProductDao.fromMap(jsonDecode(rec.data!));
        productDao.transactionId = rec.id;
        list.add(productDao);
      }
    }
    store.dispatch(SetSellStateAction(SellState(
        items: list,
        transactionState: TransactionState()
          ..uuid = uuid
          ..transactionId = transactionId)));
  } catch (e, stack) {
    log.e('loadSellData', e, stack);
  }
}
