import EditableInput from './EditableInput';
import APIClient from '../APIClient';

class LayoutTreeDataGridModel {
  constructor(root, apiRequest) {
    this.dataRoot = root;
    this.apiRequest = apiRequest;
    console.log('LayoutTreeDataGridModel', this.dataRoot);
  }

  getNodePath(node, targetId, path = '') {
    // Check if the current node is the target node
    if (node.id === targetId) {
      return path;
    }

    // If the current node has children, iterate over them
    if (node.children) {
      for (let i = 0; i < node.children.length; i++) {
        // Construct the path to the current child
        const currentPath = path ? `${path}.children[${i}]` : `children[${i}]`;

        // Recursively call findPath on the child
        const resultPath = this.getNodePath(node.children[i], targetId, currentPath);

        // If resultPath is not null, the target has been found in the subtree
        if (resultPath) {
          return resultPath;
        }
      }
    }

    // Return null if the target is not found in this subtree
    return null;
  }
}

class LayoutTreeDataGridView {
  constructor(selector) {
    console.log({selector});
    this.table = $('<table>').appendTo($(selector));
    this.table.addClass('layout_table');
    this.table.css({'width': 'inherit', 'border-collapse': 'collapse', 'border': '1px gray solid'});
    console.log(this.table);
  }

  addHeaders(fields) {
    const thead = $('<thead>').appendTo(this.table);
    const tr = $('<tr>').appendTo(thead);
    tr.addClass('layout-grid-row');

    fields.forEach(field => {
      const th = $('<th>').html(field.label).appendTo(tr);
      th.css({'width': field.width + 'px', 'border': '1px gray solid', 'text-align': 'center'});
    });
  }

  toggleChildrenVisibility(nodeId, isExpanded) {
    const selector = `.treegrid-parent-${nodeId}`;
    $(selector).each((_, el) => {
      $(el).toggle(!isExpanded);

      if (isExpanded) {  // If we are hiding the children
        const childNodeId = $(el).data('nodeId'); // Assume data-nodeId is set when rows are created
        this.toggleChildrenVisibility(childNodeId, true);  // Recursively hide all descendants
        const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
        expander.html(isExpanded ? '+' : '-');
      }
    });
  }

  setupExpander(cell, callback) {
    const expander = $('<span>').addClass('treegrid-expander').html('-').appendTo(cell);
    expander.click(callback);
  }
}

class LayoutTreeGridController {
  constructor(model, view, options, totalMessageLengthSelector) {
    this.model = model;
    this.view = view;
    this.options = options;
    this.apiClient = new APIClient();
    this.totalMessageLengthSelector = totalMessageLengthSelector;
  }

  init(callback) {
    this.view.addHeaders(this.options);
    this.processNode(this.model.dataRoot, 0);
    this.setupEditableFields();
    this.callback = callback;
  }

  async rebuild(dataRoot) {
    this.model.dataRoot = dataRoot;
    this.view.table.children().remove();
    this.view.addHeaders(this.options);
    this.processNode(this.model.dataRoot, 0);
    this.setupEditableFields();
    this.callback(await this.concatenateFieldValues(this.model.dataRoot), this.model.dataRoot);
  }

  getIndent(depth) {
    return new Array(depth + 1).join('<span class="treegrid-indent"></span>');
  }

  async fillPadding(value, dataLength) {
    let paddedValue = await this.apiClient.processPreScriptAndVariables(this.model.apiRequest, value.toString());

    while (paddedValue.length < dataLength) {
      paddedValue = ' ' + paddedValue; // Prepend a space character to the string
    }

    return paddedValue;
  }

  async concatenateFieldValues(node) {
    let concatenatedValues = '';

    // Process the current node if it is of type 'Field'
    if (node.loutItemType === 'Field') {
      concatenatedValues += await this.fillPadding(node.value, node.loutItemLength) || '';
    }

    // Recursively process each child
    if (node.children && node.children.length > 0) {
      for (const child of node.children) {
        concatenatedValues += await this.concatenateFieldValues(child);
      }
    }

    return concatenatedValues;
  }

  sumFieldLoutItemLengths() {
    const queue = [this.model.dataRoot];
    let sum = 0;

    while (queue.length > 0) {
      const node = queue.shift();

      if (node.loutItemType === 'Field') {
        sum += parseInt(node.loutItemLength) || 0; // Add the loutItemLength of the node, or 0 if undefined
      }

      if (node.children && node.children.length > 0) {
        for (const child of node.children) {
          queue.push(child);
        }
      }
    }
    return sum;
  }

  setupEditableFields() {
    this.view.table.find('.editable').each((index, editableElement) => {
      const editableInput = new EditableInput({
        name: editableElement.dataset.name,
        value: editableElement.dataset.value,
        path: editableElement.dataset.path
      });

      editableInput.init(editableElement, async (value) => {
        _.set(this.model.dataRoot, editableElement.dataset.path, value);
        this.callback(await this.concatenateFieldValues(this.model.dataRoot), this.model.dataRoot);
      });
    });

    this.totalMessageLengthSelector.text(this.sumFieldLoutItemLengths());
  }

  addCell(row, node, fieldInfo, depth) {
    const td = $('<td>').appendTo(row);
    const padding = fieldInfo.isFirst ? this.getIndent(depth) : '';
    const nodePath = this.model.getNodePath(this.model.dataRoot, node.id) + '.value';

    if (node.loutItemType === 'Field' && fieldInfo.type === 'textfield') {
      const div = $('<div>');
      div.addClass('editable');
      div.appendTo(td);
      div.attr('data-name', fieldInfo.name);
      div.attr('data-value', node[fieldInfo.name]);
      div.attr('data-path', nodePath);
    } else {
      td.html(padding); // Default to just displaying the content
    }

    if (fieldInfo.width) {
      td.css({'width': fieldInfo.width + 'px', 'border': '1px gray solid'});
    }

    if (fieldInfo.align) {
      td.css({'text-align': fieldInfo.align});
    }

    return td;
  }

  addRow(node, depth) {

    const tr = $('<tr>').addClass('treegrid-' + node.id).data('nodeId', node.id);  // Storing node ID for easy access later

    if (depth === 0) {
      tr.css({'display': 'none'});
    }

    if (node.parent) {
      const parentSelector = `.treegrid-${node.parent}`;
      const lastChildSelector = `.treegrid-parent-${node.parent}`;

      if ($(this.view.table).find(lastChildSelector).length > 0) {
        const lastChild = $(this.view.table).find(lastChildSelector).last();
        tr.insertAfter(lastChild);
      } else {
        tr.insertAfter($(this.view.table).find(parentSelector));
      }
      tr.addClass('treegrid-parent-' + node.parent);
    } else {
      tr.appendTo(this.view.table);
    }

    return tr;
  }

  //processNode in bfs
  processNode(node, depth) {
    const queue = [{node, depth}];
    while (queue.length > 0) {
      const {node, depth} = queue.shift();
      const tr = this.addRow(node, depth);

      this.options.forEach((fieldInfo) => {
        const content = node[fieldInfo.name];
        const td = this.addCell(tr, node, fieldInfo, depth);

        if (fieldInfo.isFirst) {
          if (node.children && node.children.length > 0) {
            this.view.setupExpander($(tr.children()[0]), () => this.handleExpanderClick(node.id));
          }
        }

        if (fieldInfo.type !== 'textfield') {
          td.append(content);
        }
      });

      if (node.children && node.children.length > 0) {
        for (const child of node.children) {
          queue.push({node: child, depth: depth + 1});
        }
      }
    }
  }

  handleExpanderClick(nodeId) {
    const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
    const isExpanded = expander.html() === '-';
    expander.html(isExpanded ? '+' : '-');
    this.view.toggleChildrenVisibility(nodeId, isExpanded);
  }
}

class LayoutTreeDataGrid {
  constructor(selector, root, apiRequest, options, totalMessageLengthSelector) {
    this.model = new LayoutTreeDataGridModel(root, apiRequest);
    this.view = new LayoutTreeDataGridView(selector);
    this.controller = new LayoutTreeGridController(this.model, this.view, options, totalMessageLengthSelector);
  }

  init(callback) {
    this.controller.init(callback);
  }

  rebuild(dataRoot) {
    this.controller.rebuild(dataRoot);
  }

}

export default LayoutTreeDataGrid;
