import 'dart:convert';
import 'dart:ffi';

import 'package:aman_kassa_flutter/core/base/base_service.dart';
import 'package:aman_kassa_flutter/core/entity/Category.dart';
import 'package:aman_kassa_flutter/core/entity/Goods.dart';
import 'package:aman_kassa_flutter/core/entity/Service.dart';
import 'package:aman_kassa_flutter/core/entity/Voucher.dart';
import 'package:aman_kassa_flutter/core/locator.dart';
import 'package:aman_kassa_flutter/core/models/calc_model.dart';
import 'package:aman_kassa_flutter/core/models/card_data.dart';
import 'package:aman_kassa_flutter/core/models/check_data.dart';
import 'package:aman_kassa_flutter/core/models/check_image_modal.dart';
import 'package:aman_kassa_flutter/core/models/check_item.dart';
import 'package:aman_kassa_flutter/core/models/product_dao.dart';
import 'package:aman_kassa_flutter/core/models/response.dart';
import 'package:aman_kassa_flutter/core/models/user.dart';
import 'package:aman_kassa_flutter/core/services/DbService.dart';
import 'package:aman_kassa_flutter/redux/constants/operation_const.dart';
import 'package:aman_kassa_flutter/redux/constants/setting_const.dart';
import 'package:aman_kassa_flutter/redux/store.dart';
import 'package:flutter/material.dart';

import 'ApiService.dart';

class DataService extends BaseService {
  final ApiService _api = locator<ApiService>();
  final DbService _db = locator<DbService>();

  Future<List<Category>> getCategoriesByParentId(
      {int? parentId}) async {
    List<Map<String, dynamic>> list = await _db.queryRowsWithWhere(
        Category_tableName, '$Category_columnParentIn = ?', [parentId ?? 0]);
    return list.map((e) => Category.fromMap(e)).toList();
  }

  Future<List<Service>> getServices() async {
    List<Map<String, dynamic>> list = await _db.queryAllRows(Service_tableName);
    return list.map((e) => Service.fromMap(e)).toList();
  }

  Future<List<Good>> getGoodsByCategoryId({int? categoryId}) async {
    List<Map<String, dynamic>> list = await _db.queryRowsWithWhere(
        Goog_tableName, '$Goog_columnCategoryId  = ?', [categoryId ?? 0]);
    return list.map((e) => Good.fromMap(e)).toList();
  }

  Future<List<Good>> getGoodsByBarcode({required String barcode}) async {
    List<Map<String, dynamic>> list = await _db
        .queryRowsWithWhere(Goog_tableName, ' $Goog_columnEan = ?', [barcode]);
    return list.map((e) => Good.fromMap(e)).toList();
  }

  CheckData _transformProductsToCheckData(
      {String? paymentType,
      String? tradeType,
      required List<ProductDao> items}) {
    List<CheckItem> itemsList = [];
    int iterator = 1;
    num summ = 0.0;
    items.forEach((el) {
      int articul = iterator;
      if (el.service != null) {
        articul = el.service!.articul;
      } else if (el.good != null) {
        articul = el.good!.articul;
      }
      itemsList.add(CheckItem(
        name: el.name,
        cnt: el.count,
        price: el.price,
        articul: articul,
        excise: el.excise,
        ntin: el.ntin,
      ));
      summ += el.total;
      iterator++;
    });
    CheckData checkData = CheckData(type: tradeType, items: itemsList);
    if ((paymentType ?? 'cash') == 'card') {
      checkData.card = summ;
    } else if ((paymentType ?? 'cash') == 'mobile') {
      checkData.mobile = summ;
    }
    return checkData;
  }

  CheckData _transformCalcModelToCheckData(
      {String? paymentType,
      String? tradeType,
      required List<CalcModel> items}) {
    List<CheckItem> itemsList = [];
    int iterator = 1;
    num summ = 0.0;
    items.forEach((el) {
      int articul = iterator;
      CheckItem item = CheckItem(
          name: 'Позиция $iterator',
          cnt: el.num2 != null ? double.parse(el.num2!) : 1.0,
          price: double.parse(el.num1),
          articul: articul);

      summ += item.cnt * item.price;
      itemsList.add(item);
      iterator++;
    });
    CheckData checkData = CheckData(type: tradeType, items: itemsList);
    if ((paymentType ?? 'cash') == 'card') {
      checkData.card = summ;
    } else if ((paymentType ?? 'cash') == 'mobile') {
      checkData.mobile = summ;
    }
    return checkData;
  }

  /* save data to db
         *  data,
         *  dateTime,
         *  check,
         *  appCompanyId,
         *  kassaId,
         *  total,
         *  name,
         *  type
         */
  Future<void> insertVoucher(
      {required User user,
      String? data,
      String? base64Data,
      required String name,
      double total = 0.0,
      String type = VoucherTypePayment,
      String? url}) async {
    assert(user != null);
    assert(name != null);
    Voucher voucher = Voucher()
      ..name = name
      ..url = url
      ..total = total
      ..appCompanyId = user.appCompanyId
      ..kassaId = user.kassaId
      ..data = data
      ..base64Data = base64Data
      ..dateTime = DateTime.now()
      ..type = type;
    log.i(
        'save to db appCompanyId: ${user.appCompanyId}, kassaId: ${user.kassaId}');
    _db.insert(Voucher_tableName, voucher.toMap());
  }

  Future<Response<dynamic>?> refundM4Bank(
      {
        required String token,
        required CheckData checkData,
        required CardData cardData}) async {
    try {
      var json  = cardData.toJson();
      json['transactionType'] = VoucherTypeReturnPay;
      checkData.cardData = CardData.fromJson(json);
      String data = jsonEncode(checkData.toJson());

     log.i('token: $token');
     log.i('data: $data');
      Response<dynamic> response = await _api.sellReturn(token, data);
     log.i('response status: ${response.status}');
     log.i('response operation: ${response.operation}');
      if (response.status == 200 && response.operation == true) {
        User user = Redux.store!.state.userState!.user!;
        String check = response.body['check'];
        dynamic journal = response.body['journal'];
        String url = response.body['link'];
        int checkNum = journal['check_num'];
        var summ = journal['summ'];
        double total = summ != null ? double.parse(summ.toString()) : 0.0;
        this.insertVoucher(
            user: user,
            name: 'Чек №$checkNum',
            data: data,
            base64Data: check,
            total: total,
            url: url,
            type: VoucherTypeReturnPay);
      }
      return response;
    } catch (e, stack) {
      log.e("sellOrReturn", e, stack);
    }
    return null;
  }

  Future<Response<dynamic>?> sellOrReturn(
      {String? paymentType,
      String? tradeType,
        String?  contragent,
        String? ticketNumber,
      required String token,
        required List<ProductDao> kassaItems,
        required List<CalcModel> calcItems,
      required String operationType,
      required String mode,
        required CardData? cardData}) async {
    try {
      String data = "";
      if (mode == SettingModeKassa) {
        CheckData checkData = _transformProductsToCheckData(
            paymentType: paymentType, tradeType: tradeType, items: kassaItems);
        checkData.cardData = cardData;
        checkData.contragent = contragent;
        data = jsonEncode(checkData.toJson());
      } else if (mode == SettingModeCalc) {
        CheckData checkData = _transformCalcModelToCheckData(
            paymentType: paymentType, tradeType: tradeType, items: calcItems);
        checkData.cardData = cardData;
        checkData.contragent = contragent;
        data = jsonEncode(checkData.toJson());
      }

//      log.i('token: $token');
//      log.i('data: $data');
      Response<dynamic> response = await (operationType == OperationTypePay
          ? _api.sell(token, data)
          : _api.sellReturn(token, data, ticketNumber: ticketNumber));
//      log.i('response status: ${response.status}');
//      log.i('response operation: ${response.operation}');
      if (response.status == 200 && response.operation == true) {
        User user = Redux.store!.state.userState!.user!;
        //check compare

        String check = response.body['check'];
        var checkText = response.body['check_text'];
        CheckImageModal imageModal = new CheckImageModal(
            base64Data: check,
            textData: checkText != null ? jsonEncode(checkText) : null);
        // journal analyze
        dynamic journal = response.body['journal'];
        int checkNum = journal['check_num'];
        var summ = journal['summ'];
        // short url
        String url = response.body['link'];
        // total
        double total = summ != null ? double.parse(summ.toString()) : 0.0;

        //insert data to db
        this.insertVoucher(
            user: user,
            name: 'Чек №$checkNum',
            data: data,
            base64Data: jsonEncode(imageModal.toJson()),
            total: total,
            url: url,
            type: operationType == OperationTypeReturn
                ? VoucherTypeReturnPay
                : VoucherTypePayment);
      }
      return response;
    } catch (e, stack) {
      log.e("sellOrReturn", e, stack);
    }
    return null;
  }

  Future<void> checkDbFill(User user) async {
    int serviceCount = await _db.queryRowCount(Service_tableName) ?? 0;
    if (serviceCount == 0) {
      int goodCount = await _db.queryRowCount(Goog_tableName) ?? 0;
      if (goodCount == 0) {
        await getDataFromServer(user);
      } else {
        log.i('$Goog_tableName is Fill');
      }
    } else {
      log.i('$Service_tableName is Fill');
    }
  }

  Future<bool> getDataFromServer(User user) async {
    log.i('Get Data from server');
    try {
      String token = user.token!;
      Response<dynamic> goods = await _api.getGoodsFromServer(token);
      if (goods.operation == false &&
          [401, 402, 403, 412].contains(goods.status)) {
        log.i('session is closed');
        return false;
      }
      Response<dynamic> categories = await _api.getCategoryFromServer(token);
      Response<dynamic> services = await _api.getServiceFromServer(token);
      await _db.deleteAll(Goog_tableName);
      await _db.deleteAll(Category_tableName);
      await _db.deleteAll(Service_tableName);
      log.i('All tables cleaned');

      if (goods.body.isNotEmpty) {
        for (var key in goods.body.keys) {
          Good row = Good.fromJson(goods.body[key]);
          //log.i(row.toMap());
          await _db.insert(Goog_tableName, row.toMap());
        }
        log.i('Inserted ${goods.body.length} to table $Goog_tableName');
      }
      if (categories.body.isNotEmpty) {
        for (var el in categories.body) {
          Category row = Category.fromJson(el);
          await _db.insert(Category_tableName, row.toMap());
        }
        log.i(
            'Inserted ${categories.body.length} to table $Category_tableName');
      }

      if (services.body.isNotEmpty) {
        for (var key in services.body.keys) {
          Service row = Service.fromJson(services.body[key]);
          await _db.insert(Service_tableName, row.toMap());
        }
        log.i('Inserted ${services.body?.length} to table $Service_tableName');
      }
      return true;
    } catch (e, stack) {
      log.e("load from server", e, stack);
      return false;
    }
  }
}
