import { displayNumber } from '../../util/helpers.js';

import { Edge } from './edge.js';

export class Vertex {
  constructor(graph, type, id, data, options = {}) {
    this.graph = graph;
    this.type = type;
    this.id = id;
    this.data = data;
    this.label = options.label ?? this.id;
    this.options = options;
    this.edges = {
      from: [],
      to: [],
    };
    this.installedClickCallback = false;
    this.properties = options.properties ?? new Map();
  }

  toJSON() {
    return {
      id: this.id,
      type: this.type,
      label: this.label,
      properties: Array.from(this.properties.entries()).reduce((props, [key, value]) => {
        props[key] = value;
        return props;
      }, {}),
    };
  }

  reset() {
    this.installedClickCallback = false;
  }

  getEdges(type, away) {
    return this.edges[away ? 'from' : 'to'].filter(
      (edge) => edge.type === type,
    );
  }

  setProperty(key, value) {
    this.properties.set(key, value);
    return this;
  }

  displayVertex() {
    if (this.options.hide) {
      return;
    }

    let html = '';
    if (this.type) {
      html += `<span class='small'>${this.type}</span>`;
    }
    html += `${this.label || this.id}`;
    html += '<table>';
    console.log('displayVertex', { properties: this.properties });
    for (const [key, value] of this.properties.entries()) {
      const displayValue = typeof value === 'number' ? displayNumber(value) : value;
      html += `<tr><td>${key}</td><td>${displayValue}</td></tr>`;
    }
    html += '</table>';
    if (this.label && this.id !== this.label) {
      html += `<span class=small>${this.id}</span><br>`;
    }
    html = html.replaceAll(/\n\s*/g, '');
    this.graph.flowchart?.log(`${this.id}["${html}"]`);

    if (this.graph.editable && !this.installedClickCallback) {
      this.graph.flowchart?.log(`click ${this.id} WDGHandler${this.graph.index} "Edit Vertex ${this.id}"`);
      this.installedClickCallback = true;
    }
  }

  static prepareEditorDocument(graph, doc, vertexId) {
    const vertex = vertexId ? graph.getVertex(vertexId) : undefined;
    const form = doc.form().lastElement;

    if (vertex) {
      form.button({
        name: 'New Vertex',
        cb: () => {
          graph.resetEditor();
        },
      });
    }
    doc.remark(`<h3>${vertex ? 'Edit' : 'Add'} Vertex</h3>`, { parentEl: form.el });
    form
      .textField({
        name: 'id', defaultValue: vertex?.id,
      })
      .textField({ name: 'type', defaultValue: vertex?.type })
      .textField({ name: 'label', defaultValue: vertex?.label });

    doc.remark('<h4>Properties</h4>', { parentEl: form.el });
    const subFormArray = form.subFormArray({ name: 'properties' }).lastItem;
    const addPropertyForm = (key, value) => {
      const { subForm } = form.subForm({ subFormArray }).lastItem;
      subForm.textField({ name: 'key', defaultValue: key })
        .textField({ name: 'value', defaultValue: value })
        .button({
          name: 'Remove Property',
          cb: () => subFormArray.remove(subForm),
        })
        .remark('<br>');
    };

    if (vertex) {
      for (const [key, value] of vertex.properties.entries()) {
        addPropertyForm(key, value);
      }
    }

    form.button({
      name: 'Add Property',
      cb: () => addPropertyForm('', ''),
    });

    form.submit({
      name: 'Save',
      cb: ({ form: { value: formValue } }) => {
        let fullRedraw = false;
        if (vertex && formValue.id !== vertex.id) {
          fullRedraw = true;
        }
        // TODO: preserve data types of properties
        formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value]));
        if (vertex) {
          Object.assign(vertex, formValue);
          vertex.displayVertex();
        } else {
          const {
            type, id, label, properties,
          } = formValue;
          const newVertex = graph.addVertex(type, id, null, label, { properties });
          Object.assign(newVertex, formValue);
          doc.clear();
          Vertex.prepareEditorDocument(graph, doc, newVertex.id);
        }
        if (fullRedraw) {
          graph.redraw();
        }
      },
    });

    if (vertex) {
      form.button({
        name: 'Delete Vertex',
        cb: () => {
          graph.deleteVertex(vertex.id);
          graph.redraw();
          graph.resetEditor();
        },
      });

      doc.remark('<h3>New Edge</h3>', { parentEl: form.el });
      const newEdgeForm = doc.form({ name: 'newEdge' }).lastElement;
      newEdgeForm.textField({ name: 'to' });
      newEdgeForm.textField({ name: 'type' });
      newEdgeForm.textField({ name: 'weight' });
      newEdgeForm.submit({
        name: 'Save',
        cb: ({ form: { value: { to, type, weight } } }) => {
          graph.setEdgeWeight(type, vertex, to, weight, null);
          doc.clear();
          Edge.prepareEditorDocument(graph, doc, vertex.id, to);
        },
      });
    }

    form.button({
      name: 'Cancel',
      cb: () => graph.resetEditor(),
      parentEl: doc.el,
    });

    return doc;
  }
}