<template>
  <!-- eslint-disable -->
  <div>
    <Splitpanes vertical :dbl-click-splitter="false">
      <Pane class="overflow-auto hide-scrollbar">
        <Splitpanes horizontal :dbl-click-splitter="false">
          <Pane class="overflow-auto hide-scrollbar">
            <HttpRequest />
            <SmartTabs styles="sticky top-70px z-10">
              <SmartTab
                :id="'params'"
                :label="$t('parameters')"
                :selected="true"
                :info="newActiveParamsCount$"
              >
                <HttpParameters />
              </SmartTab>

              <SmartTab :id="'bodyParams'" :label="$t('body')" info="0">
                <div class="flex flex-1 py-2 items-center justify-between">
                  <tippy
                    interactive
                    ref="contentTypeOptions"
                    tabindex="-1"
                    trigger="click"
                    theme="popover"
                    arrow
                  >
                    <template #trigger>
                      <div class="flex">
                        <span class="select-wrapper">
                          <input
                            id="contentType"
                            class="bg-primary rounded-lg flex font-semibold font-mono text-xs w-full py-2 px-4 transition truncate focus:outline-none"
                            v-model="contentType"
                            readonly
                          />
                        </span>
                      </div>
                    </template>
                    <SmartItem
                      v-for="(contentTypeItem, index) in validContentTypes"
                      :key="`contentTypeItem-${index}`"
                      @click.native="
                        contentType = contentTypeItem
                        $refs.contentTypeOptions.tippy().hide()
                      "
                      :label="contentTypeItem"
                    />
                  </tippy>
                  <SmartToggle
                    v-if="canListParameters"
                    :on="rawInput"
                    @change="rawInput = !rawInput"
                    class="px-4"
                  >
                    {{ $t("raw_input") }}
                  </SmartToggle>
                </div>
                <HttpBodyParameters
                  v-if="!rawInput"
                  :bodyParams="bodyParams"
                  @clear-content="clearContent"
                  @set-route-query-state="setRouteQueryState"
                  @remove-request-body-param="removeRequestBodyParam"
                  @add-request-body-param="addRequestBodyParam"
                />
                <HttpRawBody
                  v-else
                  :rawParams="rawParams"
                  :contentType="contentType"
                  :rawInput="rawInput"
                  @clear-content="clearContent"
                  @update-raw-body="updateRawBody"
                  @update-raw-input="
                    updateRawInput = (value) => (rawInput = value)
                  "
                />
              </SmartTab>

              <SmartTab
                :id="'headers'"
                :label="$t('headers')"
                :info="newActiveHeadersCount$"
              >
                <HttpHeaders />
              </SmartTab>

              <SmartTab :id="'authentication'" :label="$t('authentication')">
                <AppSection label="authentication">
                  <ul>
                    <li>
                      <div class="flex flex-1">
                        <label for="auth">{{ $t("authentication") }}</label>
                        <div>
                          <ButtonSecondary
                            @click.native="clearContent('auth', $event)"
                            v-tippy="{ theme: 'tooltip' }"
                            :title="$t('clear')"
                            icon="clear_all"
                          />
                        </div>
                      </div>
                      <span class="select-wrapper">
                        <select class="select" id="auth" v-model="auth">
                          <option>None</option>
                          <option>Basic Auth</option>
                          <option>Bearer Token</option>
                          <option>OAuth 2.0</option>
                        </select>
                      </span>
                    </li>
                  </ul>
                  <ul v-if="auth === 'Basic Auth'">
                    <li>
                      <input
                        class="input"
                        placeholder="User"
                        name="http_basic_user"
                        v-model="httpUser"
                      />
                    </li>
                    <li>
                      <input
                        class="input"
                        placeholder="Password"
                        name="http_basic_passwd"
                        :type="passwordFieldType"
                        v-model="httpPassword"
                      />
                    </li>
                    <div>
                      <li>
                        <ButtonSecondary
                          ref="switchVisibility"
                          @click.native="switchVisibility"
                          :icon="
                            passwordFieldType === 'text'
                              ? 'visibility'
                              : 'visibility_off'
                          "
                        />
                      </li>
                    </div>
                  </ul>
                  <ul v-if="auth === 'Bearer Token' || auth === 'OAuth 2.0'">
                    <li>
                      <div class="flex flex-1">
                        <input
                          class="input"
                          placeholder="Token"
                          name="bearer_token"
                          v-model="bearerToken"
                        />
                        <ButtonSecondary
                          v-if="auth === 'OAuth 2.0'"
                          @click.native="
                            showTokenListModal = !showTokenListModal
                          "
                          v-tippy="{ theme: 'tooltip' }"
                          :title="$t('use_token')"
                          icon="open_in_new"
                        />
                        <ButtonSecondary
                          v-if="auth === 'OAuth 2.0'"
                          @click.native="showTokenRequest = !showTokenRequest"
                          v-tippy="{ theme: 'tooltip' }"
                          :title="$t('get_token')"
                          icon="vpn_key"
                        />
                      </div>
                    </li>
                  </ul>
                  <div class="flex flex-1">
                    <SmartToggle
                      :on="!URL_EXCLUDES.auth"
                      @change="setExclude('auth', !$event)"
                    >
                      {{ $t("include_in_url") }}
                    </SmartToggle>
                  </div>
                </AppSection>

                <AppSection v-if="showTokenRequest" label="accessTokenRequest">
                  <ul>
                    <li>
                      <div class="flex flex-1">
                        <label for="token-name">{{ $t("token_name") }}</label>
                        <div>
                          <ButtonSecondary
                            @click.native="showTokenRequestList = true"
                            v-tippy="{ theme: 'tooltip' }"
                            :title="$t('manage_token_req')"
                            icon="library_add"
                          />
                          <ButtonSecondary
                            @click.native="clearContent('access_token', $event)"
                            v-tippy="{ theme: 'tooltip' }"
                            :title="$t('clear')"
                            icon="clear_all"
                          />
                          <ButtonSecondary
                            @click.native="showTokenRequest = false"
                            v-tippy="{ theme: 'tooltip' }"
                            :title="$t('close')"
                            icon="close"
                          />
                        </div>
                      </div>
                      <input
                        class="input"
                        id="token-name"
                        :placeholder="$t('optional')"
                        name="token_name"
                        v-model="accessTokenName"
                        type="text"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <label for="oidc-discovery-url">
                        {{ $t("oidc_discovery_url") }}
                      </label>
                      <input
                        class="input"
                        :disabled="
                          this.authUrl !== '' || this.accessTokenUrl !== ''
                        "
                        id="oidc-discovery-url"
                        name="oidc_discovery_url"
                        type="url"
                        v-model="oidcDiscoveryUrl"
                        placeholder="https://example.com/.well-known/openid-configuration"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <label for="auth-url">{{ $t("auth_url") }}</label>
                      <input
                        class="input"
                        :disabled="this.oidcDiscoveryUrl !== ''"
                        id="auth-url"
                        name="auth_url"
                        type="url"
                        v-model="authUrl"
                        placeholder="https://example.com/login/oauth/authorize"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <label for="access-token-url">
                        {{ $t("access_token_url") }}
                      </label>
                      <input
                        class="input"
                        :disabled="this.oidcDiscoveryUrl !== ''"
                        id="access-token-url"
                        name="access_token_url"
                        type="url"
                        v-model="accessTokenUrl"
                        placeholder="https://example.com/login/oauth/access_token"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <label for="client-id">{{ $t("client_id") }}</label>
                      <input
                        class="input"
                        id="client-id"
                        name="client_id"
                        type="text"
                        v-model="clientId"
                        placeholder="Client ID"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <label for="scope">{{ $t("scope") }}</label>
                      <input
                        class="input"
                        id="scope"
                        name="scope"
                        type="text"
                        v-model="scope"
                        placeholder="e.g. read:org"
                      />
                    </li>
                  </ul>
                  <ul>
                    <li>
                      <ButtonSecondary
                        @click.native="handleAccessTokenRequest"
                        icon="vpn_key"
                        :label="$t('request_token')"
                      />
                    </li>
                  </ul>
                </AppSection>
              </SmartTab>

              <SmartTab
                :id="'pre_request_script'"
                :label="$t('pre_request_script')"
              >
                <AppSection v-if="showPreRequestScript" label="preRequest">
                  <div
                    class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-110px z-10 sticky items-center justify-between"
                  >
                    <label class="font-semibold text-xs">
                      {{ $t("javascript_code") }}
                    </label>
                    <ButtonSecondary
                      to="https://github.com/hoppscotch/hoppscotch/wiki/Pre-Request-Scripts"
                      blank
                      v-tippy="{ theme: 'tooltip' }"
                      :title="$t('wiki')"
                      icon="help_outline"
                    />
                  </div>
                  <SmartJsEditor
                    v-model="preRequestScript"
                    :options="{
                      maxLines: '16',
                      minLines: '8',
                      fontSize: '14px',
                      autoScrollEditorIntoView: true,
                      showPrintMargin: false,
                      useWorker: false,
                    }"
                    completeMode="pre"
                  />
                </AppSection>
              </SmartTab>

              <SmartTab :id="'tests'" :label="$t('tests')">
                <AppSection v-if="testsEnabled" label="postRequestTests">
                  <div
                    class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-110px z-10 sticky items-center justify-between"
                  >
                    <label class="font-semibold text-xs">
                      {{ $t("javascript_code") }}
                    </label>
                    <ButtonSecondary
                      to="https://github.com/hoppscotch/hoppscotch/wiki/Post-Request-Tests"
                      blank
                      v-tippy="{ theme: 'tooltip' }"
                      :title="$t('wiki')"
                      icon="help_outline"
                    />
                  </div>
                  <SmartJsEditor
                    v-model="testScript"
                    :options="{
                      maxLines: '16',
                      minLines: '8',
                      fontSize: '14px',
                      autoScrollEditorIntoView: true,
                      showPrintMargin: false,
                      useWorker: false,
                    }"
                    completeMode="test"
                  />
                  <div v-if="testReports.length !== 0">
                    <div class="flex flex-1 pl-4 items-center justify-between">
                      <label class="font-semibold text-xs">
                        Test Reports
                      </label>
                      <ButtonSecondary
                        @click.native="clearContent('tests', $event)"
                        v-tippy="{ theme: 'tooltip' }"
                        :title="$t('clear')"
                        icon="clear_all"
                      />
                    </div>
                    <div
                      v-for="(testReport, index) in testReports"
                      :key="`testReport-${index}`"
                      class="px-4"
                    >
                      <div v-if="testReport.startBlock">
                        <hr />
                        <h4 class="heading">
                          {{ testReport.startBlock }}
                        </h4>
                      </div>
                      <p
                        v-else-if="testReport.result"
                        class="flex font-mono flex-1 text-xs info"
                      >
                        <span
                          :class="testReport.styles.class"
                          class="flex items-center"
                        >
                          <i class="text-sm material-icons">
                            {{ testReport.styles.icon }}
                          </i>
                          <span>&nbsp;{{ testReport.result }}</span>
                          <span v-if="testReport.message">
                            <label>: {{ testReport.message }}</label>
                          </span>
                        </span>
                      </p>
                      <div v-else-if="testReport.endBlock"><hr /></div>
                    </div>
                  </div>
                </AppSection>
              </SmartTab>
            </SmartTabs>
          </Pane>
          <Pane class="overflow-auto hide-scrollbar">
            <HttpResponse ref="response" />
          </Pane>
        </Splitpanes>
      </Pane>
      <Pane
        max-size="30"
        size="25"
        min-size="20"
        class="overflow-auto hide-scrollbar"
      >
        <aside class="h-full">
          <SmartTabs styles="sticky z-10 top-0">
            <SmartTab :id="'history'" :label="$t('history')" :selected="true">
              <History :page="'rest'" ref="historyComponent" />
            </SmartTab>

            <SmartTab :id="'collections'" :label="$t('collections')">
              <Collections />
            </SmartTab>

            <SmartTab :id="'env'" :label="$t('environments')">
              <Environments />
            </SmartTab>
          </SmartTabs>
        </aside>
      </Pane>
    </Splitpanes>

    <CollectionsSaveRequest
      mode="rest"
      :show="showSaveRequestModal"
      @hide-modal="hideRequestModal"
      :editing-request="editRequest"
    />

    <HttpImportCurl
      :show="showCurlImportModal"
      @hide-modal="showCurlImportModal = false"
      @handle-import="handleImport"
    />

    <HttpCodegenModal
      :show="showCodegenModal"
      :requestTypeProp="requestType"
      :requestCode="requestCode"
      @hide-modal="showCodegenModal = false"
      @set-request-type="setRequestType"
    />

    <HttpTokenList
      :show="showTokenListModal"
      :tokens="tokens"
      @clear-content="clearContent"
      @use-oauth-token="useOAuthToken"
      @remove-oauth-token="removeOAuthToken"
      @hide-modal="showTokenListModal = false"
    />

    <SmartModal
      v-if="showTokenRequestList"
      @close="showTokenRequestList = false"
    >
      <template #header>
        <h3 class="heading">{{ $t("manage_token_req") }}</h3>
        <div>
          <ButtonSecondary
            @click.native="showTokenRequestList = false"
            icon="close"
          />
        </div>
      </template>
      <template #body>
        <div class="flex flex-1">
          <label for="token-req-list">{{ $t("token_req_list") }}</label>
          <div>
            <ButtonSecondary
              :disabled="this.tokenReqs.length === 0"
              @click.native="showTokenRequestList = false"
              v-tippy="{ theme: 'tooltip' }"
              :title="$t('use_token_req')"
              icon="input"
            />
            <ButtonSecondary
              :disabled="this.tokenReqs.length === 0"
              @click.native="removeOAuthTokenReq"
              v-tippy="{ theme: 'tooltip' }"
              :title="$t('delete')"
              icon="delete"
            />
          </div>
        </div>
        <ul>
          <li>
            <span class="select-wrapper">
              <select
                id="token-req-list"
                class="select"
                v-model="tokenReqSelect"
                :disabled="this.tokenReqs.length === 0"
                @change="tokenReqChange($event)"
              >
                <option
                  v-for="(req, index) in tokenReqs"
                  :key="`req-${index}`"
                  :value="req.name"
                >
                  {{ req.name }}
                </option>
              </select>
            </span>
          </li>
        </ul>
        <label for="token-req-name">{{ $t("token_req_name") }}</label>
        <input class="input" v-model="tokenReqName" />
        <label for="token-req-details">
          {{ $t("token_req_details") }}
        </label>
        <textarea
          id="token-req-details"
          class="textarea"
          readonly
          rows="7"
          v-model="tokenReqDetails"
        ></textarea>
      </template>
      <template #footer>
        <span></span>
        <span>
          <ButtonPrimary @click.native="addOAuthTokenReq" />
          {{ $t("save_token_req") }}
        </span>
      </template>
    </SmartModal>
  </div>
</template>

<script>
/* eslint-disable */
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"

import url from "url"
import querystring from "querystring"
import parseCurlCommand from "~/helpers/curlparser"
import getEnvironmentVariablesFromScript from "~/helpers/preRequest"
import runTestScriptWithVariables from "~/helpers/postwomanTesting"
import parseTemplateString from "~/helpers/templating"
import { tokenRequest, oauthRedirect } from "~/helpers/oauth"
import { cancelRunningRequest, sendNetworkRequest } from "~/helpers/network"
import {
  hasPathParams,
  addPathParamsToVariables,
  getQueryParams,
} from "~/helpers/requestParams"
import { parseUrlAndPath } from "~/helpers/utils/uri"
import { httpValid } from "~/helpers/utils/valid"
import {
  knownContentTypes,
  isJSONContentType,
} from "~/helpers/utils/contenttypes"
import { generateCodeWithGenerator } from "~/helpers/codegen/codegen"
import { getSettingSubject, applySetting } from "~/newstore/settings"
import { addRESTHistoryEntry } from "~/newstore/history"
import clone from "lodash/clone"
import {
  restRequest$,
  restActiveParamsCount$,
  restActiveHeadersCount$,
} from "~/newstore/RESTSession"
import { map } from "rxjs/operators"

export default {
  components: { Splitpanes, Pane },

  data() {
    return {
      showCurlImportModal: false,
      showPreRequestScript: true,
      testsEnabled: true,
      testScript: "// pw.expect('variable').toBe('value');",
      preRequestScript: "// pw.env.set('variable', 'value');",
      testReports: [],
      copyButton: '<i class="material-icons">content_copy</i>',
      downloadButton: '<i class="material-icons">save_alt</i>',
      doneButton: '<i class="material-icons">done</i>',
      showCodegenModal: false,
      response: {
        status: "",
        headers: "",
        body: "",
        duration: 0,
        size: 0,
      },
      validContentTypes: knownContentTypes,
      paramsWatchEnabled: true,
      showTokenListModal: false,
      showTokenRequest: false,
      showTokenRequestList: false,
      showSaveRequestModal: false,
      editRequest: {},
      files: [],
      filenames: "",
      runningRequest: false,
      currentMethodIndex: 0,

      newActiveParamsCount$: "",
      newActiveHeadersCount$: "",

      effectiveStream$: null,
    }
  },
  subscriptions() {
    return {
      SCROLL_INTO_ENABLED: getSettingSubject("SCROLL_INTO_ENABLED"),
      PROXY_ENABLED: getSettingSubject("PROXY_ENABLED"),
      URL_EXCLUDES: getSettingSubject("URL_EXCLUDES"),
      EXPERIMENTAL_URL_BAR_ENABLED: getSettingSubject(
        "EXPERIMENTAL_URL_BAR_ENABLED"
      ),
      newActiveParamsCount$: restActiveParamsCount$.pipe(
        map((e) => {
          if (e == 0) return null
          return e.toString()
        })
      ),
      newActiveHeadersCount$: restActiveHeadersCount$.pipe(
        map((e) => {
          if (e == 0) return null
          return e.toString()
        })
      ),
    }
  },
  watch: {
    canListParameters: {
      immediate: true,
      handler(canListParameters) {
        if (canListParameters) {
          this.$nextTick(() => {
            this.rawInput = Boolean(this.rawParams && this.rawParams !== "{}")
          })
        } else {
          this.rawInput = true
        }
      },
    },
    contentType(contentType, oldContentType) {
      const getDefaultParams = (contentType) => {
        if (isJSONContentType(contentType)) return "{}"
        switch (contentType) {
          case "application/xml":
            return "<?xml version='1.0' encoding='utf-8'?>"
          case "text/html":
            return "<!doctype html>"
        }
        return ""
      }
      if (
        !this.rawParams ||
        this.rawParams === getDefaultParams(oldContentType)
      ) {
        this.rawParams = getDefaultParams(contentType)
      }
      this.setRouteQueryState()
    },
    params: {
      handler() {
        this.setRouteQueryState()
      },
      deep: true,
    },
    selectedRequest(newValue) {
      // @TODO: Convert all variables to single request variable
      if (!newValue) return
      this.uri = newValue.url + newValue.path
      this.url = newValue.url
      this.path = newValue.path
      this.method = newValue.method
      this.auth = newValue.auth
      this.httpUser = newValue.httpUser
      this.httpPassword = newValue.httpPassword
      this.passwordFieldType = newValue.passwordFieldType
      this.bearerToken = newValue.bearerToken
      this.headers = newValue.headers
      this.params = newValue.params
      this.bodyParams = newValue.bodyParams
      this.rawParams = newValue.rawParams
      this.rawInput = newValue.rawInput
      this.contentType = newValue.contentType
      this.requestType = newValue.requestType
      if (newValue.preRequestScript) {
        this.showPreRequestScript = true
        this.preRequestScript = newValue.preRequestScript
      }
      if (newValue.testScript) {
        this.testsEnabled = true
        this.testScript = newValue.testScript
      }
      this.name = newValue.name
    },
    method() {
      this.contentType = ["POST", "PUT", "PATCH", "DELETE"].includes(
        this.method
      )
        ? this.contentType
        : "application/json"
    },
    preRequestScript(val, oldVal) {
      this.uri = this.uri
    },
  },
  computed: {
    /**
     * Check content types that can be automatically
     * serialized by Hoppscotch.
     */
    canListParameters() {
      return (
        this.contentType === "application/x-www-form-urlencoded" ||
        this.contentType === "multipart/form-data" ||
        isJSONContentType(this.contentType)
      )
    },
    uri: {
      get() {
        return this.$store.state.request.uri
          ? this.$store.state.request.uri
          : this.url + this.path
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "uri" })
        let url = value
        if (
          (this.preRequestScript && this.showPreRequestScript) ||
          hasPathParams(this.params)
        ) {
          let environmentVariables = getEnvironmentVariablesFromScript(
            this.preRequestScript
          )
          environmentVariables = addPathParamsToVariables(
            this.params,
            environmentVariables
          )
          url = parseTemplateString(value, environmentVariables)
        }
        let result = parseUrlAndPath(url)
        this.url = result.url
        this.path = result.path
      },
    },
    url: {
      get() {
        return this.$store.state.request.url
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "url" })
      },
    },
    method: {
      get() {
        return this.$store.state.request.method
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "method" })
      },
    },
    path: {
      get() {
        return this.$store.state.request.path
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "path" })
      },
    },
    name: {
      get() {
        return this.$store.state.request.name
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "name" })
      },
    },
    auth: {
      get() {
        return this.$store.state.request.auth
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "auth" })
      },
    },
    httpUser: {
      get() {
        return this.$store.state.request.httpUser
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "httpUser" })
      },
    },
    httpPassword: {
      get() {
        return this.$store.state.request.httpPassword
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "httpPassword" })
      },
    },
    bearerToken: {
      get() {
        return this.$store.state.request.bearerToken
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "bearerToken" })
      },
    },
    tokens: {
      get() {
        return this.$store.state.oauth2.tokens
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "tokens" })
      },
    },
    tokenReqs: {
      get() {
        return this.$store.state.oauth2.tokenReqs
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "tokenReqs" })
      },
    },
    tokenReqSelect: {
      get() {
        return this.$store.state.oauth2.tokenReqSelect
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "tokenReqSelect" })
      },
    },
    tokenReqName: {
      get() {
        return this.$store.state.oauth2.tokenReqName
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "tokenReqName" })
      },
    },
    accessTokenName: {
      get() {
        return this.$store.state.oauth2.accessTokenName
      },
      set(value) {
        this.$store.commit("setOAuth2", {
          value,
          attribute: "accessTokenName",
        })
      },
    },
    oidcDiscoveryUrl: {
      get() {
        return this.$store.state.oauth2.oidcDiscoveryUrl
      },
      set(value) {
        this.$store.commit("setOAuth2", {
          value,
          attribute: "oidcDiscoveryUrl",
        })
      },
    },
    authUrl: {
      get() {
        return this.$store.state.oauth2.authUrl
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "authUrl" })
      },
    },
    accessTokenUrl: {
      get() {
        return this.$store.state.oauth2.accessTokenUrl
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "accessTokenUrl" })
      },
    },
    clientId: {
      get() {
        return this.$store.state.oauth2.clientId
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "clientId" })
      },
    },
    scope: {
      get() {
        return this.$store.state.oauth2.scope
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "scope" })
      },
    },
    state: {
      get() {
        return this.$store.state.oauth2.state
      },
      set(value) {
        this.$store.commit("setOAuth2", { value, attribute: "state" })
      },
    },
    headers: {
      get() {
        return this.$store.state.request.headers
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "headers" })
      },
    },
    params: {
      get() {
        return this.$store.state.request.params
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "params" })
      },
    },
    bodyParams: {
      get() {
        return this.$store.state.request.bodyParams
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "bodyParams" })
      },
    },
    rawParams: {
      get() {
        return this.$store.state.request.rawParams
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "rawParams" })
        // Convert the rawParams to bodyParams format
        try {
          const valueObj = JSON.parse(value)
          const params = Object.keys(valueObj).map((key) => {
            if (typeof valueObj[key] !== "function") {
              return {
                active: true,
                key,
                value: valueObj[key],
              }
            }
          })
          this.$store.commit("setBodyParams", { params })
        } catch {}
      },
    },
    rawInput: {
      get() {
        return this.$store.state.request.rawInput
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "rawInput" })
      },
    },
    requestType: {
      get() {
        return this.$store.state.request.requestType
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "requestType" })
      },
    },
    contentType: {
      get() {
        return this.$store.state.request.contentType
      },
      set(value) {
        this.$store.commit("setState", { value, attribute: "contentType" })
      },
    },
    passwordFieldType: {
      get() {
        return this.$store.state.request.passwordFieldType
      },
      set(value) {
        this.$store.commit("setState", {
          value,
          attribute: "passwordFieldType",
        })
      },
    },
    selectedRequest() {
      return this.$store.state.postwoman.selectedRequest
    },
    requestName() {
      return this.name
    },
    isValidURL() {
      // if showPreRequestScript, we cannot determine if a URL is valid because the full string is not known ahead of time
      return this.showPreRequestScript || httpValid(this.url)
    },
    hasRequestBody() {
      return ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
    },
    pathName() {
      return this.path.match(/^([^?]*)\??/)[1]
    },
    rawRequestBody() {
      const { bodyParams, contentType } = this
      if (isJSONContentType(contentType)) {
        try {
          const obj = JSON.parse(
            `{${bodyParams
              .filter((item) =>
                item.hasOwnProperty("active") ? item.active == true : true
              )
              .filter(({ key }) => !!key)
              .map(({ key, value }) => `"${key}": "${value}"`)
              .join()}}`
          )
          return JSON.stringify(obj, null, 2)
        } catch (ex) {
          console.log(ex)
          this.$toast.clear()
          this.$toast.error(
            "Parameter value must be a string, switch to Raw input for other formats",
            {
              icon: "error",
            }
          )
          return "invalid"
        }
      } else {
        return bodyParams
          .filter((item) =>
            item.hasOwnProperty("active") ? item.active == true : true
          )
          .filter(({ key }) => !!key)
          .map(
            ({ key, value }) =>
              `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
          )
          .join("&")
      }
    },
    queryString() {
      const result = getQueryParams(this.params)
        .map(
          ({ key, value }) =>
            `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
        )
        .join("&")
      return result === "" ? "" : `?${result}`
    },
    requestCode() {
      let headers = []
      if (this.preRequestScript || hasPathParams(this.params)) {
        let environmentVariables = getEnvironmentVariablesFromScript(
          this.preRequestScript
        )
        environmentVariables = addPathParamsToVariables(
          this.params,
          environmentVariables
        )
        for (let k of this.headers.filter((item) =>
          item.hasOwnProperty("active") ? item.active == true : true
        )) {
          const kParsed = parseTemplateString(k.key, environmentVariables)
          const valParsed = parseTemplateString(k.value, environmentVariables)
          headers.push({ key: kParsed, value: valParsed })
        }
      }

      return generateCodeWithGenerator(this.requestType, {
        auth: this.auth,
        method: this.method,
        url: this.url,
        pathName: this.pathName,
        queryString: this.queryString,
        httpUser: this.httpUser,
        httpPassword: this.httpPassword,
        bearerToken: this.bearerToken,
        headers,
        rawInput: this.rawInput,
        rawParams: this.rawParams,
        rawRequestBody: this.rawRequestBody,
        contentType: this.contentType,
      })
    },
    tokenReqDetails() {
      const details = {
        oidcDiscoveryUrl: this.oidcDiscoveryUrl,
        authUrl: this.authUrl,
        accessTokenUrl: this.accessTokenUrl,
        clientId: this.clientId,
        scope: this.scope,
      }
      return JSON.stringify(details, null, 2)
    },
  },
  methods: {
    scrollInto(view) {
      this.$refs[view].$el.scrollIntoView({
        behavior: "smooth",
      })
    },
    async makeRequest(auth, headers, requestBody, preRequestScript) {
      const requestOptions = {
        method: this.method,
        url: this.url + this.pathName + this.queryString,
        auth,
        headers,
        data: requestBody,
        credentials: true,
      }

      if (preRequestScript || hasPathParams(this.params)) {
        let environmentVariables =
          getEnvironmentVariablesFromScript(preRequestScript)
        environmentVariables = addPathParamsToVariables(
          this.params,
          environmentVariables
        )
        requestOptions.url = parseTemplateString(
          requestOptions.url,
          environmentVariables
        )
        if (!(requestOptions.data instanceof FormData)) {
          // TODO: Parse env variables for form data too
          requestOptions.data = parseTemplateString(
            requestOptions.data,
            environmentVariables
          )
        }
        for (let k in requestOptions.headers) {
          const kParsed = parseTemplateString(k, environmentVariables)
          const valParsed = parseTemplateString(
            requestOptions.headers[k],
            environmentVariables
          )
          delete requestOptions.headers[k]
          requestOptions.headers[kParsed] = valParsed
        }
      }
      if (typeof requestOptions.data === "string") {
        requestOptions.data = parseTemplateString(requestOptions.data)
      }
      return await sendNetworkRequest(requestOptions)
    },
    cancelRequest() {
      cancelRunningRequest()
    },
    newSendRequest() {},
    async sendRequest() {
      this.$toast.clear()
      if (this.SCROLL_INTO_ENABLED) this.scrollInto("response")
      if (!this.isValidURL) {
        this.$toast.error(this.$t("url_invalid_format"), {
          icon: "error",
        })
        return
      }
      // Start showing the loading bar as soon as possible.
      // The nuxt axios module will hide it when the request is made.
      this.$nuxt.$loading.start()
      this.response = {
        duration: 0,
        size: 0,
        status: this.$t("fetching"),
        body: this.$t("loading"),
      }
      const auth =
        this.auth === "Basic Auth"
          ? {
              username: this.httpUser,
              password: this.httpPassword,
            }
          : null
      let headers = {}
      let headersObject = {}
      Object.keys(headers).forEach((id) => {
        if (headers[id].key) headersObject[headers[id].key] = headers[id].value
      })
      headers = headersObject
      // If the request has a body, we want to ensure Content-Type is sent.
      let requestBody
      if (this.hasRequestBody) {
        requestBody = this.rawInput ? this.rawParams : this.rawRequestBody
        Object.assign(headers, {
          "Content-Type": `${this.contentType}; charset=utf-8`,
        })
      }
      requestBody = requestBody ? requestBody.toString() : null
      if (this.contentType === "multipart/form-data") {
        const formData = new FormData()
        for (const bodyParam of this.bodyParams.filter((item) =>
          item.hasOwnProperty("active") ? item.active == true : true
        )) {
          if (bodyParam?.value?.[0] instanceof File) {
            for (const file of bodyParam.value) {
              formData.append(bodyParam.key, file)
            }
          } else {
            formData.append(bodyParam.key, bodyParam.value)
          }
        }
        requestBody = formData
      }
      // If the request uses a token for auth, we want to make sure it's sent here.
      if (this.auth === "Bearer Token" || this.auth === "OAuth 2.0")
        headers["Authorization"] = `Bearer ${this.bearerToken}`
      headers = Object.assign(
        // Clone the app headers object first, we don't want to
        // mutate it with the request headers added by default.
        Object.assign(
          {},
          this.headers.filter((item) =>
            item.hasOwnProperty("active") ? item.active == true : true
          )
        )
        // We make our temporary headers object the source so
        // that you can override the added headers if you
        // specify them.
        // headers
      )
      Object.keys(headers).forEach((id) => {
        if (headers[id].key) headersObject[headers[id].key] = headers[id].value
      })
      headers = headersObject
      const startTime = new Date().getTime()
      try {
        this.runningRequest = true
        const payload = await this.makeRequest(
          auth,
          headers,
          requestBody,
          this.showPreRequestScript && this.preRequestScript
        )
        this.runningRequest = false
        const duration = new Date().getTime() - startTime
        this.response.duration = duration
        this.response.size = payload.headers["content-length"]
        ;(() => {
          this.response.status = payload.status
          this.response.headers = payload.headers
          // We don't need to bother parsing JSON, axios already handles it for us!
          this.response.body = payload.data
          // Addition of an entry to the history component.
          const entry = {
            name: this.requestName,
            status: this.response.status,
            date: new Date().toLocaleDateString(),
            time: new Date().toLocaleTimeString(),
            updatedOn: new Date(),
            method: this.method,
            url: this.url,
            path: this.path,
            usesPreScripts: this.showPreRequestScript,
            preRequestScript: this.preRequestScript,
            duration,
            star: false,
            auth: this.auth,
            httpUser: this.httpUser,
            httpPassword: this.httpPassword,
            bearerToken: this.bearerToken,
            headers: this.headers,
            params: this.params,
            bodyParams: this.bodyParams,
            rawParams: this.rawParams,
            rawInput: this.rawInput,
            contentType: this.contentType,
            requestType: this.requestType,
            testScript: this.testScript,
            usesPostScripts: this.testsEnabled,
          }

          if (
            (this.preRequestScript && this.showPreRequestScript) ||
            hasPathParams(this.params)
          ) {
            let environmentVariables = getEnvironmentVariablesFromScript(
              this.preRequestScript
            )
            environmentVariables = addPathParamsToVariables(
              this.params,
              environmentVariables
            )
            entry.path = parseTemplateString(entry.path, environmentVariables)
            entry.url = parseTemplateString(entry.url, environmentVariables)
          }

          addRESTHistoryEntry(entry)
        })()
      } catch (error) {
        this.runningRequest = false
        // If the error is caused by cancellation, do nothing
        if (error === "cancellation") {
          this.response.status = this.$t("cancelled")
          this.response.body = this.$t("cancelled")
        } else {
          console.log(error)
          const duration = new Date().getTime() - startTime
          this.response.duration = duration
          this.response.size = Buffer.byteLength(JSON.stringify(error))
          if (error.response) {
            this.response.headers = error.response.headers
            this.response.status = error.response.status
            this.response.body = error.response.data
            // Addition of an entry to the history component.
            const entry = {
              name: this.requestName,
              status: this.response.status,
              date: new Date().toLocaleDateString(),
              time: new Date().toLocaleTimeString(),
              updatedOn: new Date(),
              method: this.method,
              url: this.url,
              path: this.path,
              usesPreScripts: this.showPreRequestScript,
              preRequestScript: this.preRequestScript,
              duration,
              star: false,
              auth: this.auth,
              httpUser: this.httpUser,
              httpPassword: this.httpPassword,
              bearerToken: this.bearerToken,
              headers: this.headers,
              params: this.params,
              bodyParams: this.bodyParams,
              rawParams: this.rawParams,
              rawInput: this.rawInput,
              contentType: this.contentType,
              requestType: this.requestType,
              testScript: this.testScript,
              usesPostScripts: this.testsEnabled,
            }

            if (
              (this.preRequestScript && this.showPreRequestScript) ||
              hasPathParams(this.params)
            ) {
              let environmentVariables = getEnvironmentVariablesFromScript(
                this.preRequestScript
              )
              environmentVariables = addPathParamsToVariables(
                this.params,
                environmentVariables
              )
              entry.path = parseTemplateString(entry.path, environmentVariables)
              entry.url = parseTemplateString(entry.url, environmentVariables)
            }

            addRESTHistoryEntry(entry)
            return
          } else {
            this.response.status = error.message
            this.response.body = `${error}. ${this.$t("check_console_details")}`
            this.$toast.error(`${error} ${this.$t("f12_details")}`, {
              icon: "error",
            })
            if (!this.PROXY_ENABLED) {
              this.$toast.info(this.$t("enable_proxy"), {
                icon: "help",
                duration: 8000,
                action: {
                  text: this.$t("yes"),
                  onClick: (e, toastObject) => {
                    this.$router.push({ path: "/settings" })
                  },
                },
              })
            }
          }
        }
      }
      // tests
      const syntheticResponse = {
        status: this.response.status,
        body: this.response.body,
        headers: this.response.headers,
      }

      // Parse JSON body
      if (
        syntheticResponse.headers["content-type"] &&
        isJSONContentType(syntheticResponse.headers["content-type"])
      ) {
        try {
          syntheticResponse.body = JSON.parse(
            new TextDecoder("utf-8").decode(
              new Uint8Array(syntheticResponse.body)
            )
          )
        } catch (_e) {}
      }

      const { testResults } = runTestScriptWithVariables(this.testScript, {
        response: syntheticResponse,
      })
      this.testReports = testResults
    },
    getQueryStringFromPath() {
      const pathParsed = url.parse(this.uri)
      return pathParsed.query ? pathParsed.query : ""
    },
    queryStringToArray(queryString) {
      const queryParsed = querystring.parse(queryString)
      return Object.keys(queryParsed).map((key) => ({
        key,
        value: queryParsed[key],
        active: true,
      }))
    },
    pathInputHandler() {
      if (this.uri.includes("?")) {
        const queryString = this.getQueryStringFromPath()
        let environmentVariables = getEnvironmentVariablesFromScript(
          this.preRequestScript
        )
        environmentVariables = addPathParamsToVariables(
          this.params,
          environmentVariables
        )
        const params = this.queryStringToArray(queryString)
        let parsedParams = []
        for (let k of params.filter((item) =>
          item.hasOwnProperty("active") ? item.active == true : true
        )) {
          const kParsed = parseTemplateString(k.key, environmentVariables)
          const valParsed = parseTemplateString(k.value, environmentVariables)
          parsedParams.push({ key: kParsed, value: valParsed, active: true })
        }
        this.paramsWatchEnabled = false
        this.params = parsedParams
      }
    },
    addRequestHeader() {
      this.$store.commit("addHeaders", {
        key: "",
        value: "",
        active: true,
      })
      return false
    },
    removeRequestHeader(index) {
      // .slice() gives us an entirely new array rather than giving us just the reference
      const oldHeaders = this.headers.slice()
      this.$store.commit("removeHeaders", index)
      this.$toast.error(this.$t("deleted"), {
        icon: "delete",
        action: {
          text: this.$t("undo"),
          onClick: (e, toastObject) => {
            this.headers = oldHeaders
            toastObject.remove()
          },
        },
      })
    },
    addRequestParam() {
      this.$store.commit("addParams", {
        key: "",
        value: "",
        type: "query",
        active: true,
      })
      return false
    },
    removeRequestParam(index) {
      // .slice() gives us an entirely new array rather than giving us just the reference
      const oldParams = this.params.slice()
      this.$store.commit("removeParams", index)
      this.$toast.error(this.$t("deleted"), {
        icon: "delete",
        action: {
          text: this.$t("undo"),
          onClick: (e, toastObject) => {
            this.params = oldParams
            toastObject.remove()
          },
        },
      })
    },
    addRequestBodyParam() {
      this.$store.commit("addBodyParams", { key: "", value: "", active: true })
      return false
    },
    removeRequestBodyParam(index) {
      // .slice() gives us an entirely new array rather than giving us just the reference
      const oldBodyParams = this.bodyParams.slice()
      this.$store.commit("removeBodyParams", index)
      this.$toast.error(this.$t("deleted"), {
        icon: "delete",
        action: {
          text: this.$t("undo"),
          onClick: (e, toastObject) => {
            this.bodyParams = oldBodyParams
            toastObject.remove()
          },
        },
      })
    },
    copyRequest() {
      if (navigator.share) {
        const time = new Date().toLocaleTimeString()
        const date = new Date().toLocaleDateString()
        navigator
          .share({
            title: "Hoppscotch",
            text: `Hoppscotch • Open source API development ecosystem at ${time} on ${date}`,
            url: window.location.href,
          })
          .then(() => {})
          .catch(() => {})
      } else {
        const dummy = document.createElement("input")
        document.body.appendChild(dummy)
        dummy.value = window.location.href
        dummy.select()
        document.execCommand("copy")
        document.body.removeChild(dummy)
        this.$refs.copyRequest.innerHTML = this.doneButton
        this.$toast.info(this.$t("copied_to_clipboard"), {
          icon: "done",
        })
        setTimeout(
          () => (this.$refs.copyRequest.innerHTML = this.copyButton),
          1000
        )
      }
    },
    setRouteQueryState() {
      const flat = (key) => (this[key] !== "" ? `${key}=${this[key]}&` : "")
      const deep = (key) => {
        const haveItems = [...this[key]].length
        if (haveItems && this[key]["value"] !== "") {
          // Exclude files fro  query params
          const filesRemoved = this[key].filter(
            (item) => !(item?.value?.[0] instanceof File)
          )
          return `${key}=${JSON.stringify(filesRemoved)}&`
        }
        return ""
      }
      let flats = [
        "method",
        "url",
        "path",
        !this.URL_EXCLUDES.auth ? "auth" : null,
        !this.URL_EXCLUDES.httpUser ? "httpUser" : null,
        !this.URL_EXCLUDES.httpPassword ? "httpPassword" : null,
        !this.URL_EXCLUDES.bearerToken ? "bearerToken" : null,
        "contentType",
      ]
        .filter((item) => item !== null)
        .map((item) => flat(item))
      const deeps = ["headers", "params"].map((item) => deep(item))
      const bodyParams = this.rawInput
        ? [flat("rawParams")]
        : [deep("bodyParams")]
      history.replaceState(
        window.location.href,
        "",
        `${this.$router.options.base}?${encodeURI(
          flats.concat(deeps, bodyParams).join("").slice(0, -1)
        )}`
      )
    },
    setRouteQueries(queries) {
      if (typeof queries !== "object")
        throw new Error("Route query parameters must be a Object")
      for (const key in queries) {
        if (["headers", "params", "bodyParams"].includes(key))
          this[key] = JSON.parse(decodeURI(encodeURI(queries[key])))
        if (key === "rawParams") {
          this.rawInput = true
          this.rawParams = queries["rawParams"]
        } else if (typeof this[key] === "string") {
          this[key] = queries[key]
        }
      }
    },
    handleImport() {
      const { value: text } = document.getElementById("import-curl")
      try {
        const parsedCurl = parseCurlCommand(text)
        const { origin, pathname } = new URL(
          parsedCurl.url.replace(/"/g, "").replace(/'/g, "")
        )
        this.url = origin
        this.path = pathname
        this.uri = this.url + this.path
        this.headers = []
        if (parsedCurl.query) {
          for (const key of Object.keys(parsedCurl.query)) {
            this.$store.commit("addParams", {
              key,
              value: parsedCurl.query[key],
              type: "query",
              active: true,
            })
          }
        }
        if (parsedCurl.headers) {
          for (const key of Object.keys(parsedCurl.headers)) {
            this.$store.commit("addHeaders", {
              key,
              value: parsedCurl.headers[key],
            })
          }
        }
        this.method = parsedCurl.method.toUpperCase()
        if (parsedCurl["data"]) {
          this.rawInput = true
          this.rawParams = parsedCurl["data"]
        }
        this.showCurlImportModal = false
      } catch (error) {
        this.showCurlImportModal = false
        this.$toast.error(this.$t("curl_invalid_format"), {
          icon: "error",
        })
      }
    },
    switchVisibility() {
      this.passwordFieldType =
        this.passwordFieldType === "password" ? "text" : "password"
    },
    clearContent(name, { target }) {
      switch (name) {
        case "bodyParams":
          this.bodyParams = []
          this.files = []
          break
        case "rawParams":
          this.rawParams = "{}"
          break
        case "parameters":
          this.params = []
          break
        case "auth":
          this.auth = "None"
          this.httpUser = ""
          this.httpPassword = ""
          this.bearerToken = ""
          this.showTokenRequest = false
          this.tokens = []
          this.tokenReqs = []
          break
        case "access_token":
          this.accessTokenName = ""
          this.oidcDiscoveryUrl = ""
          this.authUrl = ""
          this.accessTokenUrl = ""
          this.clientId = ""
          this.scope = ""
          break
        case "headers":
          this.headers = []
          break
        case "tests":
          this.testReports = []
          break
        case "tokens":
          this.tokens = []
          break
        default:
          this.method = "GET"
          this.url = "https://httpbin.org"
          this.path = "/get"
          this.uri = this.url + this.path
          this.name = "Untitled request"
          this.bodyParams = []
          this.rawParams = "{}"
          this.files = []
          this.params = []
          this.auth = "None"
          this.httpUser = ""
          this.httpPassword = ""
          this.bearerToken = ""
          this.showTokenRequest = false
          this.tokens = []
          this.tokenReqs = []
          this.accessTokenName = ""
          this.oidcDiscoveryUrl = ""
          this.authUrl = ""
          this.accessTokenUrl = ""
          this.clientId = ""
          this.scope = ""
          this.headers = []
          this.testReports = []
      }
      // target.innerHTML = this.doneButton
      this.$toast.info(this.$t("cleared"), {
        icon: "clear_all",
      })
      setTimeout(
        () => (target.innerHTML = '<i class="material-icons">clear_all</i>'),
        1000
      )
    },
    saveRequest() {
      let urlAndPath = parseUrlAndPath(this.uri)
      this.editRequest = {
        url: decodeURI(urlAndPath.url),
        path: decodeURI(urlAndPath.path),
        method: this.method,
        auth: this.auth,
        httpUser: this.httpUser,
        httpPassword: this.httpPassword,
        passwordFieldType: this.passwordFieldType,
        bearerToken: this.bearerToken,
        headers: this.headers,
        params: this.params,
        bodyParams: this.bodyParams,
        rawParams: this.rawParams,
        rawInput: this.rawInput,
        contentType: this.contentType,
        requestType: this.requestType,
        preRequestScript:
          this.showPreRequestScript == true ? this.preRequestScript : null,
        testScript: this.testsEnabled == true ? this.testScript : null,
        name: this.requestName,
      }
      this.showSaveRequestModal = true
    },
    hideRequestModal() {
      this.showSaveRequestModal = false
      this.editRequest = {}
    },
    setExclude(excludedField, excluded) {
      const update = clone(this.URL_EXCLUDES)

      if (excludedField === "auth") {
        update.auth = excluded
        update.httpUser = excluded
        update.httpPassword = excluded
        update.bearerToken = excluded
      } else {
        update[excludedField] = excluded
      }

      applySetting("URL_EXCLUDES", update)

      this.setRouteQueryState()
    },
    updateRawBody(rawParams) {
      this.rawParams = rawParams
    },
    async handleAccessTokenRequest() {
      if (
        this.oidcDiscoveryUrl === "" &&
        (this.authUrl === "" || this.accessTokenUrl === "")
      ) {
        this.$toast.error(this.$t("complete_config_urls"), {
          icon: "error",
        })
        return
      }
      try {
        const tokenReqParams = {
          grantType: "code",
          oidcDiscoveryUrl: this.oidcDiscoveryUrl,
          authUrl: this.authUrl,
          accessTokenUrl: this.accessTokenUrl,
          clientId: this.clientId,
          scope: this.scope,
        }
        await tokenRequest(tokenReqParams)
      } catch (e) {
        this.$toast.error(e, {
          icon: "code",
        })
      }
    },
    async oauthRedirectReq() {
      const tokenInfo = await oauthRedirect()
      if (Object.prototype.hasOwnProperty.call(tokenInfo, "access_token")) {
        this.bearerToken = tokenInfo.access_token
        this.addOAuthToken({
          name: this.accessTokenName,
          value: tokenInfo.access_token,
        })
      }
    },
    addOAuthToken({ name, value }) {
      this.$store.commit("addOAuthToken", {
        name,
        value,
      })
      return false
    },
    removeOAuthToken(index) {
      const oldTokens = this.tokens.slice()
      this.$store.commit("removeOAuthToken", index)
      this.$toast.error(this.$t("deleted"), {
        icon: "delete",
        action: {
          text: this.$t("undo"),
          onClick: (e, toastObject) => {
            this.tokens = oldTokens
            toastObject.remove()
          },
        },
      })
    },
    useOAuthToken(value) {
      this.bearerToken = value
      this.showTokenListModal = false
    },
    addOAuthTokenReq() {
      try {
        const name = this.tokenReqName
        const details = JSON.parse(this.tokenReqDetails)
        this.$store.commit("addOAuthTokenReq", {
          name,
          details,
        })
        this.$toast.info(this.$t("token_request_saved"))
        this.showTokenRequestList = false
      } catch (e) {
        this.$toast.error(e, {
          icon: "code",
        })
      }
    },
    removeOAuthTokenReq(index) {
      const oldTokenReqs = this.tokenReqs.slice()
      const targetReqIndex = this.tokenReqs.findIndex(
        ({ name }) => name === this.tokenReqName
      )
      if (targetReqIndex < 0) return
      this.$store.commit("removeOAuthTokenReq", targetReqIndex)
      this.$toast.error(this.$t("deleted"), {
        icon: "delete",
        action: {
          text: this.$t("undo"),
          onClick: (e, toastObject) => {
            this.tokenReqs = oldTokenReqs
            toastObject.remove()
          },
        },
      })
    },
    tokenReqChange({ target }) {
      const { details, name } = this.tokenReqs.find(
        ({ name }) => name === target.value
      )
      const { oidcDiscoveryUrl, authUrl, accessTokenUrl, clientId, scope } =
        details
      this.tokenReqName = name
      this.oidcDiscoveryUrl = oidcDiscoveryUrl
      this.authUrl = authUrl
      this.accessTokenUrl = accessTokenUrl
      this.clientId = clientId
      this.scope = scope
    },
    setRequestType(val) {
      this.requestType = val
    },
  },
  async mounted() {
    restRequest$.subscribe((x) => console.log(x))
    this._keyListener = function (e) {
      if (e.key === "g" && (e.ctrlKey || e.metaKey)) {
        e.preventDefault()
        if (!this.runningRequest) {
          this.sendRequest()
        } else {
          this.cancelRequest()
        }
      }
      if (e.key === "s" && (e.ctrlKey || e.metaKey)) {
        e.preventDefault()
        this.saveRequest()
      }
      if (e.key === "k" && (e.ctrlKey || e.metaKey)) {
        e.preventDefault()
        this.copyRequest()
      }
      if (e.key === "i" && (e.ctrlKey || e.metaKey)) {
        e.preventDefault()
        this.$refs.clearAll.click()
      }
      if ((e.key === "g" || e.key === "G") && e.altKey) {
        this.method = "GET"
      }
      if ((e.key === "h" || e.key === "H") && e.altKey) {
        this.method = "HEAD"
      }
      if ((e.key === "p" || e.key === "P") && e.altKey) {
        this.method = "POST"
      }
      if ((e.key === "u" || e.key === "U") && e.altKey) {
        this.method = "PUT"
      }
      if ((e.key === "x" || e.key === "X") && e.altKey) {
        this.method = "DELETE"
      }
      if (e.key == "ArrowUp" && e.altKey && this.currentMethodIndex > 0) {
        this.method =
          this.methodMenuItems[
            --this.currentMethodIndex % this.methodMenuItems.length
          ]
      } else if (
        e.key == "ArrowDown" &&
        e.altKey &&
        this.currentMethodIndex < 9
      ) {
        this.method =
          this.methodMenuItems[
            ++this.currentMethodIndex % this.methodMenuItems.length
          ]
      }
    }
    document.addEventListener("keydown", this._keyListener.bind(this))
    await this.oauthRedirectReq()
  },
  created() {
    if (Object.keys(this.$route.query).length)
      this.setRouteQueries(this.$route.query)
    this.$watch(
      (vm) => [
        vm.name,
        vm.method,
        vm.url,
        vm.auth,
        vm.path,
        vm.httpUser,
        vm.httpPassword,
        vm.bearerToken,
        vm.headers,
        vm.params,
        vm.bodyParams,
        vm.contentType,
        vm.rawParams,
      ],
      (val) => {
        this.setRouteQueryState()
      }
    )
  },
  beforeDestroy() {
    document.removeEventListener("keydown", this._keyListener)
  },
}
</script>
