const defaultParamDict = ['MORE', 'MORE_OR_EQUALS', 'LESS', 'LESS_OR_EQUALS', 'EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'START', 'NOT_START', 'NOT_END', 'END', 'TEXT_NOT_EQUALS', 'TEXT_EQUALS'];

const RD = {
  formData: null,
  paramSearch: {},

  paramFields: {
    registryRouteStatus: ['STATE_SUCCESSFUL', 'STATE_NOT_FINISHED', 'NO_ROUTE', 'STATE_UNSUCCESSFUL'],
    textarea: defaultParamDict,
    textbox: defaultParamDict,
    custom: defaultParamDict,
    formula: defaultParamDict,
    counter: defaultParamDict,
    projectlink: defaultParamDict,
    repeater: defaultParamDict,
    docnumber: defaultParamDict,
    personlink: defaultParamDict,
    link: defaultParamDict,
    htd: defaultParamDict,

    numericinput: ['MORE', 'MORE_OR_EQUALS', 'LESS', 'LESS_OR_EQUALS', 'EQUALS', 'NOT_EQUALS'],
    date: ['MORE', 'MORE_OR_EQUALS', 'LESS', 'LESS_OR_EQUALS', 'EQUALS', 'NOT_EQUALS'],
    entity: {
      users: ['CONTAINS', 'NOT_CONTAINS'],
      positions: ['CONTAINS', 'NOT_CONTAINS'],
      departments: ['CONTAINS', 'NOT_CONTAINS']
    },
    reglink: ['CONTAINS', 'NOT_CONTAINS'],
    listbox: ['EQUALS'],
    check: ['CONTAINS'],
    radio: ['EQUALS']
  },

  paramName: {
    STATE_SUCCESSFUL: 'Активная',
    STATE_NOT_FINISHED: 'В процессе',
    NO_ROUTE: 'Подготовка',
    STATE_UNSUCCESSFUL: 'Неуспешная',

    MORE: '>',
    MORE_OR_EQUALS: '>=',
    LESS: '<',
    LESS_OR_EQUALS: '<=',
    EQUALS: '=',
    NOT_EQUALS: '<>',

    CONTAINS: 'Содержит',
    NOT_CONTAINS: 'Не содержит',
    START: 'Начинается с',
    NOT_START: 'Не начинается с',
    NOT_END: 'Не заканчивается на',
    END: 'Заканчивается на',
    TEXT_NOT_EQUALS: 'Не совпадает',
    TEXT_EQUALS: 'Совпадает'
  },

  reset: function() {
    this.formData = null;
    this.paramSearch = {};
  }
};

const getUrlSearch = () => {
  let url = '&groupTerm=and';
  let count = 0;

  for(let key in RD.paramSearch) {
    if(RD.paramSearch[key].field == 'registryRecordStatus') {
      url += `&registryRecordStatus=${RD.paramSearch[key].value}`;
    } else {
      let gID = count > 0 ? count : '';
      switch (RD.paramSearch[key].type) {
        case 'listbox':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('keys') && RD.paramSearch[key].keys) {
            RD.paramSearch[key].keys.forEach(fieldKey =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&key${gID}=${fieldKey}`
            );
          } else {
            return null;
          }
          break;
        case 'radio':
        case 'check':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('values') && RD.paramSearch[key].values) {
            RD.paramSearch[key].values.forEach(fieldValue =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&value${gID}=${fieldValue}`
            );
          } else {
            return null;
          }
          break;
        case 'reglink':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('selectedIds') && RD.paramSearch[key].selectedIds) {
            RD.paramSearch[key].selectedIds.forEach(docID =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&key${gID}=${docID}`
            );
          } else {
            return null;
          }
          break;
        case 'users':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('users') && RD.paramSearch[key].users) {
            RD.paramSearch[key].users.map(x => x.personID).forEach(userid =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&key${gID}=${userid}`
            );
          } else {
            return null;
          }
          break;
        case 'positions':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('positions') && RD.paramSearch[key].positions) {
            RD.paramSearch[key].positions.map(x => x.elementID).forEach(positionID =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&key${gID}=${positionID}`
            );
          } else {
            return null;
          }
          break;
        case 'departments':
          RD.paramSearch[key].condition.indexOf('NOT') == -1 ? url += `&term${gID}=or` : url += `&term${gID}=and`;
          if(RD.paramSearch[key].hasOwnProperty('departments') && RD.paramSearch[key].departments) {
            RD.paramSearch[key].departments.map(x => x.departmentId).forEach(departmentId =>
              url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}&key${gID}=${departmentId}`
            );
          } else {
            return null;
          }
          break;
        default:
          url += `&field${gID}=${RD.paramSearch[key].field}&condition${gID}=${RD.paramSearch[key].condition}`;
          if(RD.paramSearch[key].hasOwnProperty('key')) {
            if(!RD.paramSearch[key].key || RD.paramSearch[key].key == "") {
              return null;
            } else {
              url += `&key${gID}=${RD.paramSearch[key].key}`;
            }
          } else {
            if(!RD.paramSearch[key].value || RD.paramSearch[key].value == "") {
              return null;
            } else {
              url += `&value${gID}=${RD.paramSearch[key].value}`;
            }
          }
      }
      count++;
    }
  }

  return url == '&groupTerm=and' ? null : url;
}

const createSelectComponent = (items, multiple) => {
  const select = $('<select class="uk-select" style="min-width: 100px;">');
  if(multiple) select.attr('multiple', 'multiple');
  if(items) items.sort((a,b) => a.value - b.value)
  .forEach(item => select.append(`<option value="${item.value}" title="${item.label}">${item.label}</option>`));
  return select;
};

const createInputText = () => $('<input type="text" class="uk-input" placeholder="Введите значение">');

const createInputNumber = () => $('<input type="number" class="uk-input" placeholder="Введите значение">');

const createInputDate = () => $('<input type="date" class="uk-input" style="max-width: 160px;"><input type="time" class="uk-input" style="padding-left: 10px;max-width: 100px;" value="00:00">');

const createBlockParam = param => {
  const container = $('<div uk-grid style="margin: 0;"></div>');
  const items = RD.paramFields[param.type].map(x => ({label: RD.paramName[x], value: x}));
  const select = createSelectComponent(items);
  let input;
  switch (param.type) {
    case 'date': input = createInputDate(); break;
    case 'numericinput': input = createInputNumber(); break;
    default: input = createInputText(); break;
  }

  select.addClass('uk-width-auto').css({'padding-left': '10px', 'margin-right': '5px'});
  input.addClass('uk-width-expand').css('padding-left', '10px');
  container.append(select, input);

  return container;
};

const createRegistryParamBlock = (param, uuidRow, guid) => {
  const button = $('<a class="uk-form-icon uk-form-icon-flip" href="javascript:void(0);" uk-icon="icon: list"></a>');
  const input = $('<input class="uk-input" type="text" disabled>');
  const select = createSelectComponent(RD.paramFields.reglink.map(x => ({label: RD.paramName[x], value: x})));

  if(guid) {
    AS.FORMS.ApiUtils.simpleAsyncGet(`rest/api/formPlayer/getDocMeaningContents?documentId=${RD.paramSearch[uuidRow].selectedIds.join('&documentId=')}`, content => {
      content = content.map(x => x.meaning).join('; ');
      input.val(content).attr('title', content);
    });
  } else {
    RD.paramSearch[uuidRow] = {field: param.id, type: param.type, condition: select.val()};
  }

  select.on('change', () => RD.paramSearch[uuidRow].condition = select.val());

  button.on('click', async e => {
    Cons.showLoader();

    const selectedIds = RD.paramSearch[uuidRow].hasOwnProperty('selectedIds') ? RD.paramSearch[uuidRow].selectedIds : null;
    const registryInfo = await appAPI.getRegistryInfoByID(param.registryID);
    registryInfo.registryCustomFilters = [];

    Cons.hideLoader();

    //(registry, multi, selectedIds, handler)
    AS.SERVICES.showRegistryLinkDialog(registryInfo, true, selectedIds, async docIDs => {
    	RD.paramSearch[uuidRow].selectedIds = docIDs;
    	let content = await AS.FORMS.ApiUtils.simpleAsyncGet(`rest/api/formPlayer/getDocMeaningContents?documentId=${docIDs.join('&documentId=')}`);
    	content = content.map(x => x.meaning).join('; ');
    	input.val(content).attr('title', content);
    });
  });

  return $('<div class="uk-grid-small" uk-grid>').append(
  	$('<div class="uk-width-1-4@s">').append(select),
  	$('<div class="uk-inline uk-width-3-4@s">').append(button, input)
  );
}

const createUserParamBlock = (param, uuidRow, guid) => {
  const button = $('<a class="uk-form-icon uk-form-icon-flip" href="javascript:void(0);" uk-icon="icon: users"></a>');
  const input = $('<input class="uk-input" type="text" disabled>');
  const select = createSelectComponent(RD.paramFields.entity.users.map(x => ({label: RD.paramName[x], value: x})));

  if(guid) {
    const userNames = RD.paramSearch[uuidRow].users.map(x => x.personName).join('; ');
    input.val(userNames).attr('title', userNames);
  } else {
    RD.paramSearch[uuidRow] = {field: param.id, type: param.entity, condition: select.val()};
  }

  select.on('change', () => RD.paramSearch[uuidRow].condition = select.val());

  button.on('click', e => {
    const values = RD.paramSearch[uuidRow].hasOwnProperty('users') ? RD.paramSearch[uuidRow].users : null;
    //(values, multiSelectable, isGroupSelectable, showWithoutPosition, filterPositionID, filterDepartmentID, locale, handler)
    AS.SERVICES.showUserChooserDialog(values, true, false, false, null, null, AS.OPTIONS.locale, users => {
      const userNames = users.map(x => x.personName).join('; ');
      input.val(userNames).attr('title', userNames);
      RD.paramSearch[uuidRow].users = users;
    });
  });

  return $('<div class="uk-grid-small" uk-grid>')
  .append($('<div class="uk-width-1-4@s">').append(select))
  .append($('<div class="uk-inline uk-width-3-4@s">').append(button, input));
}

const createDepParamBlock = (param, uuidRow, guid) => {
  const button = $('<a class="uk-form-icon uk-form-icon-flip" href="javascript:void(0);" uk-icon="icon: more"></a>');
  const input = $('<input class="uk-input" type="text" disabled>');
  const select = createSelectComponent(RD.paramFields.entity.departments.map(x => ({label: RD.paramName[x], value: x})));

  if(guid) {
    const depNames = RD.paramSearch[uuidRow].departments.map(x => x.departmentName).join('; ');
    input.val(depNames).attr('title', depNames);
  } else {
    RD.paramSearch[uuidRow] = {field: param.id, type: param.entity, condition: select.val()};
  }

  select.on('change', () => RD.paramSearch[uuidRow].condition = select.val());

  button.on('click', e => {
    const values = RD.paramSearch[uuidRow].hasOwnProperty('departments') ? RD.paramSearch[uuidRow].departments : null;
    //(values, multiSelectable, filterUserID, filterPositionID, filterDepartmentID, filterChildDepartmentID, locale, handler)
    AS.SERVICES.showDepartmentChooserDialog(values, true, null, null, null, null, AS.OPTIONS.locale, departments => {
      const depNames = departments.map(x => x.departmentName).join('; ');
      input.val(depNames).attr('title', depNames);
      RD.paramSearch[uuidRow].departments = departments;
    });
  });

  return $('<div class="uk-grid-small" uk-grid>')
  .append($('<div class="uk-width-1-4@s">').append(select))
  .append($('<div class="uk-inline uk-width-3-4@s">').append(button, input));
}

const createPositionParamBlock = (param, uuidRow, guid) => {
  const button = $('<a class="uk-form-icon uk-form-icon-flip" href="javascript:void(0);" uk-icon="icon: more"></a>');
  const input = $('<input class="uk-input" type="text" disabled>');
  const select = createSelectComponent(RD.paramFields.entity.positions.map(x => ({label: RD.paramName[x], value: x})));

  if(guid) {
    const posNames = RD.paramSearch[uuidRow].positions.map(x => x.elementName).join('; ');
    input.val(posNames).attr('title', posNames);
  } else {
    RD.paramSearch[uuidRow] = {field: param.id, type: param.entity, condition: select.val()};
  }

  select.on('change', () => RD.paramSearch[uuidRow].condition = select.val());

  button.on('click', e => {
    const values = RD.paramSearch[uuidRow].hasOwnProperty('positions') ? RD.paramSearch[uuidRow].positions : null;
    //(values, multiSelect, filterUserId, filterDepartmentId, showVacant, locale, handler)
    AS.SERVICES.showPositionChooserDialog(values, true, null, null, null, AS.OPTIONS.locale, positions => {
      const posNames = positions.map(x => x.elementName).join('; ');
      input.val(posNames).attr('title', posNames);
      RD.paramSearch[uuidRow].positions = positions;
    });
  });

  return $('<div class="uk-grid-small" uk-grid>')
  .append($('<div class="uk-width-1-4@s">').append(select))
  .append($('<div class="uk-inline uk-width-3-4@s">').append(button, input));
}


//Метод добавления параметра для поиска
const addParamRow = (param, guid, filterBody) => {
  if(!param) return;

  const tableParamBody = filterBody.find('#rd-table-params tbody');
  const uuidRow = guid || UTILS.generateGuid('R');
  const tr = $('<tr>');
  const tdFilter = $('<td>');
  const deleteRow = $('<a href="#" uk-icon="trash"></a>');

  deleteRow.on('click', e => {
    e.preventDefault();
    e.stopPropagation();
    tr.remove();
    delete RD.paramSearch[uuidRow];
  });

  switch(param.type) {
    case 'registryRouteStatus':
      let statusRow = createSelectComponent(RD.paramFields[param.type].map(x => ({label: RD.paramName[x], value: x})));
      if(guid) {
        statusRow.val(RD.paramSearch[uuidRow].value);
      } else {
        RD.paramSearch[uuidRow] = {field: 'registryRecordStatus', value: statusRow.val()};
      }
      statusRow.on('change', () => RD.paramSearch[uuidRow].value = statusRow.val());
      tdFilter.append(statusRow);
      break;
    case 'listbox':
      let dictValues = createSelectComponent(param.elements, true);
      if(guid) {
        dictValues.val(RD.paramSearch[uuidRow].keys);
      } else {
        RD.paramSearch[uuidRow] = {
          field: param.id,
          type: param.type,
          condition: RD.paramFields[param.type][0],
          keys: dictValues.val()
        };
      }
      dictValues.on('change', () => RD.paramSearch[uuidRow].keys = dictValues.val());
      tdFilter.append(dictValues);
      break;
    case 'radio':
    case 'check':
      let dictValuesR = createSelectComponent(param.elements, true);
      if(guid) {
        dictValuesR.val(RD.paramSearch[uuidRow].values);
      } else {
        RD.paramSearch[uuidRow] = {
          field: param.id,
          type: param.type,
          condition: RD.paramFields[param.type][0],
          values: dictValuesR.val()
        };
      }
      dictValuesR.on('change', () => RD.paramSearch[uuidRow].values = dictValuesR.val());
      tdFilter.append(dictValuesR);
      break;
    case 'date':
      let blockParamND = createBlockParam(param);
      let selectND = blockParamND.find('select');
      let inputDate = blockParamND.find('input[type="date"]');
      let inputTime = blockParamND.find('input[type="time"]');

      if(guid) {
        selectND.val(RD.paramSearch[uuidRow].condition);
        inputDate.val(RD.paramSearch[uuidRow].key.split(' ')[0]);
        inputTime.val(RD.paramSearch[uuidRow].key.split(' ')[1].substr(0,5));
      } else {
        RD.paramSearch[uuidRow] = {
          field: param.id,
          type: param.type,
          condition: selectND.val(),
          key: `${inputDate.val()} ${inputTime.val()}:00`
        }
      }

      selectND.on('change', () => RD.paramSearch[uuidRow].condition = selectND.val());
      inputDate.on('change', () => RD.paramSearch[uuidRow].key = `${inputDate.val()} ${inputTime.val()}:00`);
      inputTime.on('change', () => RD.paramSearch[uuidRow].key = `${inputDate.val()} ${inputTime.val()}:00`);
      tdFilter.append(blockParamND);
      break;
    case 'reglink':
      tdFilter.append(createRegistryParamBlock(param, uuidRow, guid));
      break;
    case 'entity':
      switch (param.entity) {
        case 'users': tdFilter.append(createUserParamBlock(param, uuidRow, guid)); break;
        case 'departments': tdFilter.append(createDepParamBlock(param, uuidRow, guid)); break;
        case 'positions': tdFilter.append(createPositionParamBlock(param, uuidRow, guid)); break;
        default: tdFilter.append(`<div>${i18n.tr('Параметр в разработке')}</div>`);
      }
      break;
    default:
      let blockParamText = createBlockParam(param);
      let inputText = blockParamText.find('input');
      let selectText = blockParamText.find('select');

      if(guid) {
        inputText.val(RD.paramSearch[uuidRow].value);
        selectText.val(RD.paramSearch[uuidRow].condition);
      } else {
        RD.paramSearch[uuidRow] = {
          field: param.id,
          type: param.type,
          condition: selectText.val(),
          value: inputText.val()
        };
      }

      inputText.on('change', () => RD.paramSearch[uuidRow].value = inputText.val());
      selectText.on('change', () => RD.paramSearch[uuidRow].condition = selectText.val());
      tdFilter.append(blockParamText);
  }

  tr.append(
		`<td class="uk-form-label uk-text-primary uk-width-small uk-text-truncate" title="${param.name}">${param.name}</td>`,
		tdFilter,
		$(`<td title="${i18n.tr('Удалить условие')}" style="width: 30px; text-align: end;">`).append(deleteRow)
  );

  tableParamBody.append(tr);
}

//получение элементов справочника для компонента listbox
const getDictionaryItems = async el => {
  if (el.hasOwnProperty('dataSource')) {
    const dict = await appAPI.getDictionary(el.dataSource.dict);
    el.elements = [];
    for (let key in dict.items) {
      const item = dict.items[key];
      el.elements.push({
        label: item[el.dataSource.key].value,
        value: item[el.dataSource.value].value
      });
    }
  }
}

this.RegistryAdvancedSearch = class {
	constructor(registryComponent){
		this._registryCode = null;
		this._registryComponent = registryComponent;

		this.render();
	}

	set registryCode(value) {
		this._registryCode = value;
		this.update();
	}

	openFilterWindow() {
		this.filterWindow.fadeIn();
		$('body').addClass('lock');
	}

	closeFilterWindow() {
		this.filterWindow.fadeOut();
		$('body').removeClass('lock');
	}

	render(){
		$('.filter-window').remove();

		this.filterWindow = $('<div>', {class: 'filter-window'});
		const filterContent = $('<div>', {class: 'filter-content'});
		this.filterBody = $('<div>', {class: 'filter-body'});
		const filterButtons = $('<div>', {class: 'filter-buttons'});

		this.btnFilter = $('<button>', {id: 'btn-filter', class: 'uk-button uk-button-primary', text: i18n.tr('Фильтровать')});
		this.btnFilterDrop = $('<button>', {id: 'btn-filter-drop', class: 'uk-button uk-button-default uk-margin-small-left', text: i18n.tr('Сбросить')});

		filterContent.append(
			'<div class="filter-close"><span uk-icon="icon: close; ratio: 1.8"></span></div>',
			`<div class="filter-header"><h4>${i18n.tr('Расширенный поиск')}</h4></div>`,
			this.filterBody, filterButtons
		);

		this.filterBody.append(`
			<div id="rd-params-block">
        <div class="uk-grid-small uk-grid uk-margin-bottom uk-margin-top" uk-grid>
          <div style="width: calc(100% - 60px);">
            <select class="uk-select" id="rd-form-param-list"></select>
          </div>
        </div>

        <label class="uk-form-label" for="rd-table-params">${i18n.tr('Условия')}:</label>
        <div class="uk-form-controls rd-table-params-container">
          <table id="rd-table-params" class="uk-table uk-table-justify uk-table-divider"></table>
        </div>
      </div>
		`);

		filterButtons.append(this.btnFilter, this.btnFilterDrop);

		this.filterWindow.append(filterContent);

		$('body').append(this.filterWindow);
	}

	async _parseFormData() {
		const formData = {
			component: {},
			label: {}
		};

		const { properties } = this._formDefinition;
		const columns = this._registryInfo?.columns ?? [];
		const columnsMap = Object.fromEntries(columns.filter(c => c.label).map(c => [c.columnID, c]));

		const getItem = async (item) => {
			if (['listbox', 'check', 'radio'].includes(item.type)) await getDictionaryItems(item);
			if (item.type === 'reglink') item.registryID = item.config.dateFormat;
			if (item.type === 'entity') item.entity = item.config.entity;

			formData.label[item.id] = columnsMap[item.id] ?? { label: item.id };
			formData.component[item.id] = item;
		};

		for (const item of properties) {
			if (item.type === 'label') continue;

			if (item.type === 'table' && !item.config.appendRows) {
				for (const child of item.properties) {
					if (child.type !== 'label') await getItem(child);
				}
			} else {
				await getItem(item);
			}
		}

		return formData;
	}

	async update() {
		try {
			this._registryInfo = await appAPI.getRegistryInfo(this._registryCode);
			this._formDefinition = await appAPI.getAsfDefinition(this?._registryInfo?.formId);

			let saveParam = sessionStorage.getItem(`filterParam_${Cons.getCurrentPage().code}_${this._registryCode}`);
			RD.reset();

			if(saveParam) {
				saveParam = JSON.parse(saveParam);

				if (saveParam.formData && Object.keys(saveParam.formData.component || {}).length > 0) {
					RD.formData = saveParam.formData;
					RD.paramSearch = saveParam.paramSearch;
					for (const key in RD.paramSearch) selectParameter(RD.paramSearch[key].field, key);
				} else {
					sessionStorage.removeItem(key);
					RD.formData = await this._parseFormData();
					sessionStorage.setItem(`filterParam_${Cons.getCurrentPage().code}_${this._registryCode}`, JSON.stringify({
						formData: RD.formData,
						paramSearch: RD.paramSearch
					}));
				}
			} else {
				RD.formData = await this._parseFormData();
				sessionStorage.setItem(`filterParam_${Cons.getCurrentPage().code}_${this._registryCode}`, JSON.stringify({
          formData: RD.formData,
          paramSearch: RD.paramSearch
        }));
			}

			// initParamList();

			console.log('RegistryAdvancedSearch', {
				RDformData: RD.formData,
				RDparamSearch: RD.paramSearch
			})

		} catch (err) {
			console.log(`[RegistryAdvancedSearch] ERROR: ${err.message}`);
		}
	}
}