const { assetCategoryManager } = require('../../datasets/assetData');

class AssetMenu {
  constructor({ manager = assetCategoryManager, softwareType = null } = {}) {
    this.manager = manager;
    this.softwareType = softwareType;
    this.categories = this.manager
      .listCategories({ withMeta: true })
      .reduce((map, meta) => {
        map[meta.softwareType] = this.manager.getCategory(meta.softwareType);
        return map;
      }, {});
  }

  normalizeCategory(key) {
    const category = this.categories[key];
    if (!category) {
      return null;
    }

    return {
      ...category,
      label: category.label || category.softwareType || key,
      suppliers: (category.suppliers || []).map((supplier) => ({
        ...supplier,
        id: supplier.id || supplier.name,
        types: (supplier.types || []).map((type) => ({
          ...type,
          id: type.id || type.name,
          models: (type.models || []).map((model) => ({
            ...model,
            id: model.id || model.name,
            units: model.units || []
          }))
        }))
      }))
    };
  }

  resolveCategoryForNode(nodeName) {
    const keys = Object.keys(this.categories);
    if (keys.length === 0) {
      return null;
    }

    if (this.softwareType && this.categories[this.softwareType]) {
      return this.softwareType;
    }

    if (nodeName) {
      const normalized = typeof nodeName === 'string' ? nodeName.toLowerCase() : nodeName;
      if (normalized && this.categories[normalized]) {
        return normalized;
      }
    }

    return keys[0];
  }

  getAllMenuData(nodeName) {
    const categoryKey = this.resolveCategoryForNode(nodeName);
    const selectedCategories = {};

    if (categoryKey && this.categories[categoryKey]) {
      selectedCategories[categoryKey] = this.normalizeCategory(categoryKey);
    }

    return {
      categories: selectedCategories,
      defaultCategory: categoryKey
    };
  }

  getClientInitCode(nodeName) {
    const htmlCode = this.getHtmlInjectionCode(nodeName);
    const dataCode = this.getDataInjectionCode(nodeName);
    const eventsCode = this.getEventInjectionCode(nodeName);
    const saveCode = this.getSaveInjectionCode(nodeName);

    return `
      // --- AssetMenu for ${nodeName} ---
      window.EVOLV.nodes.${nodeName}.assetMenu =
        window.EVOLV.nodes.${nodeName}.assetMenu || {};

      ${htmlCode}
      ${dataCode}
      ${eventsCode}
      ${saveCode}

      window.EVOLV.nodes.${nodeName}.assetMenu.initEditor = function(node) {
        console.log('Initializing asset properties for ${nodeName}');
        this.injectHtml();
        this.wireEvents(node);
        this.loadData(node);
      };
    `;
  }

  getDataInjectionCode(nodeName) {
    return `
      // Asset data loader for ${nodeName}
      window.EVOLV.nodes.${nodeName}.assetMenu.loadData = function(node) {
        const menuAsset = window.EVOLV.nodes.${nodeName}.menuData.asset || {};
        const categories = menuAsset.categories || {};
        const defaultCategory = menuAsset.defaultCategory || Object.keys(categories)[0] || null;
        const elems = {
          supplier: document.getElementById('node-input-supplier'),
          type: document.getElementById('node-input-assetType'),
          model: document.getElementById('node-input-model'),
          unit: document.getElementById('node-input-unit')
        };

        function populate(selectEl, items = [], selectedValue, mapFn, placeholderText = 'Select...') {
          const previous = selectEl.value;
          const mapper = typeof mapFn === 'function'
            ? mapFn
            : (value) => ({ value, label: value });

          selectEl.innerHTML = '';

          const placeholder = document.createElement('option');
          placeholder.value = '';
          placeholder.textContent = placeholderText;
          placeholder.disabled = true;
          placeholder.selected = true;
          selectEl.appendChild(placeholder);

          items.forEach((item) => {
            const option = mapper(item);
            if (!option || typeof option.value === 'undefined') {
              return;
            }
            const opt = document.createElement('option');
            opt.value = option.value;
            opt.textContent = option.label;
            selectEl.appendChild(opt);
          });

          if (selectedValue) {
            selectEl.value = selectedValue;
            if (!selectEl.value) {
              selectEl.value = '';
            }
          } else {
            selectEl.value = '';
          }
          if (selectEl.value !== previous) {
            selectEl.dispatchEvent(new Event('change'));
          }
        }

        const resolveCategoryKey = () => {
          if (node.softwareType && categories[node.softwareType]) {
            return node.softwareType;
          }
          if (node.category && categories[node.category]) {
            return node.category;
          }
          return defaultCategory;
        };

        const categoryKey = resolveCategoryKey();
        node.category = categoryKey;
        const activeCategory = categoryKey ? categories[categoryKey] : null;

        const suppliers = activeCategory ? activeCategory.suppliers : [];
        populate(
          elems.supplier,
          suppliers,
          node.supplier,
          (supplier) => ({ value: supplier.id || supplier.name, label: supplier.name }),
          suppliers.length ? 'Select...' : 'No suppliers available'
        );

        const activeSupplier = suppliers.find(
          (supplier) => (supplier.id || supplier.name) === node.supplier
        );
        const types = activeSupplier ? activeSupplier.types : [];
        populate(
          elems.type,
          types,
          node.assetType,
          (type) => ({ value: type.id || type.name, label: type.name }),
          activeSupplier ? 'Select...' : 'Awaiting Supplier Selection'
        );

        const activeType = types.find(
          (type) => (type.id || type.name) === node.assetType
        );
        const models = activeType ? activeType.models : [];
        populate(
          elems.model,
          models,
          node.model,
          (model) => ({ value: model.id || model.name, label: model.name }),
          activeType ? 'Select...' : 'Awaiting Type Selection'
        );

        const activeModel = models.find(
          (model) => (model.id || model.name) === node.model
        );
        populate(
          elems.unit,
          activeModel ? activeModel.units || [] : [],
          node.unit,
          (unit) => ({ value: unit, label: unit }),
          activeModel ? 'Select...' : activeType ? 'Awaiting Model Selection' : 'Awaiting Type Selection'
        );
      };
    `;
  }

  getEventInjectionCode(nodeName) {
    return `
      // Asset event wiring for ${nodeName}
      window.EVOLV.nodes.${nodeName}.assetMenu.wireEvents = function(node) {
        const menuAsset = window.EVOLV.nodes.${nodeName}.menuData.asset || {};
        const categories = menuAsset.categories || {};
        const defaultCategory = menuAsset.defaultCategory || Object.keys(categories)[0] || null;
        const elems = {
          supplier: document.getElementById('node-input-supplier'),
          type: document.getElementById('node-input-assetType'),
          model: document.getElementById('node-input-model'),
          unit: document.getElementById('node-input-unit')
        };

        function populate(selectEl, items = [], selectedValue, mapFn, placeholderText = 'Select...') {
          const previous = selectEl.value;
          const mapper = typeof mapFn === 'function'
            ? mapFn
            : (value) => ({ value, label: value });

          selectEl.innerHTML = '';

          const placeholder = document.createElement('option');
          placeholder.value = '';
          placeholder.textContent = placeholderText;
          placeholder.disabled = true;
          placeholder.selected = true;
          selectEl.appendChild(placeholder);

          items.forEach((item) => {
            const option = mapper(item);
            if (!option || typeof option.value === 'undefined') {
              return;
            }
            const opt = document.createElement('option');
            opt.value = option.value;
            opt.textContent = option.label;
            selectEl.appendChild(opt);
          });

          if (selectedValue) {
            selectEl.value = selectedValue;
            if (!selectEl.value) {
              selectEl.value = '';
            }
          } else {
            selectEl.value = '';
          }
          if (selectEl.value !== previous) {
            selectEl.dispatchEvent(new Event('change'));
          }
        }

        const resolveCategoryKey = () => {
          if (node.softwareType && categories[node.softwareType]) {
            return node.softwareType;
          }
          if (node.category && categories[node.category]) {
            return node.category;
          }
          return defaultCategory;
        };

        const getActiveCategory = () => {
          const key = resolveCategoryKey();
          return key ? categories[key] : null;
        };

        node.category = resolveCategoryKey();

        elems.supplier.addEventListener('change', () => {
          const category = getActiveCategory();
          const supplier = category
            ? category.suppliers.find(
                (item) => (item.id || item.name) === elems.supplier.value
              )
            : null;
          const types = supplier ? supplier.types : [];
          populate(
            elems.type,
            types,
            node.assetType,
            (type) => ({ value: type.id || type.name, label: type.name }),
            supplier ? 'Select...' : 'Awaiting Supplier Selection'
          );
          populate(elems.model, [], '', undefined, 'Awaiting Type Selection');
          populate(elems.unit, [], '', undefined, 'Awaiting Type Selection');
        });

        elems.type.addEventListener('change', () => {
          const category = getActiveCategory();
          const supplier = category
            ? category.suppliers.find(
                (item) => (item.id || item.name) === elems.supplier.value
              )
            : null;
          const type = supplier
            ? supplier.types.find(
                (item) => (item.id || item.name) === elems.type.value
              )
            : null;
          const models = type ? type.models : [];
          populate(
            elems.model,
            models,
            node.model,
            (model) => ({ value: model.id || model.name, label: model.name }),
            type ? 'Select...' : 'Awaiting Type Selection'
          );
          populate(
            elems.unit,
            [],
            '',
            undefined,
            type ? 'Awaiting Model Selection' : 'Awaiting Type Selection'
          );
        });

        elems.model.addEventListener('change', () => {
          const category = getActiveCategory();
          const supplier = category
            ? category.suppliers.find(
                (item) => (item.id || item.name) === elems.supplier.value
              )
            : null;
          const type = supplier
            ? supplier.types.find(
                (item) => (item.id || item.name) === elems.type.value
              )
            : null;
          const model = type
            ? type.models.find(
                (item) => (item.id || item.name) === elems.model.value
              )
            : null;
          populate(
            elems.unit,
            model ? model.units || [] : [],
            node.unit,
            (unit) => ({ value: unit, label: unit }),
            model ? 'Select...' : type ? 'Awaiting Model Selection' : 'Awaiting Type Selection'
          );
        });
      };
    `;
  }

  getHtmlTemplate() {
    return `
      <!-- Asset Properties -->
      <hr />
      <h3>Asset selection</h3>
      <div class="form-row">
        <label for="node-input-supplier"><i class="fa fa-industry"></i> Supplier</label>
        <select id="node-input-supplier" style="width:70%;"></select>
      </div>
      <div class="form-row">
        <label for="node-input-assetType"><i class="fa fa-puzzle-piece"></i> Type</label>
        <select id="node-input-assetType" style="width:70%;"></select>
      </div>
      <div class="form-row">
        <label for="node-input-model"><i class="fa fa-wrench"></i> Model</label>
        <select id="node-input-model" style="width:70%;"></select>
      </div>
      <div class="form-row">
        <label for="node-input-unit"><i class="fa fa-balance-scale"></i> Unit</label>
        <select id="node-input-unit" style="width:70%;"></select>
      </div>
      <hr />
    `;
  }

  getHtmlInjectionCode(nodeName) {
    const htmlTemplate = this.getHtmlTemplate()
      .replace(/`/g, '\\`')
      .replace(/\$/g, '\\$');

    return `
      // Asset HTML injection for ${nodeName}
      window.EVOLV.nodes.${nodeName}.assetMenu.injectHtml = function() {
        const placeholder = document.getElementById('asset-fields-placeholder');
        if (placeholder && !placeholder.hasChildNodes()) {
          placeholder.innerHTML = \`${htmlTemplate}\`;
          console.log('Asset HTML injected successfully');
        }
      };
    `;
  }

  getSaveInjectionCode(nodeName) {
    return `
      // Asset save handler for ${nodeName}
      window.EVOLV.nodes.${nodeName}.assetMenu.saveEditor = function(node) {
        console.log('Saving asset properties for ${nodeName}');
        const menuAsset = window.EVOLV.nodes.${nodeName}.menuData.asset || {};
        const categories = menuAsset.categories || {};
        const defaultCategory = menuAsset.defaultCategory || Object.keys(categories)[0] || null;
        const resolveCategoryKey = () => {
          if (node.softwareType && categories[node.softwareType]) {
            return node.softwareType;
          }
          if (node.category && categories[node.category]) {
            return node.category;
          }
          return defaultCategory || '';
        };

        node.category = resolveCategoryKey();

        const fields = ['supplier', 'assetType', 'model', 'unit'];
        const errors = [];

        fields.forEach((field) => {
          const el = document.getElementById(\`node-input-\${field}\`);
          node[field] = el ? el.value : '';
        });

        if (node.assetType && !node.unit) {
          errors.push('Unit must be set when a type is specified.');
        }
        if (!node.unit) {
          errors.push('Unit is required.');
        }

        errors.forEach((msg) => RED.notify(msg, 'error'));

        const saved = fields.reduce((acc, field) => {
          acc[field] = node[field];
          return acc;
        }, {});
        console.log('[AssetMenu] save result:', saved);

        return errors.length === 0;
      };
    `;
  }
}

module.exports = AssetMenu;
