const _this = {};

export const init_module = (e) => {
  _this.func = e.func;
  _this.glb = e.glb;
  _this.SESSION_OBJ = e.SESSION_OBJ;
  _this.APP_OBJ = e.APP_OBJ;
  _this.IS_DOCKER = e.IS_DOCKER;
  _this.IS_API_SERVER = e.IS_API_SERVER;
  _this.IS_PROCESS_SERVER = e.IS_PROCESS_SERVER;
};

export const build_filter = async function (SESSION_ID, dataSourceSession, v, _ds) {
  let file_ret2 = await _this.func.utils.FILES_OBJ.get(SESSION_ID, _ds._dataSourceTableId);
  if (!file_ret2) {
    _this.func.utils.debug_report(SESSION_ID, 'datasource build query', 'Table reference not exist', 'E');
    return;
  }
  let view_ret = await _this.func.utils.VIEWS_OBJ.get(SESSION_ID, _ds.args.prog_id);
  v.dataSourceIndexId = view_ret.progDataSource.dataSourceIndexId; // get index id
  v.dataSourceIndexIdExp = view_ret.progDataSource.dataSourceIndexIdExp; // get index exp
  if (v.dataSourceIndexIdExp) {
    let ret_dataSourceIndexIdExp = await _this.func.expression.get(SESSION_ID, v.dataSourceIndexIdExp, dataSourceSession, 'index', _ds.args.rowIdP);
    if (ret_dataSourceIndexIdExp.result) {
      v.dataSourceIndexId = ret_dataSourceIndexIdExp.result;
    }
    if (!v.dataSourceIndexId) {
      // issue alert expression returned empty value
      _this.func.utils.debug_report(SESSION_ID, 'BUILD FILTER QUERY', 'Index expression return empty result', 'W');
    }
    v.dataSourceIndexesObj = view_ret.progDataSource.dataSourceIndexesObj;

    const index_obj = _this.func.common.find_item_by_key_root(file_ret2.tableIndexes, 'id', v.dataSourceIndexId);

    if (!index_obj) {
      return _this.func.utils.debug_report(SESSION_ID, 'BUILD FILTER QUERY', 'Index not found in table: ' + _ds._dataSourceTableId, 'W');
    }

    // var index_keys = index_obj.data.keys;

    if (!v.dataSourceIndexesObj?.[v.dataSourceIndexId]) {
      _this.func.utils.debug_report(SESSION_ID, 'BUILD FILTER QUERY', 'Index not found', 'W');
    }

    // v.viewIndex = {};
    // _.forEach(index_keys, function (val, key) {
    //   var item = v.dataSourceIndexesObj[v.dataSourceIndexId][val];
    //   v.viewIndex[key] = {
    //     [val]: {
    //       from: item.from,
    //       to: item.to,
    //       locate_from: item.locate_from,
    //       locate_to: item.locate_to,
    //     },
    //   };
    // });
  }
  var filter_from = {};
  var filter_to = {};
  let ret = await _this.func.utils.VIEWS_OBJ.get(SESSION_ID, _ds.args.prog_id);
  var sortOrder = ret.progDataSource.dataSourceSort; // get ascending/descending
  var sortOrderTypeExp = ret.progDataSource.dataSourceSortExp; // get ascending/descending
  if (_ds.sortOrder) {
    sortOrder = _ds.sortOrder;
  }
  if (_ds.sortOrderTypeExp) {
    sortOrderTypeExp = _ds.sortOrderTypeExp;
  }
  v.sortOrder = sortOrder;
  v.sortOrderTypeExp = sortOrderTypeExp;
  if (sortOrderTypeExp) {
    var sort_orderExp = await _this.func.expression.get(SESSION_ID, v.sortOrderTypeExp, dataSourceSession, 'sortorder', _ds.args.rowIdP);
    if (sort_orderExp.result) {
      v.sortOrder = sort_orderExp.result;
      sortOrder = v.sortOrder;
    }
  }
  let file_ret = await _this.func.utils.FILES_OBJ.get(SESSION_ID, _ds._dataSourceTableId);

  if (v.dataSourceIndexId) {
    // index definition exist
    _ds.filter_from = {};
    _ds.filter_to = {};
    _ds.locate_from = {};
    _ds.locate_to = {};
    var locate_exist;
    var locate_error;
    v.segFrom = [];
    v.segTo = [];
    if (v.viewIndex) {
      for await (const [keySegment, valSegment] of Object.entries(v.viewIndex[v.dataSourceIndexId])) {
        // run on segments rows
        var fieldId = keySegment; // key segment

        if (!_this.func.common.find_item_by_key(file_ret.tableFields, 'field_id', fieldId)) {
          _this.func.utils.debug_report(SESSION_ID, 'datasource build query', `Wrong index fields for ${fieldId} at field range ${v.viewSourceDesc}`, 'E', null, _ds);
          break;
        }

        var fieldProp = _this.func.common.find_item_by_key(file_ret.tableFields, 'field_id', fieldId);

        var fieldType = fieldProp.props.fieldType;
        var fieldValFrom = valSegment.from; // key val or fieldID from
        var fieldValTo = valSegment.to; // key val or fieldID to
        const get_val = async function (valP, actionP, typeP) {
          var ret = {};
          ret.result = valP;
          ret.fields = {};
          ret.fields[fieldId] = valP;
          if (!ret.result) {
            ret.result = 0;
            if (fieldType !== 'number') {
              // ret.result = '""'; // removed Aug 27 2024
              ret.result = '';
              ret.fields[fieldId] = ret.result;
            }
          } else {
            if (fieldType === 'number') {
              if (!valP.includes('@')) {
                if (typeof valP === 'boolean') {
                  ret.result = valP ? 1 : 0;
                } else {
                  ret.result = Number(valP);
                }

                ret.fields[fieldId] = ret.result;
              }
            } else {
              if (!valP.includes('@')) {
                ret.result = valP;
                ret.fields[fieldId] = ret.result;
              }
            }
            ret = await _this.func.expression.get(SESSION_ID, ret.result, dataSourceSession, 'query', _ds.args.rowIdP);
          }
          _this.func.utils.debug.log(SESSION_ID, _ds.args.prog_id + '_' + valSegment.id, {
            module: v.viewModule,
            action: actionP,
            prop: typeP,
            details: ret.result,
            result: ret.result,
            error: ret.error,
            warning: ret.warning,
            source: v.viewSourceDesc,
            fields: ret.fields,
            type: 'query',
            prog_id: _ds.args.prog_id,
            dsSession: dataSourceSession,
          });
          return ret;
        };
        var value_from = await get_val(fieldValFrom, 'range', 'from');
        var value_to = await get_val(fieldValTo, 'range', 'to');
        v.segFrom.push(value_from.result);
        v.segTo.push(value_to.result);
        var add_filters_to_datasource = function (objP, typeP) {
          // later use for refresh purpose
          for (let [key, val] of Object.entries(objP.fields)) {
            if (!val) val = null;
            _ds[typeP][key] = val; //"@" +
          }
        };
        add_filters_to_datasource(value_from, 'filter_from');
        add_filters_to_datasource(value_to, 'filter_to');
        filter_from[fieldId] = value_from.result;
        filter_to[fieldId] = value_to.result;
        if (_ds.tree_obj.menuType === 'component') {
          // locate
          v.segLocateFrom[keySegment] = v.segFrom[keySegment]; // put defaults from range
          v.segLocateTo[keySegment] = v.segTo[keySegment]; // put defaults from range
          //  linkTypeLocate = valSegment.data[6]; // value/field
          v.fieldLocateValFrom = valSegment.locate_from; // key val or fieldID from
          v.fieldLocateValTo = valSegment.locate_to; // key val or fieldID to
          if (valSegment.locate_from || valSegment.locate_to) locate_exist = true;
          var value_from_locate = await get_val(v.fieldLocateValFrom, 'locate', 'from');
          var value_to_locate = await get_val(v.fieldLocateValTo, 'locate', 'to');
          //
          v.segLocateFrom[keySegment] = value_from_locate.result;
          v.segLocateTo[keySegment] = value_to_locate.result;
          add_filters_to_datasource(value_from_locate, 'locate_from');
          add_filters_to_datasource(value_to_locate, 'locate_to');
        }
      }
    }
    const query_string_helper = function (fileIdP, indexIdP, segFromP, segToP, sortOrderP, filter_from, filter_to) {
      var query = {};
      var from = [fileIdP, indexIdP, segFromP];
      var to = [fileIdP, indexIdP, segToP];
      query.desc = sortOrderP === 'des';
      query.view = 'db_index';
      query.table_id = fileIdP;
      query.indexId = indexIdP;
      if (sortOrderP === 'des') {
        query.startkey = to;
        query.endkey = from;
      } else {
        query.startkey = from;
        query.endkey = to;
      }
      query.filter_from = filter_from;
      query.filter_to = filter_to;
      query.sortOrder = sortOrderP;
      return query;
    };

    {
      // regular filter
      v.couchView = query_string_helper(_ds._dataSourceTableId, v.dataSourceIndexId, v.segFrom, v.segTo, sortOrder, filter_from, filter_to);
    }
    // locate
    if (_ds.tree_obj.menuType === 'component' && locate_exist && !locate_error) {
      v.locateQuery = query_string_helper(_ds._dataSourceTableId, v.dataSourceIndexId, v.segLocateFrom, v.segLocateTo, sortOrder, _ds.locate_from, _ds.locate_to);
    }
  } else {
    // index definition missing
    v.couchView = {
      view: 'db_index_all',
      table_id: _ds._dataSourceTableId,
    };
  }
};

export const process_view_dataset = async function (SESSION_ID, dataSourceSession, _ds) {
  const tree_obj = await _this.func.utils.TREE_OBJ.get(SESSION_ID, _ds.prog_id);
  var rows_count = _ds?.v.raw_data?.rows?.length || 0;
  // var skip;
  var args = _ds.args;
  const finish_dataset = async function () {
    _ds.rows_found = rows_count;
    // new condition "rows" added to prevent after record if no rows found 2021 09 17
    if (tree_obj.menuType === 'get_data' && rows_count && (await _this.func.datasource.get_view_events_count(SESSION_ID, dataSourceSession, 'after_record')) > 0) {
      await _this.func.datasource.execute_view_events(SESSION_ID, dataSourceSession, 'after_record');
    }
    return _this.func.datasource.callback(SESSION_ID, dataSourceSession, args.dataSourceNoP, args.rowIdP, args.jobNoP, null, _ds.nodeId);
  };
  const iterate_rows = async function () {
    // _ds.currentRecordId = "dataset";
    for await (const [key, raw_data_row] of Object.entries(_ds.v.raw_data.rows)) {
      _ds.currentRecordId = raw_data_row.id;
      await _this.func.datasource.render_fields_dataset(SESSION_ID, dataSourceSession, raw_data_row);

      break;
    }
    return await finish_dataset();
  };

  if (rows_count) {
    return await iterate_rows();
  }

  // Alert record not found

  _ds.currentRecordId = 'dataset';
  _ds.set_mode = 'C';
  await _this.func.datasource.render_fields_dataset(SESSION_ID, dataSourceSession);
  await _this.func.events.validate(SESSION_ID, 'record_not_found', dataSourceSession);
  _ds.record_not_found = true;
  return await finish_dataset();
};

export const set_dataset_key_data = function (SESSION_ID, dataSourceSession, valP) {
  var _ds = _this.SESSION_OBJ[SESSION_ID].DS_GLB[dataSourceSession];
  var v = _ds.v;
  v.udfData = valP.value.udfData;
  var keyData = {
    _id: valP.value._id,
    _rev: valP.value._rev,
    table_id: _ds._dataSourceTableId,
  };
  _ds.key = keyData;
};

export const get_locate_from_db = async function (SESSION_ID, dataSourceSession) {
  var _ds = _this.SESSION_OBJ[SESSION_ID].DS_GLB[dataSourceSession];
  var v = _ds.v;

  if (!v.locateQuery) {
    return;
  }
  // from DB
  const json = await _this.func.db.get_query(SESSION_ID, _ds._dataSourceTableId, v.locateQuery, dataSourceSession, v.viewSourceDesc, 'locate record');
  var rows = json.rows.length;

  if (!rows) {
    return await _this.func.events.validate(SESSION_ID, 'locate_not_found', dataSourceSession);
  }
  _ds.locatedRecordId = json.rows[0].id;
  return;
};

export const find_fieldId_in_index = function (SESSION_ID, dataSourceSession, fieldId) {
  var _ds = _this.SESSION_OBJ[SESSION_ID].DS_GLB[dataSourceSession];
  var v = _ds.v;
  var ret;

  _.forEach(v.viewIndex, function (val, key) {
    if (val.data[0] === fieldId) {
      ret = key;
      return false;
    }
  });
  return ret;
};

export const dataset_save_before_exit = async function (SESSION_ID, dataSourceSession, reEnterP) {
  var exit_event_count = 0;
  if (!reEnterP) {
    exit_event_count = await _this.func.datasource.get_view_events_count(SESSION_ID, dataSourceSession, 'on_exit');
  }
  var _ds = _this.SESSION_OBJ[SESSION_ID].DS_GLB[dataSourceSession];
  var v = _ds.v;
  // var save_before_exit = async function () {
  //   if (
  //     v.screenInfo &&
  //     v.screenInfo.menuType === "set_data" &&
  //     _.size(v.jsonP.rows)
  //   ) {
  //     await _this.func.db.save_data(SESSION_ID, dataSourceSession, callback_dataset);
  //   } else {
  //     callback_dataset();
  //   }
  // };
  if (exit_event_count) {
    await _this.func.datasource.execute_view_events(SESSION_ID, dataSourceSession, 'on_exit');

    if (v?.screenInfo?.properties?.menuType === 'set_data' && _.size(v.jsonP.rows)) {
      return await _this.func.db.save_data(SESSION_ID, dataSourceSession);
    }
  }
  return;
};
