import * as monaco from 'monaco-editor';
import APIClient from './APIClient';
import EditableInput from './components/EditableInput';
import {createPopper} from '@popperjs/core/lib/popper-lite';
import PropertyTable from './components/PropertyTable';
import VariableSnippet from './components/VariableSnippet';

class APITester {

  constructor(contextPath) {
    this.servers = [];
    this.apiTabTitleTarget = '#apiRequestTabTitle';
    this.apiTabContentTarget = '#apiRequestTabContent';
    this.collectionTarget = '#side_menubar';
    this.currentIndex = 0;
    this.apiRequests = [];  //탭 열린 API
    this.collections = [];  //API 컬렉션
    this.requestEditors = [];  //API request body editor
    this.responseEditors = []; //API response body editor
    this.consoleEditors = []; //API console editor
    this.preRequestEditors = [];
    this.postRequestEditors = [];

    this.apiClient = new APIClient();
    this.popperContent = document.getElementById('popperContent');
    this.popperInstance = null;
    this.contextPath = contextPath || '/';
  }

  getCollectionListURL() {
    return this.contextPath + 'mgmt/collections/list.do';
  }

  getCollectionCreateURL() {
    return this.contextPath + 'mgmt/collections/create.do';
  }

  getCollectionUpdateURL() {
    return this.contextPath + 'mgmt/collections/update.do';
  }

  getCollectionDeleteURL() {
    return this.contextPath + 'mgmt/collections/delete.do';
  }

  getAPISaveURL(collectionId) {
    return this.contextPath + `mgmt/collections/${collectionId}/apis/save.do`;
  }

  getAPIDeleteURL(collectionId) {
    return this.contextPath + `mgmt/collections/${collectionId}/apis/delete.do`;
  }

  renderServers(selectedServer) {
    const optionsHtml = this.servers.map(server => `<option value="${server.id}" ${selectedServer === server.id ? 'selected' : ''}  data-path="${server.basePath}">${server.name}</option>`).join('');

    return `<select class="form-select server" name="server" style="width: 180px; border-radius: 0;" required>
                <option value="">서버 선택</option>
                ${optionsHtml}</select>`;
  }

  basePath(serverId) {
    if (serverId === undefined || serverId === null || serverId === '') {
      return '';
    }
    return this.servers.filter(server => server.id == serverId)[0].basePath; // == : 타입 비교 없이 값만 비교
  }

  apiRequestTemplate(apiRequest, index) {
    return `
        <div class="tab-pane fade mt-1 api_request" id="api_request_${apiRequest.id}" style="height: 100%;">
            <div><span class="collection_name">${apiRequest.collectionName}</span> &gt; <span class="api_name">${apiRequest.name}</span></div>
            <div class="d-flex api_request_info">
                <div class="form-floating">
                    <select name="method" class="form-select method" style="width: 90px; border-bottom-right-radius: 0;border-top-right-radius: 0;">
                        <option value="POST"    ${apiRequest.method === 'POST' ? 'selected' : ''}>POST</option>
                        <option value="GET"     ${apiRequest.method === 'GET' ? 'selected' : ''}>GET</option>
                        <option value="PUT"     ${apiRequest.method === 'PUT' ? 'selected' : ''}>PUT</option>
                        <option value="DELETE"  ${apiRequest.method === 'DELETE' ? 'selected' : ''}>DELETE</option>
                        <option value="CONNECT" ${apiRequest.method === 'CONNECT' ? 'selected' : ''}>CONNECT</option>
                        <option value="HEAD"    ${apiRequest.method === 'HEAD' ? 'selected' : ''}>HEAD</option>
                        <option value="OPTIONS" ${apiRequest.method === 'OPTIONS' ? 'selected' : ''}>OPTIONS</option>
                        <option value="TRACE"   ${apiRequest.method === 'TRACE' ? 'selected' : ''}>TRACE</option>
                        <option value="PATCH"   ${apiRequest.method === 'PATCH' ? 'selected' : ''}>PATCH</option>
                    </select>
                    <label class="form-label must">Method</label>
                </div>
                <div class="form-floating">
                    ${this.renderServers(apiRequest.server)}
                    <label class="form-label must">Server</label>
                </div>
                <div class="form-floating">
                    <input type="text" name="basePath" class="form-control" style="width: 180px; border-radius: 0" value="${this.basePath(apiRequest.server)}" readonly>
                    <label class="must">기본 경로</label>
                </div>
                <div class="form-floating editableInput me-1 path" data-name="path" data-label="Path">                       
                    <label>Path</label>
                </div>
                <button class="btn btn-primary send me-1">Send</button>
                <button class="btn btn-primary save" data-index="${index}" style="height: 58px;">저장</button>
            </div>
            <div class="api_request d-flex flex-column" style="height:100%;">
              <div class="api_request_body">
                <ul class="nav nav-tabs mt-1" role="tablist">
                    <li class="nav-item" role="presentation">
                        <button class="nav-link btn_request_body_tab" data-bs-toggle="tab"
                                type="button" role="tab" aria-controls="requestBody" data-bs-target="#requestBodyTab_${apiRequest.id}"
                                aria-selected="false">Body
                        </button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link btn_request_param_tab" data-bs-toggle="tab" 
                                type="button" role="tab" aria-controls="paramTab" data-bs-target="#requestParamTab_${apiRequest.id}"
                                aria-selected="true">Param
                        </button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link btn_request_header_tab" data-bs-toggle="tab"
                                type="button" role="tab" aria-controls="requestHeader" data-bs-target="#requestHeaderTab_${apiRequest.id}"
                                aria-selected="false">Header
                        </button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link btn_prerequest_tab" data-bs-toggle="tab"
                                type="button" role="tab" aria-controls="preScrit" data-bs-target="#preRequestTab_${apiRequest.id}"
                                aria-selected="false">Pre-Request
                        </button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link btn_postrequest_tab" data-bs-toggle="tab"
                                type="button" role="tab" aria-controls="postScript" data-bs-target="#postRequestTab_${apiRequest.id}"
                                aria-selected="false">Post-Request
                        </button>
                    </li>
                </ul>
                <div class="tab-content" style="height: 100%;">
                    <div class="tab-pane fade show request_param_tab" role="tabpanel" id="requestParamTab_${apiRequest.id}" aria-labelledby="requestParams-tab">
                         <div class="query_params">
                         </div> 
                    </div>
                    <div class="tab-pane fade request_header_tab" role="tabpanel" id="requestHeaderTab_${apiRequest.id}"
                         aria-labelledby="requestHeader-tab">
                         <div class="request_headers">
                         </div>
                    </div>
                    <div class="tab-pane fade request_body_tab" role="tabpanel" id="requestBodyTab_${apiRequest.id}" aria-labelledby="requestBody-tab">
                      <div class="d-flex flex-column">
                        <select class="form-select mt-1 request_body_type" name="request_type">
                            <option value="json">json</option>
                            <option value="xml">xml</option>
                            <option value="">text/plain</option>
                        </select>
                        <div class="request_body mt-1" style="width:100%;height: 300px; border:1px solid grey;"></div>
                      </div>
                    </div>
                    <div class="tab-pane fade pre_script_tab" role="tabpanel" id="preRequestTab_${apiRequest.id}" aria-labelledby="preScrit-tab">
                        <div class="d-flex flex-row">
                            <div style="width:calc(100% - 200px);">
                                <div class="pre-request mt-1" style="height:300px;border:1px solid grey;"></div>
                            </div>
                            <div style="width: 200px;">
                                <div class="pre-request-variable">
                                </div>
                            </div> 
                        </div>
                    </div>
                    <div class="tab-pane fade post_script_tab" role="tabpanel" id="postRequestTab_${apiRequest.id}"
                         aria-labelledby="postScrit-tab">
                         <div class="d-flex flex-row">
                           <div style="width:calc(100% - 200px);">
                             <div class="post-request mt-1" style="height:300px;border:1px solid grey;"></div>
                           </div>
                           <div style="width: 200px;">
                             <div class="post-request-variable">
                             </div>
                           </div> 
                        </div>
                    </div>
                </div>
              </div>
<!--              <div class="resizer" data-direction="vertical"></div>-->
              <div class="api_response" style="flex: 1">
                <div class="mt-1">
                  <span>Response</span>
                  <span class="response_status"></span>
                  <span class="response_time"></span>
                  <span class="response_size"></span>
                </div>
                <ul class="nav nav-tabs mt-1" role="tablist">
                    <li class="nav-item" role="presentation">
                        <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#responseBodyTab_${apiRequest.id}" type="button" role="tab" aria-controls="responseBody" aria-selected="false">Body</button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link" data-bs-toggle="tab" data-bs-target="#responseHeaderTab_${apiRequest.id}" type="button" role="tab" aria-controls="responseHeader" aria-selected="false">Header</button>
                    </li>
                    <li class="nav-item" role="presentation">
                        <button class="nav-link" data-bs-toggle="tab" data-bs-target="#consoleTab_${apiRequest.id}" type="button" role="tab" aria-controls="consoleTab" aria-selected="false">Console</button>
                    </li>
                </ul>
                <div class="tab-content border-bottom" style="min-height: 250px;">
                  <div class="tab-pane fade show active" id="responseBodyTab_${apiRequest.id}" role="tabpanel"
                       aria-labelledby="responseParams-tab">
                      <div class="response_body mt-1" style="width:100%;height:250px;border:1px solid grey;"></div>
                  </div>
                  <div class="tab-pane fade" id="responseHeaderTab_${apiRequest.id}" role="tabpanel"
                       aria-labelledby="responseParams-tab">
                      <table class="table">
                          <thead>
                          <tr>
                              <th scope="col">이름</th>
                              <th scope="col">값</th>
                          </tr>
                          </thead>
                          <tbody class="response_headers">
                          </tbody>
                      </table>
                  </div>
                  <div class="tab-pane fade" id="consoleTab_${apiRequest.id}" role="tabpanel"
                       aria-labelledby="console-tab">
                      <div class="console mt-1" style="width:100%;height:250px;border:1px solid grey;"></div>
                  </div>
                </div>
            </div>
            </div>
        </div>
       
        `;
  }

  headerTemplate(header, index) {
    return `
            <tr data-index="${index}">
                <td>
                    <div class="form-check">
                        <input class="form-check-input" type="checkbox" name="headers[${index}].enabled" ${header.enabled ? 'checked' : ''} />
                    </div>
                </td>
                <td>
                    <input class="form-control" type="text" name="headers[${index}].key" value="${header.key}"/>
                </td>
                <td>
                    <input class="form-control" type="text" name="headers[${index}].value" value="${header.value}"/>
                </td>
                <td>
                    <button type="button" class="btn btn-secondary btn-sm btn_remove_header">제거</button>
                </td>
            </tr>
        `;
  }

  responseHeaderTemplate(key, value) {
    return `
            <tr>
                <td>
                    <input class="form-control" type="text" value="${key}" readonly/>
                </td>
                <td>
                    <input class="form-control" type="text" value="${value}" readonly/>
                </td>
            </tr>
        `;
  }

  collectionTemplate(collection, index) {
    return `
        <li id="collection_${index}" class="collection api_scenario_item" data-index="${index}">
            <div class="d-flex justify-content-between">
                <button class="btn d-inline-flex align-items-center rounded normal"
                        data-bs-toggle="collapse"
                        data-bs-target="#btn_collection_${index}" aria-expanded="false" aria-current="false">${collection.name}
                </button>
                <div class="d-flex justify-content-between">
                    <button class="p-1 reset-button edit_collection" data-collection="${collection.id}"><i class="fa fa-sharp fa-file-edit"></i></button>
                    <button class="p-1 reset-button delete_collection" data-collection="${collection.id}"><i class="fa fa-sharp fa-trash-can"></i></button>
                </div>
            </div>
            <div class="collapse" id="btn_collection_${index}">
                <ul class="list-unstyled fw-normal pb-1 small">
                    ${collection.apis.map((api, apiIndex) => this.collectionApiTemplate(api, apiIndex, collection.id)).join('')}
                </ul>
            </div>
        </li>
        `;
  }

  collectionApiTemplate(api, apiIndex, collectionId) {
    return `
        <li class="d-flex justify-content-between api_request_sortable " draggable="true" data-collection="${collectionId}" data-id="${api.id}" data-index="${apiIndex}" data-node-type="api" data-name="${api.name}">
                <a href="#" class="d-inline-flex align-items-center rounded open_api_request" data-node-type="api">${api.name}</a>
                <div class="d-flex justify-content-between">
                    <button class="p-1 reset-button edit_api_request"><i class="fa fa-sharp fa-file-edit"></i></button>
                    <button class="p-1 reset-button delete_api_request"><i class="fa fa-trash-can"></i></button>
                </div>
            </li>
        `;
  }

  init(servers) {
    if (servers) {
      this.servers = servers;
      if (this.servers.length === 0) {
        $('.toast-body').text('서버관리에서 서버를 등록해주세요.');
        $('.toast').toast('show');
      }
    }
    this.bindEvents();
    this.loadCollections();
  }

  bindEvents() {
    const events = [
      {selector: '.btn_add_header', action: this.addHeader.bind(this)},
      {selector: '.btn_remove_header', action: this.removeHeader.bind(this)},
      {selector: '.send', action: this.handleSendAPIRequest.bind(this)},
      {selector: '.save', action: this.saveAPIRequest.bind(this)},
      {selector: '.save_collection', action: this.addCollection.bind(this)},
      {selector: '#cancel-ajax', action: this.cancelAjaxRequest.bind(this)},
      {selector: '#new_collection', action: this.showNewCollectionModal.bind(this)},
      {selector: '#new_request', action: this.showNewApiRequestModal.bind(this)},
      {selector: '.edit_collection', action: this.showCollectionEditModal.bind(this)},
      {selector: '.delete_collection', action: this.showCollectionDeleteModal.bind(this)},
      {selector: '.confirm_delete', action: this.removeCollection.bind(this)},
      {selector: '.add_new_request', action: this.addAPIRequest.bind(this)},
      {selector: '.edit_api_request', action: this.showEditNameModal.bind(this)},
      {selector: '.delete_api_request', action: this.showDeleteApiRequestModal.bind(this)},
      {selector: '.open_api_request', action: this.openApiRequest.bind(this)},
      {selector: '.confirm_delete_api', action: this.deleteApiRequest.bind(this)},
      {selector: '.close_tab', action: this.closeTab.bind(this)}
    ];

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

    $(document).on('change', '.path', this.parseParam.bind(this));
    $(document).on('change', '.request_headers input', this.handleUIUpdate.bind(this));
    $(document).on('change', '.api_request_info select, .api_request_info input', this.handleUIUpdate.bind(this));
    $(document).on('change', '.api_request_info select', this.handleUpdateServer.bind(this));

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

  }

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

    for (let i = 0; i < this.requestEditors.length; i++) {
      this.requestEditors[i].layout({
        width: 0
      });
    }
    for (let i = 0; i < this.responseEditors.length; i++) {
      this.responseEditors[i].layout({
        width: 0
      });
    }
    for (let i = 0; i < this.preRequestEditors.length; i++) {
      this.preRequestEditors[i].layout({
        width: 0
      });
    }
    for (let i = 0; i < this.postRequestEditors.length; i++) {
      this.postRequestEditors[i].layout({
        width: 0
      });
    }

    for (let i = 0; i < this.requestEditors.length; i++) {
      this.requestEditors[i].layout({});
    }
    for (let i = 0; i < this.responseEditors.length; i++) {
      this.responseEditors[i].layout({});
    }
    for (let i = 0; i < this.preRequestEditors.length; i++) {
      this.preRequestEditors[i].layout({});
    }
    for (let i = 0; i < this.postRequestEditors.length; i++) {
      this.postRequestEditors[i].layout({});
    }

  }

  showCollectionDeleteModal(event) {
    const collectionId = $(event.currentTarget).closest('button').data('collection');
    $('#confirm_delete_modal').find('.confirm_delete').data('id', collectionId);
    $('#confirm_delete_modal').modal('show');
  }

  getApiRequestModel(apiId) {
    let foundApi = null;
    this.collections.forEach(function(collection) {
      collection.apis.forEach(function(api) {
        if (api.id === apiId) {
          foundApi = api;
        }
      });
    });

    return foundApi;
  }

  showCollectionEditModal(event) {
    const me = this;
    const collectionId = $(event.currentTarget).closest('button').data('collection');
    const collection = this.collections.filter(function(collection) {
      return collection.id === collectionId;
    })[0];

    $('#edit_name_modal').find('input').val('');
    $('#edit_name_modal').modal('show');

    let saveBtn = $('#edit_name_modal').find('button.change_name');
    saveBtn.off('click');

    saveBtn.on('click', function() {
      collection.name = $('#edit_name_modal').find('input').val();
      me.saveCollection(collection.id, collection.name);
      me.renderCollections();
      $('#edit_name_modal').modal('hide');
    });
  }

  showDeleteApiRequestModal(event) {
    let collectionId = $(event.currentTarget).closest('li').data('collection');
    let apiId = $(event.currentTarget).closest('li').data('id');
    $('#confirm_delete_api_modal').find('.confirm_delete_api').data('id', apiId);
    $('#confirm_delete_api_modal').find('.confirm_delete_api').data('collection', collectionId);
    $('#confirm_delete_api_modal').modal('show');
  }

  showNewApiRequestModal(event) {
    if (this.collections.length == 0) {
      $('.toast-body').text('새로운 컬렉션을 추가해 주세요.');
      $('.toast').toast('show');
      return;
    }
    $('#new_api_request_modal').modal('show');
  }

  showNewCollectionModal(event) {
    $('#new_collection_modal').find('input').val('');
    $('#new_collection_modal').modal('show');
  }

  showEditNameModal(event) {
    const me = this;
    const apiId = $(event.currentTarget).closest('li').data('id');
    const collectionId = $(event.currentTarget).closest('li').data('collection');

    const collection = this.collections.filter(function(collection) {
      return collection.id === collectionId;
    })[0];

    let apiRequest = collection.apis.filter(function(api) {
      return api.id === apiId;
    })[0];

    apiRequest.collectionId = collectionId;

    let saveBtn = $('#edit_name_modal').find('button.change_name');
    saveBtn.off('click');
    saveBtn.on('click', function() {
      apiRequest.name = $('#edit_name_modal').find('input').val();
      me.changeAPIName(apiRequest);
      me.renderCollections();
      $('#edit_name_modal').modal('hide');

      //탭이 열려 있으면, 닫았다가 다시 열어야 함.
      me.renderTabHeader();
      me.renderTabContent(apiRequest.index);

    });
    $('#edit_name_modal').find('input').val('');
    $('#edit_name_modal').modal('show');
  }

  getFieldValue(element) {
    if ($(element).is('select') || $(element).is('input[type="text"]')) {
      return $(element).val();
    }
    if ($(element).is('input[type="checkbox"]')) {
      return $(element).is(':checked');
    }
  }

  handleUIUpdate(event) {
    if (event) {
      event.preventDefault();
      const element = event.currentTarget;
      const fieldName = $(element).attr('name');
      const newValue = this.getFieldValue(element);
      const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
      let model = this.apiRequests[tabIndex];

      if (fieldName !== '') {
        _.set(model, fieldName, newValue);
      }
    }
  }

  handleUpdateServer(event) {
    const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
    let model = this.apiRequests[tabIndex];
    let target = $('#api_request_' + model.id);
    target.find('input[name="basePath"]').val(this.basePath(model.server));
  }

  renderHeaders(headers) {
    return headers.map((header, index) => this.headerTemplate(header, index)).join('');
  }

  renderTabHeader() {
    let apiRequestTabTitle = $(this.apiTabTitleTarget);
    apiRequestTabTitle.empty();

    for (let idx = 0; idx < this.apiRequests.length; idx++) {
      let li = `<li class="nav-item" role="presentation">
                        <button class="nav-link" id="api_request_tab_${this.apiRequests[idx].id}" data-bs-toggle="tab" type="button" role="tab" aria-controls="api_request_${this.apiRequests[idx].id}" data-bs-target="#api_request_${this.apiRequests[idx].id}" aria-selected="false">${this.apiRequests[idx].name}
                            <span class="close_tab"><i class="fa fa-light fa-close"></i></span>
                        </button>
                    </li>`;
      apiRequestTabTitle.append(li);
    }
  }

  showPopper(element, text) {
    const variable = text.replace(/{{|}}/g, '');
    this.popperContent.style.display = 'block';

    const innerDiv = this.popperContent.querySelector('.variable_popper');
    innerDiv.textContent = '{{' + variable + '}} = ' + (window.globals[variable] === undefined ? '' : window.globals[variable]);

    this.popperInstance = createPopper(element, this.popperContent, {
      placement: 'bottom', // Popper below the element
      modifiers: [
        {
          name: 'offset',
          options: {
            offset: [0, 8] // Adjust the position if needed
          }
        }
      ]
    });
  }

  hidePopper(element) {
    if (this.popperInstance) {
      this.popperInstance.destroy();
      this.popperInstance = null;
    }
    this.popperContent.style.display = 'none';
  }

  renderTabContent(index) {
    var me = this;
    let apiRequestTabContent = $(this.apiTabContentTarget);
    let model = this.apiRequests[index];

    apiRequestTabContent.append(this.apiRequestTemplate(model, index));

    let editableInput = new EditableInput({
      name: 'path',
      value: model.path
    });

    let queryParamTable = new PropertyTable({
      propertyName: 'queryParams',
      properties: model.queryParams
    });

    let headerTable = new PropertyTable({
      propertyName: 'headers',
      properties: model.headers
    });

    queryParamTable.init(apiRequestTabContent.find(`#api_request_${model.id}`).find('.query_params')[0], (properties) => {
      model.queryParams = properties;
      console.log(model.queryParams);
      this.buildPath(model);
      editableInput.setValue(model.path);
      $('span.variable').off('mouseover');
      $('span.variable').off('mouseout');
      document.querySelectorAll('span.variable').forEach(element => {
        let text = element.innerText;
        element.addEventListener('mouseover', () => me.showPopper(element, text));
        element.addEventListener('mouseout', () => me.hidePopper(element));
      });
    });

    headerTable.init(apiRequestTabContent.find(`#api_request_${model.id}`).find('.request_headers')[0], (properties) => {
      model.headers = properties;
      $('span.variable').off('mouseover');
      $('span.variable').off('mouseout');
      document.querySelectorAll('span.variable').forEach(element => {
        let text = element.innerText;
        element.addEventListener('mouseover', () => me.showPopper(element, text));
        element.addEventListener('mouseout', () => me.hidePopper(element));
      });
    });

    apiRequestTabContent.find(`#api_request_${model.id}`).find('.api_request_info').find('.path').each(function() {
      editableInput.init(this, (value) => {
        model.path = value;
        model.queryParams = me.parseParam(model.path);
        queryParamTable.setProperties(model.queryParams);
        $('span.variable').off('mouseover');
        $('span.variable').off('mouseout');
        document.querySelectorAll('span.variable').forEach(element => {
          let text = element.innerText;
          element.addEventListener('mouseover', () => me.showPopper(element, text));
          element.addEventListener('mouseout', () => me.hidePopper(element));
        });
      });

      $(this).append(`<label>Path</label>`);

      $('span.variable').off('mouseover');
      $('span.variable').off('mouseout');
    });

    document.querySelectorAll('.variable').forEach(element => {

      let text = element.innerText;
      element.addEventListener('mouseover', () => me.showPopper(element, text));
      element.addEventListener('mouseout', () => me.hidePopper(element));
    });

    this.openTab(model.id);
    let target = $('#api_request_' + model.id);

    let requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], {
      value: model.requestBody,
      language: 'json',
      automaticLayout: true
    });

    requestBodyEditor.onMouseMove((e) => {
      let position = e.target.position;
      if (position) {
        let model = requestBodyEditor.getModel();
        let word = model.getWordAtPosition(position);
        let lineContent = model.getLineContent(position.lineNumber);
        if (word) {
          if (lineContent.indexOf('{{' + word.word + '}}') > -1) {
            me.showPopper(e.target.element, word.word);
          }
        } else {
          me.hidePopper(e.target.element);
        }
      }
    });

    me.requestEditors.push(requestBodyEditor);
    target.find('.request_body')[0].addEventListener('shown.bs.tab', () => {
      me.requestEditors[index].layout();
    });

    me.requestEditors[index].onDidChangeModelContent((e) => {
      model.requestBody = requestBodyEditor.getValue();
    });

    target.find('.request_body_type').on('change', function() {
      let model = requestBodyEditor.getModel();
      monaco.editor.setModelLanguage(model, $(this).val());
    });

    let responseBodyEditor = monaco.editor.create(target.find('.response_body')[0], {
      value: '',
      language: 'json',
      automaticLayout: true,
      quickSuggestions: false
    });

    me.responseEditors.push(responseBodyEditor);

    let consoleEditor = monaco.editor.create(target.find('.console')[0], {
      value: '',
      automaticLayout: true,
      quickSuggestions: false
    });

    me.consoleEditors.push(consoleEditor);

    let preRequestEditor = monaco.editor.create(target.find('.pre-request')[0], {
      value: model.preRequestScript,
      language: 'javascript',
      automaticLayout: true,
      quickSuggestions: false
    });

    const snippet1 = new VariableSnippet();
    snippet1.init(target.find('.pre-request-variable')[0], preRequestEditor);

    me.preRequestEditors.push(preRequestEditor);
    target.find('.pre-request')[0].addEventListener('shown.bs.tab', () => {
      me.preRequestEditors[index].layout();
    });

    me.preRequestEditors[index].onDidChangeModelContent((e) => {
      model.preRequestScript = preRequestEditor.getValue();
    });

    let postRequestEditor = monaco.editor.create(target.find('.post-request')[0], {
      value: model.postRequestScript,
      language: 'javascript',
      automaticLayout: true,
      quickSuggestions: false
    });

    const snippet2 = new VariableSnippet();
    snippet2.init(target.find('.post-request-variable')[0], postRequestEditor);

    me.postRequestEditors.push(postRequestEditor);
    target.find('.post-request')[0].addEventListener('shown.bs.tab', () => {
      me.postRequestEditors[index].layout();
    });

    me.postRequestEditors[index].onDidChangeModelContent((e) => {
      model.postRequestScript = postRequestEditor.getValue();
    });

    // apiRequestTabContent.find('.resizer').each(function(index, ele) {
    //   resizable(ele,  () => {this.resizeEditor();});
    // });
  }

  renderResponse(tabIndex) {
    let model = this.apiRequests[tabIndex];
    let target = $('#api_request_' + model.id);
    target.find('.response_headers').empty();
    target.find('.response_status').empty();

    try {
      const parsedJson = JSON.parse(model.responseModel.body);
      const formattedJson = JSON.stringify(parsedJson, null, 2);
      this.responseEditors[tabIndex].setValue(formattedJson);
    } catch (e) {
      this.responseEditors[tabIndex].setValue(model.responseModel.body);
    }
    if (model.responseModel && model.responseModel.headers) {
      const headers = model.responseModel.headers;
      Object.entries(headers).forEach(([key, value]) => {
        target.find('.response_headers').append(this.responseHeaderTemplate(key, value));
      });
    }

    target.find('.response_status').text('status: ' + model.responseModel.status);
    target.find('.response_time').text('time: ' + model.responseModel.time + 'ms');
    target.find('.response_size').text('size: ' + model.responseModel.size + 'bytes');

  }

  customHelper(event) {
    // Create and return the custom helper element
    let name = $(event.target).closest('.api_request_sortable').find('.open_api_request').text().trim();
    let apiId = $(event.target).closest('.api_request_sortable').data('id');
    return `<div style="display: flex; flex-direction: column; flex: 1; border: 2px dashed #333; padding: 10px;" data-id="${apiId}">
            <div>${name}</div>
            </div>`;
  }

  renderCollections() {
    let select = $('#modal_collection');
    let sidemenu = $(this.collectionTarget);
    select.empty();
    sidemenu.empty();

    for (let i = 0; i < this.collections.length; i++) {
      sidemenu.append(this.renderCollection(this.collections[i], i));
      let option = $('<option>');
      option.attr('value', this.collections[i].id);
      option.text(this.collections[i].name);
      select.append(option);
    }

    $('li .api_request_sortable').draggable({
      helper: this.customHelper,
      start: function(event, ui) {
        // You can add any additional data or styles you want when dragging starts
        $(this).addClass('dragging');
      },
      stop: function(event, ui) {
        // Clean up, e.g., remove styles or data
        $(this).removeClass('dragging');
      }
    });

  }

  renderCollection(collection, index) {
    return this.collectionTemplate(collection, index);
  }

  openTab(id) {
    try {
      $(`#api_request_tab_${id}`).tab('show'); // API request 탭 활성화
    } catch (e) {
    }

    try {
      $(`#api_request_${id}`).find('.btn_request_body_tab').tab('show');  // Body 탭 활성화
    } catch (e) {
    }
  }

  addHeader(event) {
    const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
    let model = this.apiRequests[tabIndex];
    model.headers.push({
      enabled: true,
      key: '',
      value: ''
    });
    this.renderHeaderView(tabIndex);
  }

  removeHeader(event) {
    const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
    const index = $(event.currentTarget).closest('tr').data('index');
    let model = this.apiRequests[tabIndex];
    model.headers.splice(index, 1);
    this.renderHeaderView(tabIndex);
  }

  renderHeaderView(tabIndex) {
    let model = this.apiRequests[tabIndex];
    let target = $('#api_request_' + model.id);
    target.find('.request_headers').empty();
    target.find('.request_headers').append(this.renderHeaders(model.headers));
  }

  parseParam(path) {
    let queryParams = [];
    let queryString = path.split('?')[1];
    if (queryString) {
      let queryParamArray = queryString.split('&');
      for (let i = 0; i < queryParamArray.length; i++) {
        let queryParam = queryParamArray[i].split('=');

        queryParams.push({
          enabled: true,
          key: queryParam[0],
          value: (queryParam[1] === undefined ? '' : queryParam[1])
        });
      }
    }
    return queryParams;
  }

  buildPath(model) {
    let queryString = model.path.split('?')[0];
    if (model.queryParams.length > 0) {
      queryString += '?';
    }
    for (let i = 0; i < model.queryParams.length; i++) {
      if (!model.queryParams[i].enabled) {
        continue;
      }
      queryString += model.queryParams[i].key + '=' + model.queryParams[i].value;
      if (i < model.queryParams.length - 1) {
        queryString += '&';
      }
    }
    model.path = queryString;
  }

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

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

  handleSendAPIRequest(event) {
    const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));

    let model = this.apiRequests[tabIndex];

    if (!model.server) {
      $('.toast-body').text('서버를 등록해주세요.');
      $('.toast').toast('show');
      return;
    }

    let target = $('#api_request_' + model.id);
    target.find('.response_headers').empty();
    this.responseEditors[tabIndex].setValue('');
    let console = this.consoleEditors[tabIndex];
    model.responseModel = {};
    this.showLoadingOverlay();

    let startTime = new Date().getTime();
    this.currentAjaxRequest = this.apiClient.sendAPIRequest(model, null, this.consoleOutput.bind(this, console)).then(response => {
      if (response) {
        model.responseModel = response;
        let endTime = new Date().getTime();
        model.responseModel.time = endTime - startTime;
      } else {
        model.responseModel = {
          status: 0,
          time: 0,
          size: 0,
          headers: [],
          body: ''
        };
      }

      this.renderResponse(tabIndex);
      this.hideLoadingOverlay();
    }).catch(error => {
      $('.toast-body').text(error);
      $('.toast').toast('show');
      this.hideLoadingOverlay();
    });
  }

  consoleOutput(console, message) {
    var range = new monaco.Range(console.getModel().getLineCount(), 1, console.getModel().getLineCount(), 1);
    var id = {major: 1, minor: 1};
    if (typeof message === 'object') {
      message = JSON.stringify(message, null, 2);
    }
    var op = {
      identifier: id,
      range: range,
      text: message + '\n',
      forceMoveMarkers: true
    };
    console.executeEdits('my-source', [op]);
  }

  loadCollections() {
    const me = this;
    this.currentAjaxRequest = $.ajax({
      url: this.getCollectionListURL(),
      type: 'GET',
      contentType: 'application/json',
      success: function(data) {
        me.collections = data.map(collection => {
          if (collection.apis) {
            collection.apis = collection.apis.map(api => ({...api, collectionId: collection.id}));
          }
          return collection;
        });
      },
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.renderCollections();
        me.hideLoadingOverlay();
      }
    });
  }

  addCollection(event) {
    const name = $('#new_collection_name').val();
    const me = this;
    me.showLoadingOverlay();
    this.currentAjaxRequest = $.ajax({
      url: this.getCollectionCreateURL(),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({name: name}),
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.loadCollections();
        me.hideLoadingOverlay();
        $('#new_collection_modal').modal('hide');
      }
    });
  }

  saveCollection(id, name) {
    const me = this;
    me.showLoadingOverlay();
    this.currentAjaxRequest = $.ajax({
      url: this.getCollectionUpdateURL(),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({id: id, name: name}),
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.loadCollections();
        me.hideLoadingOverlay();
        $('#new_collection_modal').modal('hide');
      }
    });
  }

  removeCollection(event) {
    let collectionId = $(event.currentTarget).closest('button').data('id');
    const me = this;
    me.showLoadingOverlay();
    this.currentAjaxRequest = $.ajax({
      url: this.getCollectionDeleteURL(),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({id: collectionId}),
      success: function() {
      },
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.loadCollections();
        me.hideLoadingOverlay();
        $('#confirm_delete_modal').modal('hide');
      }
    });
  }

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

  saveAPIRequest(event) {
    const me = this;
    const index = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
    let model = this.apiRequests[index];
    me.showLoadingOverlay();

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

  addAPIRequest(event) {
    const collectionId = $('#modal_collection').val();
    let newApiName = $('#new_api_name').val();
    if (newApiName === '') {
      newApiName = '새로운 API';
    }

    const collection = this.collections.filter(function(collection) {
      return collection.id === collectionId;
    })[0];
    let me = this;
    let newApi = {
      id: '',
      server: null,
      method: 'GET',
      name: newApiName,
      path: '',
      headers: [],
      queryParams: [],
      requestBody: '',
      preRequestScript: '',
      postRequestScript: '',
      collectionId: collectionId,
      responseModel: {
        status: 0,
        time: 0,
        size: 0,
        headers: [],
        ody: ''
      }
    };
    newApi.collectionName = collection.name;

    this.currentAjaxRequest = $.ajax({
      url: this.getAPISaveURL(collectionId),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify(newApi),
      success: function(data) {
        newApi.id = data;
      },
      error: function(response, textStatus, errorThrown) {
        me.hideLoadingOverlay();
        $('.toast-body').text(response.responseJSON.error);
        $('.toast').toast('show');
      },
      complete: function() {
        me.loadCollections();
        me.apiRequests.push(newApi);
        me.currentIndex = me.apiRequests.length - 1;

        me.renderTabHeader();
        me.renderTabContent(me.currentIndex);
        $('#new_api_name').val('');
        me.hideLoadingOverlay();
        $('#new_api_request_modal').modal('hide');
      }
    });
  }

  cancelAjaxRequest() {
    if (this.currentAjaxRequest) {
      try {

        this.currentAjaxRequest.abort();
      } catch (e) {
        this.apiClient.abort();
      }
    }
    this.hideLoadingOverlay();
  }

  openApiRequest(event) {
    const collectionId = $(event.currentTarget).closest('li').data('collection');
    const apiId = $(event.currentTarget).closest('li').data('id');

    const collection = this.collections.filter(function(collection) {
      return collection.id === collectionId;
    })[0];
    let selectedApi = collection.apis.filter(function(api) {
      return api.id === apiId;
    })[0];

    if (selectedApi) {
      selectedApi.collectionId = collectionId;
      selectedApi.collectionName = collection.name;
      let tab = this.apiRequests.filter(function(api) {
        return api.id === selectedApi.id;
      })[0];

      if (!selectedApi.responseModel) {
        selectedApi.responseModel = {
          status: 0,
          time: 0,
          size: 0,
          headers: [],
          body: ''
        };
      }

      if (tab) {
        this.currentIndex = this.apiRequests.findIndex(function(api) {
          return api.id === selectedApi.id;
        });
        this.openTab(selectedApi.id);

      } else {
        this.apiRequests.push(selectedApi);
        this.currentIndex = this.apiRequests.length - 1;
        this.renderTabHeader();
        this.renderTabContent(this.currentIndex);
      }
    }
  }

  deleteApiRequest(event) {
    let collectionId = $(event.currentTarget).closest('button').data('collection');
    let apiId = $(event.currentTarget).closest('.confirm_delete_api').data('id');
    let apiIndex = this.apiRequests.findIndex(function(api) {
      return api.id === apiId;
    });
    if (apiIndex >= 0) {
      this.closeTabWithApiIndex(apiIndex);
    }
    const me = this;
    this.currentAjaxRequest = $.ajax({
      url: this.getAPIDeleteURL(collectionId),
      type: 'POST',
      contentType: 'application/json',
      data: JSON.stringify({id: apiId}),
      success: function(data) {
      },
      error: function(response, textStatus, errorThrown) {
        $('.toast-body').text(response.responseJSON.error);
        me.hideLoadingOverlay();
      },
      complete: function() {
        me.hideLoadingOverlay();
        me.loadCollections();
        $('#confirm_delete_api_modal').modal('hide');
      }
    });
  }

  closeTabWithApiIndex(index) {
    if (index !== undefined) {
      const model = this.apiRequests[index];
      this.apiRequests.splice(index, 1);

      this.requestEditors.splice(index, 1);
      this.responseEditors.splice(index, 1);
      this.consoleEditors.splice(index, 1);
      this.preRequestEditors.splice(index, 1);
      this.postRequestEditors.splice(index, 1);

      this.renderTabHeader();
      $(`#api_request_${model.id}`).remove();
      // $(`#apiRequestTabTitle li:eq(${this.apiRequests.length - 1}) button`).tab('show');

      $(`#apiRequestTabTitle li:last button`).tab('show');
    }
  }

  closeTab(event) {
    const index = $('#apiRequestTabTitle').children().index($(event.currentTarget).closest('.nav-item'));
    this.closeTabWithApiIndex(index);
  }
}

export default APITester;
