import * as monaco from 'monaco-editor';
import APIScenarioExecutor from './APIScenarioExecutor';
import Drawflow from 'drawflow';

class APIScenario {

  constructor(apiTester, contextPath) {
    this.scenarioList = [];
    this.currentScenario = null;
    this.apiTester = apiTester;
    this.contextPath = contextPath || '/';
  }

  getAPIScenarioListURL() {
    return this.contextPath + 'mgmt/api_scenario/list.do';
  }

  getAPIScenarioCreateURL() {
    return this.contextPath + 'mgmt/api_scenario/create.do';
  }

  getAPIScenarioUpdateURL() {
    return this.contextPath + 'mgmt/api_scenario/update.do';
  }

  getAPIScenarioDeleteURL() {
    return this.contextPath + 'mgmt/api_scenario/delete.do';
  }

  init() {
    this.loadScenarioList();
    this.outputEditor = monaco.editor.create(document.getElementById('output'), {
      value: '',
      automaticLayout: true
    });
    this.bindEvents();

    let id = document.getElementById('drawflow');
    this.editor = new Drawflow(id);
    this.editor.reroute = true;
    this.editor.reroute_fix_curvature = true;
    this.editor.force_first_input = true;
    this.editor.start();
    // this.editor.on('contextmenu', (event) => {
    //     let node = this.editor.getNodeFromId(event.target.id);
    // })

    this.showOutput = true;
  }

  loadScenarioList(callback) {
    const me = this;
    this.currentAjaxRequest = $.ajax({
      url: this.getAPIScenarioListURL(),
      type: 'GET',
      contentType: 'application/json',
      success: function(data) {
        me.scenarioList = data;
        let drawflowData = {drawflow: {Home: {data: {}}}};
        data.forEach((scenario) => {
          let scenarioData = null;
          try {
            scenarioData = JSON.parse(scenario.scenario);
          } catch (e) {
            scenarioData = {};
          }
          drawflowData.drawflow[scenario.id] = {data: scenarioData};
        });

        me.editor.import(drawflowData);
        me.editor.changeModule('Home');
      },
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.renderScenarioList();
        if (callback) {
          callback();
        }
        me.hideLoadingOverlay();
      }
    });
  }

  getScenarioList() {
    return this.scenarioList;
  }

  openScenario(event) {
    event.preventDefault();
    let scenarioId = $(event.target).closest('li').data('id');
    this.stopScenario();
    this.openScenarioById(scenarioId);
  }

  openScenarioById(scenarioId) {
    document.querySelectorAll('.api-scenario-list li').forEach((node) => {
      node.classList.remove('selected');
    });

    //find li by data-id and add class selected
    document.querySelector(`.api-scenario-list li[data-id="${scenarioId}"]`).classList.add('selected');

    this.editor.changeModule(scenarioId);
    this.currentScenario = scenarioId;
  }

  getScenario(scenarioId) {
    return this.getScenarioList().find((scenario) => scenario.id === scenarioId);
  }

  showLoadingOverlay() {
    $('#loading-overlay').show();
  }

  hideLoadingOverlay() {
    $('#loading-overlay').hide();
  }

  showNewScenarioModal() {
    $('#new_scenario_modal').find('input').val('');
    $('#new_scenario_modal').modal('show');
  }

  createScenario() {
    const name = $('#new_scenario_name').val();
    const me = this;
    me.showLoadingOverlay();
    this.currentAjaxRequest = $.ajax({
      url: this.getAPIScenarioCreateURL(),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({name: name, scenario: '{}', description: ''}),
      success: function(data) {
        me.loadScenarioList(() => {
          me.openScenarioById(data.id);
          let start = `
                        <div>
                            <div><i class="fas fa-play"></i> Start </div>
                        </div>
                    `;
          me.editor.addNode('Start', 0, 1, 100, 100, 'start', {}, start);
          me.saveScenario();
        });
      },
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.hideLoadingOverlay();
        $('#new_scenario_modal').modal('hide');
      }
    });
  }

  saveScenario() {
    if (this.currentScenario !== null) {
      let data = this.editor.export();
      console.log(data.drawflow[this.currentScenario].data);
      let updatedScenario = this.getScenario(this.currentScenario);
      updatedScenario.scenario = JSON.stringify(data.drawflow[this.currentScenario].data);

      const me = this;
      me.showLoadingOverlay();
      this.currentAjaxRequest = $.ajax({
        url: this.getAPIScenarioUpdateURL(),
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(updatedScenario),
        error: function(response, textStatus, errorThrown) {
          me.hideLoadingOverlay();
          $('.toast-body').text(response.responseJSON.error);
          $('.toast').toast('show');
        },
        complete: function() {
          me.hideLoadingOverlay();
          $('.toast-body').text('저장되었습니다.');
          $('.toast').toast('show');
        }
      });
    }
  }

  showDeleteModal(event) {
    const id = $(event.currentTarget).closest('li').data('id');
    $('#confirm_delete_scenario_modal').find('.confirm_delete_scenario').data('id', id);
    $('#confirm_delete_scenario_modal').modal('show');
  }

  removeScenario(event) {
    const id = $(event.currentTarget).data('id');
    const me = this;
    me.showLoadingOverlay();
    this.currentAjaxRequest = $.ajax({
      url: this.getAPIScenarioDeleteURL(),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({id: id}),
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.hideLoadingOverlay();
        $('#confirm_delete_scenario_modal').modal('hide');
        $('.toast-body').text('삭제되었습니다.');
        $('.toast').toast('show');
        me.editor.changeModule('Home');
        me.loadScenarioList(() => {
          if (me.getScenarioList().length > 0) {
            me.openScenarioById(me.getScenarioList()[0].id);
          }

        });
      }
    });
  }

  toggleOutput() {
    // Define your selectors as constants for easy changes and better readability
    const scenarioContentSelector = '.api-scenario-content';
    const scenarioOutputSelector = '.api-scenario-output';
    const scenarioOutputBodySelector = '.api-scenario-output-body';
    const drawflowSelector = '#drawflow';
    const scenarioEditorSelector = '.scenario_editor';

    // Ensure parentHeight is a number
    let parentHeight = parseInt($(scenarioContentSelector).css('height'), 10);

    // Toggle the showOutput state
    this.showOutput = !this.showOutput;

    if (!this.showOutput) {
      $(scenarioOutputSelector).css('flex', '0');
      $(scenarioOutputBodySelector).hide();

      let editorHeight = $(scenarioEditorSelector).css('height');
      $(drawflowSelector).css('height', editorHeight);
      $(scenarioOutputSelector).removeClass('halfHeight');
      $(drawflowSelector).removeClass('halfHeight');
    } else {
      $(scenarioOutputSelector).css('flex', '1');
      $(scenarioOutputSelector).addClass('halfHeight');
      $(scenarioOutputBodySelector).show();
      $(drawflowSelector).addClass('halfHeight');
      $(drawflowSelector).css('height', parentHeight / 2);
    }

    // Adjustments for when parentHeight isn't a valid number
    if (isNaN(parentHeight)) {
      console.error('parentHeight is not a number. Check the height of the api-scenario-content element.');
      // Fallback or additional error handling can be placed here
    }
  }

  resizeEditor() {
    //FIXME: 창 크기 변경시 자동으로 editor 크기가 조정되지 않아서, 0으로 강제 변경후 다시 layout() 호출
    this.outputEditor.layout({
      width: 0
    });
    this.outputEditor.layout({});

  }

  runScript() {
    console.log(this.editor.export().drawflow);
    let graph = APIScenarioExecutor.extractGraphFromStart(this.editor.export().drawflow[this.currentScenario].data);
    this.executor = new APIScenarioExecutor(graph, this.apiTester, this.outputEditor);
  }

  runScenarioAll() {
    if (!this.executor) {
      this.runScript();
    }
    if (!this.executor.isDone) {
      this.executor.executeAll();
    } else {
      this.executor.reset();
    }
  }

  runScenarioStep() {
    if (!this.executor) {
      this.runScript();
    }
    if (!this.executor.isDone) {
      this.executor.executeCurrentNode();
      this.executor.moveToNextNode();
    } else {
      this.executor.reset();
    }

  }

  resetScenario() {

  }

  pauseScenario() {

  }

  stopScenario() {
    if (this.executor) {
      this.executor.reset();
      this.executor = null;
    }
    this.outputEditor.setValue('');
  }

  addStart() {
    let start = `
            <div>
                <div><i class="fas fa-play"></i> Start </div>
            </div>
        `;
    this.editor.addNode('Start', 0, 1, 100, 100, 'start', {}, start);
  }

  bindEvents() {
    const events = [
      {type: 'click', selector: '.open_scenario', action: this.openScenario.bind(this)},
      {type: 'click', selector: '.export', action: this.runScript.bind(this)},
      {type: 'click', selector: '.toggle_output', action: this.toggleOutput.bind(this)},
      {type: 'click', selector: '.run_scenario_step', action: this.runScenarioStep.bind(this)},
      {type: 'click', selector: '.run_scenario_all', action: this.runScenarioAll.bind(this)},
      {type: 'click', selector: '.reset_scenario', action: this.resetScenario.bind(this)},
      {type: 'click', selector: '.pause_scenario', action: this.pauseScenario.bind(this)},
      {type: 'click', selector: '.stop_scenario', action: this.stopScenario.bind(this)},
      {type: 'click', selector: '#new_scenario', action: this.showNewScenarioModal.bind(this)},
      {type: 'click', selector: '.create_scenario', action: this.createScenario.bind(this)},
      {type: 'click', selector: '.save_scenario', action: this.saveScenario.bind(this)},
      // {type: 'click', selector: '.add_start', action: this.addStart.bind(this)},
      {type: 'click', selector: '.delete_scenario', action: this.showDeleteModal.bind(this)},
      {type: 'click', selector: '.confirm_delete_scenario', action: this.removeScenario.bind(this)},
      {type: 'click', selector: '.stop_scenario', action: this.stopScenario.bind(this)}
    ];

    for (let event of events) {
      $(document).on(event.type, event.selector, event.action);
    }

    window.addEventListener('resize', this.resizeEditor.bind(this));
    var me = this;

    $('#drawflow').droppable({
      drop: function(event, ui) {
        const x = ui.offset.left - $(this).offset().left;
        const y = ui.offset.top - $(this).offset().top;

        const dataset = ui.draggable[0].dataset;
        console.log(dataset);
        var node = `<div><div class="title-box">API</div><div class="box">${dataset.name}</div></div>`;

        me.editor.addNode(dataset.name, 1, 1, x, y, dataset.nodeType, dataset, node);
      }
    });
  }

  scenarioListTemplate(scenario) {
    return `
        <li class="d-flex justify-content-between " data-id="${scenario.id}">
            <a href="#" class="d-inline-flex align-items-center rounded open_scenario">${scenario.name}</a>
            <div class="d-flex justify-content-between">
                <button class="p-1 reset-button edit_scenario"><i class="fa fa-sharp fa-file-edit"></i></button>
                <button class="p-1 reset-button delete_scenario"><i class="fa fa-trash-can"></i></button>
            </div>
        </li>
        `;
  }

  renderScenarioList() {
    let scenarioList = this.getScenarioList();
    let scenarioListHtml = scenarioList.map((scenario) => this.scenarioListTemplate(scenario)).join('');
    $('.api-scenario-list').html(scenarioListHtml);

    let nodes = document.querySelectorAll('.api-scenario-list li');
    nodes.forEach((node) => {
      if ($(node).data('id') === this.currentScenario) {
        node.classList.add('selected');
      }
    });
  }
}

export default APIScenario;
