import { useEditFormStore } from '../../../../store/storeForm.js';
import Settings from './Settings/Settings.js';
import { components as localComponents } from "../../../shared/formFields.js";
import useScopedStyleMixin from '../../../useScopedStyleMixin.js';
import FormFieldStyles from './FormFieldStyles.js';

const EMPTY_VALUES = ['', '[]', 'undefined', 'null', 'NaN'];

const isEmpty = (value) => EMPTY_VALUES.includes(String(value));

const FormField = {
  name: 'edit-form-field',
  components: {
    Settings,
    ...localComponents
  },
  data() {
    return {
      componentClass: 'editform-field',
    }
  },
  props: {
    storeFieldId: String,
    dataSource: Object,
    fieldsSource: Object,
    table: String,
    link: String,
  },
  setup(props) {
    const store = useEditFormStore();

    //Получаем описания полей
    const activeFieldsSource = props.fieldsSource || store.fields;

    const fieldType = activeFieldsSource[props.storeFieldId]
      ? activeFieldsSource[props.storeFieldId].type
      : 'field-not-found';

    const field = activeFieldsSource[props.storeFieldId] || {};

    //Получаем источник данных
    const { data } = Pinia.storeToRefs(store);

    var options = field.options || null;

    //Здесь храним ID таблицы, для которой загружаем элементы (optionsFrom.source)

    return {
      store,
      field,
      fieldType,
      activeFieldsSource,
      options,
      data
    }
  },
  methods: {
    async fetchOptions() {
      if (!this.computedTableId) return;
  
      const tableId = this.computedTableId;
      const tableFieldType = this.field.optionsFrom?.fieldsType || 0;
      const isRoot = this.store.isRoot();
  
      try {
        let dataPromise;
  
        switch (this.field.optionsFrom?.source) {
          case "fields":
            dataPromise = window.CB.metadata.getFields(tableId, tableFieldType, isRoot, false, true);
            break;
          case "subtables":
            dataPromise = window.CB.metadata.getSubtables(tableId, isRoot);
            break;
          case "filters":
            dataPromise = window.CB.metadata.getFilters(tableId, isRoot);
            break;
          default:
            return;
        }
  
        let result = await dataPromise;

        if (this.field.optionsFrom.extraOptions) {
          result = Object.assign(result, this.field.optionsFrom.extraOptions);
        }

        this.loadOptions(result);
      } catch (error) {
        console.error("Ошибка загрузки данных:", error);
      }
    },

    optionsForValue() {
      //Типы полей, в которых список options не используется для значения
      let typesNoValue = ['template'];
      return !typesNoValue.includes(this.fieldType);
    },
  
    loadOptions(data) {
      this.options = data;
      if (
        this.fieldType === "checkbox" 
        && !Array.isArray(this.activeDataSource[this.storeFieldId])
      ) {
        this.activeDataSource[this.storeFieldId] = [];
      }
      /**
       * @todo Перенести очистку `activeDataSource[this.storeFieldId]` в родительский компонент формы.
       * Это логика, связанная с бизнес-правилами формы, а не с поведением самой строки.
       * Родитель должен отслеживать изменения `tableId` и сбрасывать связанные поля.
       * Здесь мы просто эмитим событие наверх
       */
      //Сбрасываем значение (если список options используется для значения)
      if (this.optionsForValue()) {
        if(!data[this.activeDataSource[this.storeFieldId]]) {
          this.activeDataSource[this.storeFieldId] = [];
        }
      }
      
      this.$forceUpdate();
    },
    /**
     * Загружает данные таблиц
     */
    async loadFromTables() {
      this.loadOptions(await window.CB.metadata.getDataTables(this.store.isRoot()));
    },
    /**
     * Загружает данные групп
     */
    async loadFromGroups() {
      this.loadOptions(await window.CB.metadata.getGroups());
    },
    /**
     * Отслеживает изменение поля списка и обновляет опции
     */
    loadFromList() {
      let fieldIdent = this.field.optionsFrom?.field;
      this.$watch(() => this.data?.[fieldIdent], (newValue) => {
        this.options = [];
        if (Array.isArray(newValue)) {
          newValue.forEach((item, index) => {
            let key = this.field.optionsFrom?.value ? item[this.field.optionsFrom.value] : index;
            this.options[key] = item[this.field.optionsFrom?.label];
          });
        };
        this.$forceUpdate();
      }, { deep: true, immediate: true });
    },
    /**
     * Загружает данные для селекта из options
     */
    loadFromOptions() {
      let optFieldIdent = this.field.optionsFrom?.field;
      if (this.field.optionsFrom?.optionsDir) {
        this.$watch(() => this.activeDataSource?.[optFieldIdent], (newValue) => {
          this.updateOptions(newValue, optFieldIdent);
          this.$forceUpdate();
        }, { deep: true, immediate: true });
      } else {
        this.$watch(() => this.data?.__options?.[optFieldIdent], (newValue) => {
          this.updateOptionsSimple(newValue);
          this.$forceUpdate();
        }, { deep: true, immediate: true });
      }
    },
    /**
     * Обновляет опции на основе нового значения
     * @param {*} newValue - Новое значение
     * @param {string} optFieldIdent - Идентификатор поля
     */
    updateOptions(newValue, optFieldIdent) {
      if (Array.isArray(newValue)) {
        newValue = newValue[0];
      }
      let options = {};
      this.options = [];

      if (this.field.optionsFrom?.from_list) {
        optFieldIdent = this.field.optionsFrom?.from_list_options;
        for (const listItem of this.data[this.field.optionsFrom?.from_list]) {
          if (listItem["id"] == newValue) {
            newValue = listItem[optFieldIdent];
            break;
          }
        }
      }

      let optionsSource = this.data?.__options?.[optFieldIdent]?.[newValue];
      let optionsSourceEmpty = this.data?.__options?.[optFieldIdent]?.["__empty"];

      if (optionsSource) {
        options = optionsSource["__dir"]?.[this.field.optionsFrom?.optionsDir];
      } else if (optionsSourceEmpty) {
        options = optionsSourceEmpty["__dir"]?.[this.field.optionsFrom?.optionsDir];
      }

      for (let key in options) {
        if (options[key]?.name) {
          this.options[key] = options[key].name;
        }
      }
    },
    /**
     * Простое обновление списка опций
     * @param {Object} newValue - Объект с новыми значениями опций
     */
    updateOptionsSimple(newValue) {
      this.options = [];
      for (let key in newValue) {
        if (newValue[key]?.name) {
          this.options[key] = newValue[key].name;
        }
      }
    }
  },
  mixins: [useScopedStyleMixin, FormFieldStyles],
  mounted() {
    if (this.options) {
      Object.keys(this.options).forEach(key => {
        this.options[key] = this.$t(this.options[key]);
      });
    }
    if (this.field.optionsFrom) {
      switch (this.field.optionsFrom.source) {
        case "tables":
          this.loadFromTables();
          break;
        case "groups":
          this.loadFromGroups();
          break;
        case "list":
          this.loadFromList();
          break;
        case "options":
          this.loadFromOptions();
          break;
      }
    }
  },
  watch: {
    selectedValue: {
      handler(newValue) {
        this.$emit('change', { value: newValue, source: this.source });
      },
      immediate: true,
    },
    computedTableId: {
      handler(newValue) {
        if (!newValue) return;
        this.fetchOptions();
      },
      immediate: true
    },

    computedOptionsField: {
      handler(newValue) {
        if (!newValue) return;
        this.loadFromOptions(newValue);
      },
      immediate: true
    }
  },
  computed: {
    activeDataSource() {
      return this.dataSource || this.data;
    },    
    computedTableId() {
      if (this.field.optionsFrom?.parentId) {
        // Если передан конкретный ID таблицы, используем его
        return this.field.optionsFrom.parentId;
      } 
      if (this.field.optionsFrom?.parent) {
        // Берём ID из activeDataSource по названию поля
        let tableIdField = this.field.optionsFrom.parent;
        let newValue = this.activeDataSource?.[tableIdField];
  
        if (Array.isArray(newValue)) {
          newValue = newValue[0];
        }
        
        return newValue;
      }
    },
    computedOptionsField() {
      return this.field.optionsFrom?.field;
    },
    selectedValue() {
      if(isEmpty(this.activeDataSource[this.storeFieldId])) return '';
      return this.activeDataSource[this.storeFieldId];
    },
    show: function () {
      let result = true;
      if (this.field['showOn']) {
        if (typeof this.field['showOnValue'] !== 'undefined') {
          result = (this.activeDataSource[this.field.showOn] == this.field.showOnValue);
        } else {
          result = this.activeDataSource[this.field.showOn];
        }
      }
      return result;
    },
    showSettings() {
      const isDropdown = this.fieldType === 'dropdown';
      const source = this.field?.optionsFrom?.source;
      return isDropdown && !!source
    },
    source() {
      return this.field?.optionsFrom?.source
    },
    srcMatching() {
      const value = this.selectedValue;
      const computedTableId = this.computedTableId;

      return {
        tables: value ? `edit_table.php?table=${value}` : 'edit_cat.php',
        groups: value ? `edit_group.php?group=${value}` : 'edit_group.php',
        fields: value
          ? `edit_field.php?field=${value}`
          : `edit_field.php?table=${computedTableId}`,
        subtables: value ? `./edit_table.php?table=${value}` : 'edit_cat.php',
        filters: value 
          ? `edit_filter.php?table=${computedTableId}&filters=${value}`
          : `edit_filter.php?table=${computedTableId}`,
      }[this.source]
    },
  },
  template: /*html*/`
    <div
      :class="componentClass + hash"
    >
      <component 
        v-if="show" 
        :size="field.size" 
        v-bind:is="fieldType" 
        :link="link" 
        :required="field['required']" 
        :default="field['default']"        
        :data="data" 
        :dataSource="activeDataSource" 
        :fieldsSource="activeFieldsSource" 
        :dataValue="selectedValue" 
        v-bind:options="options" 
        :storeFieldId="storeFieldId"
        width="100%"
      />
      <Settings
        v-if="showSettings && srcMatching"
        :src="srcMatching"
      />
    </div>
  `
};
export default FormField